The idea of Flutter is that everything is a Widget and it is easy to build such a Widget.

In this Widget Tree, there are often many dependencies between components, and over time it can easily become the following.

This is a common problem with declarative programming, because widgets are used to present data that may come from many other widgets, and sharing and passing data across widgets becomes cumbersome and difficult to manage.

Therefore, Flutter has an InheritedWidget on top of StatelessWidget and StatefulWidget, which is specially used to share and transfer data and state. In addition, the unique responsive architecture of declarative programming also uses observer mode. This makes it easier to listen for changes in data state. These are all advantages of Flutter to process data.

The following articles will guide you to sort out the data flow in Flutter and master the state management scheme of Flutter.

The opening

To manage the data and state of widgets, you need to first understand which scenarios need to manage data in Flutter.

In general, there are two scenarios for data management:

  • Same page data management across widgets
  • Cross-page data management

A Flutter may have many different widgets within the same Page. These widgets are all at the same Page level. When a Flutter state changes, other widgets need to respond.

The other is data sharing between multiple pages.

In the same Page, all the widgets and the Page root Widget can form a parent-child relationship, because the new Page generated by PageRoute is the Page root Widget mounted on the App root Widget. There is no father-son relationship.

First, let’s look at same-page cross-widget data management.

In order to ensure the integrity of the article, this article will explain all aspects of Flutter state management one by one, so please don’t mind if there is some redundancy.

Scenario 1-1: StatefulWidget

When setState is called, the StatefulWidget will re-execute the build function to generate a new Widget using new data. With StatefulWidget, is it possible to fully manage same-page data?

Yes, but there is a problem. If there are 100 widgets in the page, only one Widget needs to accept the change and modify its UI. However, in this StatefulWidget, because setState is called, So all of the 100 widgets on this page will be rebuilt. This is obviously “mine at home”, so to avoid this problem, you need to narrow down the scope of statefulWidgets and make the refresh controlled by the setState function as small as possible. This way, when only one of the 100 widgets needs to be rebuilt, there is no need to recreate the 99 unnecessary widgets.

However, a new problem arises. The scope of the StatefulWidget is smaller. How do I refresh the StatefulWidget for data changes that occur outside the StatefulWidget? At this point, Flutter’s responsive programming architecture needs to be leveraged.

Solution 1-2: ValueNotifier

ValueNotifier implements an observer mode. ValueNotifier holds a Value object and notifes all registered observers when the Value object changes.

A [ChangeNotifier] that holds a single value. When [value] is replaced with something that is not equal to the old value  as evaluated by the equality operator ==, this class notifies its listeners.Copy the code

ValueNotifier allows you to manage data across widgets within the same Page. The managed data is hosted by ValueNotifier. All widgets that need to be changed due to this data are registered to listen. ValueNotifier automatically notifies all listeners to manage data.

The following example demonstrates the use of the simplest ValueNotifier.

class ValueNotifierWidget extends StatelessWidget { @override Widget build(BuildContext context) { ValueNotifier<String>  valueNotifier = ValueNotifier<String>('Init String Data'); return Column( children: <Widget>[MainTitleWidget('ValueNotifier basic use '), SubtitleWidget(' addListener in the Widget that needs to respond if ValueNotifier's value changes, '), NotifierWidget(data: valueNotifier), RaisedButton(onPressed: () => valueNotifier.value = 'New Value ${Random().nextInt(100)}', child: Text('Change'), ), ], ); } } class NotifierWidget extends StatefulWidget { final ValueNotifier<String> data; NotifierWidget({this.data}); @override _NotifierWidgetState createState() => _NotifierWidgetState(); } class _NotifierWidgetState extends State<NotifierWidget> { String info; @override initState() { super.initState(); widget.data.addListener(changeNotifier); info = '${widget.data.value}'; } void changeNotifier() { setState(() => info = '${widget.data.value}'); } @override Widget build(BuildContext context) { return Text( info, style: TextStyle(fontSize: 30), ); } @override dispose() { widget.data.removeListener(changeNotifier); super.dispose(); }}Copy the code

The NotifierWidget registers a listener for ValueNotifier. When ValueNotifier is updated by another Widget in the Demo page (RaisedButton triggered), the NotifierWidget automatically receives notification. To refresh the UI.

Flutter dojo-widget-async-valuenotifier

Custom ValueNotifier

ValueNotifier can also specify custom types in the same way as using base types.

class ValueNotifierWidget extends StatelessWidget { @override Widget build(BuildContext context) { PersonNotifier customNotifier = PersonNotifier(People('xuyisheng', 18)); return Column( children: <Widget>[ MainTitleWidget('Custom ValueNotifier'), CustomNotifierWidget(data: customNotifier), RaisedButton( onPressed: () => customNotifier.changePeopleName('zhujia'), child: Text('Change'), ), ], ); } } class CustomNotifierWidget extends StatefulWidget { final ValueNotifier<People> data; CustomNotifierWidget({this.data}); @override _CustomNotifierWidgetState createState() => _CustomNotifierWidgetState(); } class _CustomNotifierWidgetState extends State<CustomNotifierWidget> { People info; @override initState() { super.initState(); widget.data.addListener(changeNotifier); info = widget.data.value; } void changeNotifier() { setState(() => info = widget.data.value); } @override Widget build(BuildContext context) { return Text( '${info.name},${info.age}', style: TextStyle(fontSize: 30)); } @override dispose() { widget.data.removeListener(changeNotifier); super.dispose(); } } class People { String name; int age; People(this.name, this.age); } class PersonNotifier extends ValueNotifier<People> { PersonNotifier(People value) : super(value); void changePeopleName(String name) { value.name = name; notifyListeners(); }}Copy the code

Again, click RaisedButton and change ValueNotifier. Value to modify the UI.

Flutter dojo-widget-async-valuenotifier

With ValueNotifier, we encapsulate each Widget that might change due to changes in shared data, so that when data changes, only the Widget listening for that data is updated.

ValueNotifier, however, is a bit redundant, just like the NotifierWidget used earlier. In fact, most ValueNotifier needs to be used in this way. Flutter also provides a similar Widget, ValueListenableBuilder.

1-3: ValueListenableBuilder

ValueListenableBuilder is a Widget that encapsulates the use of ValueNotifier to simplify the creation process. On the page of Flutter Dojo, PageView and the following progress bar save the synchronization process. ValueListenableBuilder is used to do this.

Code location: Flutter Dojo-/pages/main/mainpage_scroll_container.dart

The usage paradigm of ValueListenableBuilder is very simple, which is to share administrative data through ValueNotifier on multiple widgets that create and listen for changes.

Because ValueListenableBuilder is a StatefulWidget, their parent widgets can organize widgets directly using StatelessWidgets, as shown in a simple example below.

class ValueListenableBuilderWidget extends StatefulWidget { @override _ValueListenableBuilderWidgetState createState() => _ValueListenableBuilderWidgetState(); } class _ValueListenableBuilderWidgetState extends State<ValueListenableBuilderWidget> { int _counter = 0; final ValueNotifier<int> _notifier = ValueNotifier<int>(0); void _incrementCounter() { _counter++; _notifier.value++; } @override Widget build(BuildContext context) { return Column( children: <Widget>[MainTitleWidget('ValueListenableBuilder basic use '), SubtitleWidget(' setState is not called when modifying data, SizedBox(height: 20), ValueListenableBuilder(valueListenable: _notifier, builder: (context, value, widget) { return Text('Click with ValueListenableBuilder $value'); }, ), SizedBox(height: 20), Text('Click without setState $_counter'), SizedBox(height: 20), RaisedButton( onPressed: () { _incrementCounter(); }, child: Text('Click me'), ), ], ); }}Copy the code

Code location: Flutter dojo-Widget-Async-ValuelistEnableBuilder

Cultivate immortality

Since the Flutter Dojo is open source, it has been enjoyed by many Flutter learners and enthusiasts. More and more people have joined the Flutter study, so I started a Flutter community, but there were too many people. Therefore, there are three groups of “Guide to Flutter” [North] [East]. If you are interested in Flutter, you can add my wechat account and indicate that you want to join the Flutter group, or directly follow my wechat public account [Android Group English].

If you are interested, please add me to wechat [Tomcat_xu] and I will join you in the group.

Project Address:

Github.com/xuyisheng/f…