Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
/*
This version of the example adds a static Model method for updating the
ModelBinding's current model value:
```
Model.update(context, newModel);
```
This substantially reduces the plumbing required for widgets that need to
changes the app's model. Instead of passing down a callback that rebuilds the
ModelBinding, any widget can update the ModelBinding's current state directly.
The stateless ViewController now handles a button press like this:
```
onPressed: () {
final Model model = Model.of(context);
Model.update(context, Model(value: model.value + 1));
}
```
The implementation of this feature is a little tricky. ModelBinding is now
a stateful widget that tracks the current Model value. ModelBinding
builds an _ModelBindingScope InheritedWidget child that has pointer to the
State<ModelBinding> - an _ModelBindingState; essentially to its stateful widget
(ModelBinding) parent. To update change ModelBinding's current Model value,
we rebuild the ModelBinding with setState().
*/
import 'package:flutter/material.dart';
class Model {
const Model({ this.value = 0 });
final int value;
@override
bool operator ==(Object other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
final Model otherModel = other;
return otherModel.value == value;
}
@override
int get hashCode => value.hashCode;
static Model of(BuildContext context) {
final _ModelBindingScope scope = context.inheritFromWidgetOfExactType(_ModelBindingScope);
return scope.modelBindingState.currentModel;
}
static void update(BuildContext context, Model newModel) {
final _ModelBindingScope scope = context.inheritFromWidgetOfExactType(_ModelBindingScope);
scope.modelBindingState.updateModel(newModel);
}
}
class _ModelBindingScope extends InheritedWidget {
_ModelBindingScope({
Key key,
@required this.modelBindingState,
Widget child,
}) : assert(modelBindingState != null), super(key: key, child: child);
final _ModelBindingState modelBindingState;
@override
bool updateShouldNotify(_ModelBindingScope oldWidget) => true;
}
class ModelBinding extends StatefulWidget {
ModelBinding({
Key key,
this.initialModel = const Model(),
this.child,
}) : assert(initialModel != null), super(key: key);
final Model initialModel;
final Widget child;
_ModelBindingState createState() => _ModelBindingState();
}
class _ModelBindingState extends State<ModelBinding> {
Model currentModel;
@override
void initState() {
super.initState();
currentModel = widget.initialModel;
}
void updateModel(Model newModel) {
if (newModel != currentModel) {
setState(() {
currentModel = newModel;
});
}
}
@override
Widget build(BuildContext context) {
return _ModelBindingScope(
modelBindingState: this,
child: widget.child,
);
}
}
class ViewController extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
final Model model = Model.of(context);
Model.update(context, Model(value: model.value + 1));
},
child: Text('Hello World ${Model.of(context).value}'),
);
}
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ModelBinding(
initialModel: const Model(),
child: Scaffold(
body: Center(
child: ViewController(),
),
),
),
);
}
}
void main() {
runApp(App());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment