对Provider中的Consumer和Selector的实践

270 阅读3分钟

将以下源码复制粘贴到你的Demo下自行探索,很容易明白的。

源码后面有对Selector的解释

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("----------整个MyApp被绘制----------");
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("MyProviderDemo")),
        body: MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (context) => MyNotifier0()),
            ChangeNotifierProvider(create: (context) => MyNotifier1()),
            ChangeNotifierProvider(create: (context) => MyNotifier2()),
          ],
          child: MyProviderDemo(),
        ),
      ),
    );
  }
}

class MyProviderDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    MyNotifier0 myNotifier0 = Provider.of<MyNotifier0>(context);
    print("----------整个MyProviderDemo被重绘----------");
    return Column(
      children: <Widget>[
        //使用Provider.of<T>(context)方法来引用数据
        RaisedButton(
          child: Text(myNotifier0.dataInt.toString()),
          onPressed: () {
            myNotifier0.changeData();
          },
        ),
        //使用Consumer方法来引用数据
        //第一个Consumer:
        Consumer(
          builder: (BuildContext context, MyNotifier1 value, Widget child) {
            print("----------第一个Consumer被重绘了----------");
            return RaisedButton(
              child: Text("引用而为被使用"),
              onPressed: () {},
            );
          },
        ),
        //第二个Consumer:
        Consumer(
          builder: (BuildContext context, MyNotifier1 value, Widget child) {
            print("----------第二个Consumer被重绘了----------");
            return RaisedButton(
              child: Text(value.dataInt1.toString()),
              onPressed: () {
                value.changeData1();
              },
            );
          },
        ),
        //第三个Consumer:
        Consumer(
          builder: (BuildContext context, MyNotifier1 value, Widget child) {
            print("----------第三个Consumer被重绘了----------");
            return RaisedButton(
              child: Text(value.dataInt2.toString()),
              onPressed: () {
                value.changeData2();
              },
            );
          },
        ),
        //第四个Consumer:
        Consumer(
          builder: (BuildContext context, MyNotifier2 value, Widget child) {
            print("----------第四个Consumer被重绘了----------");
            return RaisedButton(
              child: Text(value.dataInt.toString()),
              onPressed: () {
                value.changeData();
              },
            );
          },
        ),
        //第一个Selector:
        Selector(
          selector: (BuildContext context, MyNotifier1 myNotifier1) {
            print("----------第一个Selector中的selector被重执行了----------");
            return myNotifier1;
          },
          builder: (BuildContext context, MyNotifier1 myNotifier1, Widget child) {
            print("----------第一个Selector中的builder被重绘了----------");
            return RaisedButton(
              child: Text(myNotifier1.dataInt1.toString()),
              onPressed: () {
                myNotifier1.changeData1();
              },
            );
          },
        ),
        //第二个Selector:
        Selector(
          selector: (BuildContext context, MyNotifier1 myNotifier1) {
            print("----------第二个Selector中的selector被重执行了----------");
            return myNotifier1;
          },
          builder: (BuildContext context, MyNotifier1 myNotifier1, Widget child) {
            print("----------第二个Selector中的builder被重绘了----------");
            return RaisedButton(
              child: Text(myNotifier1.dataInt1.toString()),
              onPressed: () {
                myNotifier1.changeData1();
              },
            );
          },
        ),
        //第三个Selector:
        Selector(
          selector: (BuildContext context, MyNotifier1 myNotifier1) {
            print("----------第三个Selector中的selector被重执行了----------");
            return myNotifier1.dataInt1;
          },
          builder: (BuildContext context, int dataInt1, Widget child) {
            print("----------第三个Selector中的builder被重绘了----------");
            return RaisedButton(
              child: Text(dataInt1.toString()),
              onPressed: () {},
            );
          },
        ),
        //第四个Selector:
        Selector(
          selector: (BuildContext context, MyNotifier1 myNotifier1) {
            print("----------第四个Selector中的selector被重执行了----------");
            return [myNotifier1.dataInt1, myNotifier1.changeData1];
          },
          builder: (BuildContext context, List list, Widget child) {
            print("----------第四个Selector中的builder被重绘了----------");
            return RaisedButton(
              child: Text(list[0].toString()),
              onPressed: () {
                list[1]();
              },
            );
          },
        )
      ],
    );
  }
}

class MyNotifier0 with ChangeNotifier {
  int dataInt = 0;
  void changeData() {
    dataInt++;
    notifyListeners();
  }
}

class MyNotifier1 with ChangeNotifier {
  int dataInt1 = 0;
  int dataInt2 = 0;
  void changeData1() {
    dataInt1++;
    notifyListeners();
  }

  void changeData2() {
    dataInt2++;
    notifyListeners();
  }
}

class MyNotifier2 with ChangeNotifier {
  int dataInt = 0;
  void changeData() {
    dataInt++;
    notifyListeners();
  }
}

Selector上有话要说,Selector构造函数源码:

Selector({
    Key key,
    @required ValueWidgetBuilder<S> builder,
    @required S Function(BuildContext, A) selector,
    ShouldRebuild<S> shouldRebuild,
    Widget child,
  }) 
//第一个Selector:
        Selector(
          selector: (BuildContext context, MyNotifier1 myNotifier1) {
            print("----------第一个Selector中的selector被重执行了----------");
            return myNotifier1;
          },
          builder: (BuildContext context, MyNotifier1 myNotifier1, Widget child) {
            print("----------第一个Selector中的builder被重绘了----------");
            return RaisedButton(
              child: Text(myNotifier1.dataInt1.toString()),
              onPressed: () {
                myNotifier1.changeData1();
              },
            );
          },
        ),

第一个Selector第二个Selector中,之所以builder属性不执行,是因为Selector()中还有个属性shouldRebuild,可以自定义如何重绘算法,这个属性返回true,则会执行builder属性,返回false则不会执行builder属性。shouldRebuild的默认算法是new_myNotifier1 != old_myNotifier1等于true时才会返回true。而在第一个Selector第二个Selector中 ,selector属性所筛选出的是myNotifier1,在myNotifier1.changeData1()后,myNotifier1对象并未被改变,也就是new_myNotifier1 != old_myNotifier1等于false返回false了,因此不会执行builder属性.

Selector()还有个属性child,builder中引用的Widget child即是Selector()中的child。就算builder被重新渲染,这个child也不会被重新渲染,这也充分展现出“类diff算法”的超前性。

加上child后可这样扩展:

 //第一个Selector:
        Selector(
          selector: (BuildContext context, MyNotifier1 myNotifier1) {
            print("----------第一个Selector中的selector被重执行了----------");
            return myNotifier1;
          },
          builder: (BuildContext context, MyNotifier1 myNotifier1, Widget child) {
            print("----------第一个Selector中的builder被重绘了----------");
            return RaisedButton(
              child: Column(children: <Widget>[
                Text(myNotifier1.dataInt1.toString()),
                child,"//新增的child"
              ]),
              onPressed: () {
                myNotifier1.changeData1();
              },
            );
          },
          child: Text("这是Selector()中的child,不会被builder所重绘"),"//新增的child"
        ),