Created
August 10, 2018 19:37
-
-
Save Hixie/8fa74b8ff6f1debf0ab5e9048b5974bd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:flutter/material.dart'; | |
final Identifier time = new Identifier(); | |
final Identifier counter = new Identifier(); | |
void main() { | |
Model model = new Model(); | |
runApp(new ModelProvider( | |
model: model, | |
child: new MyApp(), | |
)); | |
model.setValue(counter, 0); | |
new Timer.periodic(const Duration(seconds: 1), (Timer timer) { | |
model.setValue(time, new DateTime.now()); | |
}); | |
} | |
class MyApp extends StatelessWidget { | |
MyApp({ Key key }) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return new MaterialApp( | |
title: 'Flutter Demo', | |
home: new MyHomePage(), | |
); | |
} | |
} | |
class MyHomePage extends ModelDependentWidget { | |
MyHomePage({ Key key }) : super(key: key); | |
@override | |
Widget build(BuildContext context, DependCallback depend) { | |
return new Scaffold( | |
appBar: new AppBar( | |
title: new Text('Time: ${depend(time)}'), | |
), | |
body: new Center( | |
child: new Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
new Text( | |
'You have pushed the button this many times:', | |
), | |
new Text( | |
'${depend(counter)}', | |
style: Theme.of(context).textTheme.display1, | |
), | |
], | |
), | |
), | |
floatingActionButton: new FloatingActionButton( | |
onPressed: () { | |
Model model = ModelProvider.modelFor(context); | |
model.setValue(counter, model.lookup(counter) + 1); | |
}, | |
tooltip: 'Increment', | |
child: new Icon(Icons.add), | |
), | |
); | |
} | |
} | |
// MODEL LIBRARY | |
class Identifier { } | |
class Model extends ChangeNotifier { | |
final Map<Identifier, dynamic> _values = <Identifier, dynamic>{}; | |
void setValue(Identifier identifier, dynamic value) { | |
_values[identifier] = value; | |
notifyListeners(); | |
} | |
dynamic lookup(Identifier identifier) { | |
return _values[identifier]; | |
} | |
} | |
typedef dynamic DependCallback(Identifier identifier); | |
class ModelProvider extends InheritedWidget { | |
const ModelProvider({ | |
Key key, | |
@required this.model, | |
@required Widget child, | |
}) : assert(model != null), | |
assert(child != null), | |
super(key: key, child: child); | |
final Model model; | |
static Model modelFor(BuildContext context) { | |
return (context.inheritFromWidgetOfExactType(ModelProvider) as ModelProvider).model; | |
} | |
@override | |
bool updateShouldNotify(ModelProvider old) => model != old.model; | |
} | |
abstract class ModelDependentWidget extends StatefulWidget { | |
const ModelDependentWidget({ Key key }) : super(key: key); | |
@protected | |
Widget build(BuildContext context, DependCallback depend); | |
@override | |
State<ModelDependentWidget> createState() => new ModelDependentWidgetState(); | |
} | |
class ModelDependentWidgetState extends State<ModelDependentWidget> { | |
bool _debugDependable = false; | |
Model _model; | |
Model _subscribedModel; | |
final Map<Identifier, dynamic> _dependencies = <Identifier, dynamic>{}; | |
dynamic depend(Identifier identifier) { | |
assert(_debugDependable); | |
_model ??= ModelProvider.modelFor(context); | |
_dependencies.putIfAbsent(identifier, () => _model.lookup(identifier)); | |
return _dependencies[identifier]; | |
} | |
void _changeHandler() { | |
assert(_subscribedModel != null); | |
for (Identifier identifier in _dependencies.keys) { | |
if (_subscribedModel.lookup(identifier) != _dependencies[identifier]) { | |
_dependencies.clear(); // in case it changes multiple times before we rebuild | |
setState(() { /* Model changed. */ }); | |
return; | |
} | |
} | |
} | |
@override | |
void dispose() { | |
_subscribedModel?.removeListener(_changeHandler); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
assert(() { | |
_debugDependable = true; | |
return true; | |
}()); | |
_dependencies.clear(); | |
final Widget result = widget.build(context, depend); | |
assert(() { | |
_debugDependable = false; | |
return true; | |
}()); | |
if (_subscribedModel != _model) { | |
_subscribedModel?.removeListener(_changeHandler); | |
_subscribedModel = _model; | |
_subscribedModel?.addListener(_changeHandler); | |
assert((_subscribedModel == null) == (_dependencies.isEmpty)); | |
} | |
return result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment