Skip to content

Instantly share code, notes, and snippets.

@Hixie
Created August 10, 2018 19:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hixie/8fa74b8ff6f1debf0ab5e9048b5974bd to your computer and use it in GitHub Desktop.
Save Hixie/8fa74b8ff6f1debf0ab5e9048b5974bd to your computer and use it in GitHub Desktop.
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