An interface is made up of many components. It is often necessary to encapsulate a component, but there is a problem of getting multiple components to share some values.

For example, here we use the WidgetA component in a _State, passing in a method that incrementCounter increments itself and a _counter count.

WidgetA in turn consists of several custom widgets. So the question is, how do I get the value in WidgetE to +1 when WidgetK clicks? One of the most straightforward ways to do this is by passing variables and functions down one layer at a time through constructors. You might say WTF, you’re making me laugh?


4. 1. Smart trashcan

Divide the counter page into five sections, each controlled by a Widget.

WidgetA: control view display depends on WidgetB and WidgetF WidgetB: Control view layout depends on WidgetC WidgetC: Control view content composition depends on WidgetD WidgetD: Control view counter uses WidgetF: Control view trigger countCopy the code

Now to get WidgetF’s click to be answered by WidgetD, here’s the dumbest solution: construct a pass-through and pass it layer by layer. It’s a hassle, but it’s not impossible.

class _MyHomePageState extends State<MyHomePage> { int _counter = 0; @override Widget build(BuildContext context) { return WidgetA(_counter,_incrementCounter,widget.title); } void _incrementCounter() { setState(() { _counter++; }); } } class WidgetA extends StatelessWidget { WidgetA(this.counter,this.increment,this.title); final int counter; final VoidCallback increment; final String title; @override Widget build(BuildContext context) { var result= Scaffold( appBar: AppBar(title: Text(title),), body: WidgetB(counter), floatingActionButton: WidgetF(increment), ); return result; } } class WidgetB extends StatelessWidget { WidgetB(this.counter); final int counter; @override Widget build(BuildContext context) { var center= Center( child: WidgetC(counter), ); return center; } } class WidgetC extends StatelessWidget { WidgetC(this.counter); final int counter; @override Widget build(BuildContext context) { var column=Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('You have pushed the button this many times:',), WidgetD(counter) ], ); return column; } } class WidgetD extends StatelessWidget { WidgetD(this.counter); final int counter; @override Widget build(BuildContext context) { var text= Text('$counter', style: Theme.of(context).textTheme.display1,); return text; } } class WidgetF extends StatelessWidget { WidgetF(this.increment); final VoidCallback increment; @override Widget build(BuildContext context) { var button= FloatingActionButton( onPressed: increment, tooltip: 'Increment', child: Icon(Icons.add), ); return button; }}Copy the code

Does it feel very troublesome? How can this be handled so that the required parameters are not so convoluted?


2. The first solution: InheritedWidget

Here we use an InheritedWidget to provide data and methods that are shared among the five components. Store values in an InheritedWidget, as shown below, and take them on demand. So the world is finally quiet, no need to construct the transmission value flying.

Class count {final int count; // count final VoidCallback increment; / / growth function const CountModel (enclosing the count, enclosing increment); } class CountWidget extends InheritedWidget { final CountModel model; CountWidget({ Key key, @required this.model, @required Widget child, }) : super(key: key, child: child); The static CountWidget of (BuildContext context) {/ / provide data model methods return context. InheritFromWidgetOfExactType (CountWidget); Override bool updateShouldNotify(CountWidget oldWidget) {return model.count! = oldWidget.model.count; }}Copy the code

Looking at the current implementation of each child component, there is no need to pass the required parameters down one layer at a time.

If you have a deeper level of encapsulation, the InheritedWidget is a great way to deliver your data.

class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; @override Widget build(BuildContext context) { return CountWidget(child: WidgetA(widget.title),model: CountModel(_counter, _incrementCounter),); } void _incrementCounter() { setState(() { _counter++; }); } } class WidgetA extends StatelessWidget { WidgetA(this.title); final String title; @override Widget build(BuildContext context) { var result= Scaffold( appBar: AppBar(title: Text(title),), body: WidgetB(), floatingActionButton: WidgetF(), ); return result; } } class WidgetB extends StatelessWidget { @override Widget build(BuildContext context) { var center= Center( child: WidgetC(), ); return center; } } class WidgetC extends StatelessWidget { @override Widget build(BuildContext context) { var column=Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('You have pushed the button this many times:',), WidgetD() ], ); return column; } } class WidgetD extends StatelessWidget { @override Widget build(BuildContext context) { var counter= CountWidget.of(context).model.count; var text= Text('$counter', style: Theme.of(context).textTheme.display1,); return text; } } class WidgetF extends StatelessWidget { @override Widget build(BuildContext context) { var increment= CountWidget.of(context).model.increment; var button= FloatingActionButton( onPressed: increment, tooltip: 'Increment', child: Icon(Icons.add), ); return button; }}Copy the code

conclusion

This is the end of this article. If you want to taste Flutter quickly, Flutter For Seven days is a must-have. If you want to explore it, follow in my footsteps and complete a Flutter tour. In addition, I have a Flutter wechat communication group. You are welcome to join and discuss Flutter issues together. My wechat account is ZDL1994328.

Stay tuned for the next article, which will show you how to optimize your current code to make the amount of state more manageable.