Intimate analysis of "fluent must know and know": the complete use scheme of InheritedWidget for state management and data sharing paves the way for you to learn from the big front end
1, Foreword
Because fluent organizes pages in the way of node tree, the node level of an ordinary page will be very deep. At this time, if we still transfer data layer by layer, it will be more troublesome when we need to modify the data. It is mentioned in the actual combat of fluent that InheritedWidget is a very important functional component in fluent. It provides a way to transfer and share data from top to bottom in the widget tree For example, if we share a data in the root widget of the application through the InheritedWidget, we can obtain the shared data in any child widget! This feature is very convenient in some scenarios where data needs to be shared in the widget tree! For example, in the fluent SDK, the InheritedWidget is used to share the application Theme and locale information.

- So, without much nonsense, let's start by introducing the use of inherited widgets
2, When not imported
- Let's take the template provided by the system as an example and gradually introduce InheritedWidget
- The following template will be generated automatically when new shuttle project is
void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); } } 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; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }

- At this time, click the Add button to see that all child controls are refreshed

- However, the actual child on the page needs to refresh the number control (number increase) and button (shadow color change)
- All controls are refreshed, which consumes a lot of memory, which is not in line with our expectations
- Now let's introduce inherited widget to modify this code to see the effect
3, Using InheritedWidget
- Next, we will transform the above shutter template
- Make it reach the effect of refreshing only the number Text and FloatingButton when clicking Add
3.1 shared data encapsulation
- We now sub assemble the data to be shared (here is the number of hits) into a class, such as EnvConfig
class EnvConfig { int count; }
3.2 packaging InheritedWidget
- Create a new class and let it inherit from InheritedWidget
- His role is to bind pages to data classes
class _InheritedWidget extends InheritedWidget { _InheritedWidget({ Key key, @required Widget child, @required this.data, }) : super(key: key, child: child); final _ConfigWrapperState data; @override bool updateShouldNotify(_InheritedWidget oldWidget) { return true; } }
- Because the page needs to be refreshed when the data is updated, we package a StatefulWidget outside the InheritedWidget
class ConfigWrapper extends StatefulWidget { final EnvConfig config; final Widget child; ConfigWrapper({Key key, this.config, this.child}); @override State<StatefulWidget> createState() => _ConfigWrapperState(); static _ConfigWrapperState of(BuildContext context) { final _InheritedWidget inheritedConfig = context.dependOnInheritedWidgetOfExactType<_InheritedWidget>(); return inheritedConfig.data; } } class _ConfigWrapperState extends State<ConfigWrapper> { void incrementCounter() { setState(() { widget.config.count++; }); } @override Widget build(BuildContext context) { return new _InheritedWidget(data: this, child: widget.child); } }
- After creating a class, ConfigWrapper is used for wrapping to facilitate management
class ConfigWrapper extends StatelessWidget { ConfigWrapper({Key key, this.config, this.child}); @override Widget build(BuildContext context) { return new _InheritedWidget(data: this.config, child: this.child); } static EnvConfig of(BuildContext context) { final _InheritedWidget inheritedConfig = context.dependOnInheritedWidgetOfExactType<_InheritedWidget>(); return inheritedConfig.data; } final EnvConfig config; final Widget child; }
3.3 modify HomePage
- Separate the original app entry MyApp/MyHomePage
- Use shared data through configwrapper of(context)... method
- The modify data / refresh page can be accessed through configwrapper of(context). Incrementcounter callback
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key}) : super(key: key); @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Demo Home Page'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ _TitleText(), _CountText() ], ), ), floatingActionButton: _FloatBtn() ); } } class _TitleText extends StatelessWidget { @override Widget build(BuildContext context) { return Text( 'You have pushed the button this many times:', ); } } class _CountText extends StatelessWidget { @override Widget build(BuildContext context) { return Text( '${ConfigWrapper.of(context).widget.config.count}', style: Theme.of(context).textTheme.headline4, ); } } class _FloatBtn extends StatelessWidget { @override Widget build(BuildContext context) { return FloatingActionButton( onPressed: ConfigWrapper.of(context).incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ); // This trailing comma makes auto-formatting nicer for build methods. } }
3.4 final
- Change the original entry main to:
void main() { runApp(ConfigWrapper( config: EnvConfig(), child: MyApp()) ); }
- Run it, and then we're clicking the Add button
- You can see that only a number of Text and hover buttons have been updated

3, Summary
- The above belongs to the more complex use of InheritedWidget. It will be easier if you don't need to refresh the data
- If there are deficiencies in the text, you are also welcome to add in the comment area. I will change it into the original text
- Well, thank you for your praise, attention and support. See you next time
If you have any questions about InheritedWidget, please refer to: 1. Instructions for InheritedWidget 2. Data sharing (InheritedWidget)