Flutter Provider status management - Selector

If you do not know Consumer, please move on my blog post, Flutter Provider status management -Consumer , this article is based on a foundation of the past.

We know from previous Consumer can achieve local UI refresh the page, the temptation to fall traditional setState, make a step on the performance of the UI. But I use the Consumer in actual business scenarios has encountered many problems.

For chestnut:

A counter page, this is not a simple page counter, because it has two Text, we were named Text1 and Text2, and two Button, are the Button1 and Button2. Click Button1 to change the value of the Text1 change Button2 change Text2, by changing the values ​​are the same CounterProvider, but we maintain two data only, this time we hope that will redraw click Button1 only Text1, Text2 remain unchanged , but the reality is chilling. . .

We use the Consumer to get CounterProvider, as long as the value is changed it will notify the host refresh UI. So click on any one Button will result in Text1 and Text2 redraw, let's look at the specific code

1.counter_provider.dart
 

import 'package:flutter/material.dart';

class CounterProvider with ChangeNotifier {
  int _count = 0;
  int _count1 = 100;

  int get value => _count;

  int get value1 => _count1;

  void increment() {
    _count++;
    notifyListeners();
  }

  void increment1() {
    _count1++;
    notifyListeners();
  }
}

And a method of increasing the variable _count1 increment1 (), and provide the appropriate get methods

2.my_page.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'counter_provider.dart';

class MyPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyPageState();
}

class MyPageState extends State<MyPage> {
  ///初始化CounterProvider
  CounterProvider _counterProvider = new CounterProvider();

  @override
  Widget build(BuildContext context) {
    print('页面重绘了。。。。。。。。。。。');

    //整个页面使用ChangeNotifier来包裹
    return ChangeNotifierProvider(
      builder: (context) => _counterProvider,
      child:
          //child里面的内容不会因为数据的改变而重绘
          Scaffold(
        appBar: AppBar(
          title: Text('my page'),
        ),
        body: Center(
            child:
                //使用Cousumer来获取Provider
                Column(
          children: <Widget>[
            //使用Consumer来获取CounterProvider,为Text提供数据
            Consumer(builder: (BuildContext context,
                CounterProvider counterProvider, Widget child) {
              print('Text1重绘了。。。。。。');

              return Text(
                //获取数据
                'Text1 : ${counterProvider.value}',
                style: TextStyle(fontSize: 20),
              );
            }),
            //使用Consumer来获取CounterProvider,为Text提供数据
            Consumer(builder: (BuildContext context,
                CounterProvider counterProvider, Widget child) {
              print('Text2重绘了。。。。。。');

              return Text(
                //获取数据
                'Text2 : ${counterProvider.value1}',
                style: TextStyle(fontSize: 20),
              );
            }),
            RaisedButton(
              onPressed: () {
                print('Button 1被点击了。。。。。。。。。。');
                _counterProvider.increment();
              },
              child: Text('Button1'),
            ),
            RaisedButton(
              onPressed: () {
                print('Button 2被点击了。。。。。。。。。。');
                _counterProvider.increment1();
              },
              child: Text('Button2'),
            )
          ],
        )),
      ),
    );
  }
}

Consumer Text1 and Text2 by two separately wrapped, we hope the effect is:

  1. Click Button1 cause redraw the Text1, Text2 the print statements are not executed
  2. Click Button2 caused Text2 redraw, Text1 in the print statements are not executed

We look at the actual situation:

When click button1

When click button2

Whether Click the Button will cause redraw Text1 and Text2, which is clearly our intention to use Provider is violated. In the actual usage scenarios, this situation will bring a worse than expected performance of the UI.

Provider designers no doubt also found this problem, it launched Selector to solve such problems.

Let us transform counter demo, will replace the Consumer into Selector. International practice, first take a look at the constructor of a Selector:

Selector({
    Key key,
    //当父widget请求更新或者selector的返回值与之前的返回值不一样时会调用builder
    @required ValueWidgetBuilder<S> builder,
    //selector返回具体的值,返回的值必须继承自==而且不能为null
    @required S Function(BuildContext, A) selector,
    Widget child,
  })  : assert(selector != null),
        super(
          key: key,
          builder: builder,
          selector: (context) => selector(context, Provider.of(context)),
          child: child,
        );

Selector control of particle size finer than Consumer, Consumer all data changes Provider in a listening, listening Selector is a change in one / values . Specific code:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'counter_provider.dart';

class MyPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyPageState();
}

class MyPageState extends State<MyPage> {
  ///初始化CounterProvider
  CounterProvider _counterProvider = new CounterProvider();

  @override
  Widget build(BuildContext context) {
    print('页面重绘了。。。。。。。。。。。');
    
    //整个页面使用ChangeNotifier来包裹
    return ChangeNotifierProvider(
      builder: (context) => _counterProvider,
      child:
      //child里面的内容不会因为数据的改变而重绘
      Scaffold(
        appBar: AppBar(
          title: Text('my page'),
        ),
        body: Center(
            child:
            //使用Cousumer来获取Provider
            Column(
              children: <Widget>[
                //用Selector替换Consumer
                Selector(
                    builder: (BuildContext context, int data, Widget child) {
                      print('Text 1 重绘了。。。。。。。。。。');
                      return Text(
                        //获取数据
                          'Text1 : ${data.toString()}',
                          style: TextStyle(fontSize: 20));
                    }, selector:
                    (BuildContext context, CounterProvider counterProvider) {
                      //这个地方返回具体的值,对应builder中的data
                  return counterProvider.value;
                }),
                Selector(
                    builder: (BuildContext context, int data, Widget child) {
                      print('Text 2 重绘了。。。。。。。。。。');
                      return Text(
                        //获取数据
                        'Text2 : ${data.toString()}',
                        style: TextStyle(fontSize: 20),
                      );
                    },
                    selector: (BuildContext context,
                        CounterProvider counterProvider) {
                      return counterProvider.value1;
                    }),
                RaisedButton(
                  onPressed: () {
                    print('Button 1被点击了。。。。。。。。。。');
                    _counterProvider.increment();
                  },
                  child: Text('Button1'),
                ),
                RaisedButton(
                  onPressed: () {
                    print('Button 2被点击了。。。。。。。。。。');
                    _counterProvider.increment1();
                  },
                  child: Text('Button2'),
                )
              ],
            )),
      ),
    );
  }
}

After the transformation of the above code, we then look at the effect of a Button, you can clearly see click Button1 will only lead to draw Text1, click the Button2 grayed lead to draw Text2, and so far, Consumer leaving the pit perfect solution! !

Thinking:

Before the code is still in use, Scaffold in the body of the child is a center component, if we live with Consumer Center wrapped in what would be the effect? Click the button but also as a partial update does not affect the other components of thing like the above? Pseudo-code shown below

//用Consumer把selector包裹起来,Consumer和两个Selector都用的是CounterModel
Consumer(builder: (_, counterProvider, _) {
      return Column(
        children: <Widget>[
          Selector(builder: (_, value, _) {
            return Text(value);
          }, selector: (_, counterProvider) {
            return counterProvider.value;
          },),
          Selector(builder: (_, value1, _) {
            return Text(value1);
          }, selector: (_, counterProvider) {
            return counterProvider.value1;
          },),
        ],
      )
    });

The answer is no, because the Selector and Consumer are used in CounterProvider, if Selector monitor value changes, the Cousumer page can listen to, it will trigger the Consumer redraw the whole package of components.

 

One more question:

If I replace the Consumer Selector it, and with Button3 to control the value of the outermost Selector listening, pseudo-code is as follows:

//selector中还包含了两个子selector
Selector(
      builder: (_, value2, _) {
        return Column(
          children: <Widget>[
            //这个Text使用了value2
            Text(value2),
            Selector(
              builder: (_, value, _) {
                return Text(value);
              },
              selector: (_, counterProvider) {
                return counterProvider.value;
              },
            ),
            Selector(
              builder: (_, value1, _) {
                return Text(value1);
              },
              selector: (_, counterProvider) {
                return counterProvider.value1;
              },
            ),
          ],
        );
      },
      selector: (_, counterProvider) {
        return counterProvider.value2;
      },
    );

Look at the results

Click Button1 when we see only Text1 redraw

Click Button2 when only Text2 redraw,

Click Button3 time will cause Selector wrapped outermost redraw the whole content, due to the Text1 and Text2 also belong to its child, so redraw is inevitable

 

So we are also careful when using the Selector some of them, wrapped size as small as possible, Consumer more so. As for how to use the actual project selection Tell me what you see in the.

 

Well, almost that these, I hope that if there are problems to communicate, learn together! There wrong with please correct me

 

 

 

 

发布了21 篇原创文章 · 获赞 21 · 访问量 2万+

Guess you like

Origin blog.csdn.net/u013894711/article/details/102785532