Flutter Provider

InheritedWidget 组件是功能型组件,提供了沿树向下,共享数据的功能,即子组件可以获取父组件(InheritedWidget 的子类)的数据,通过BuildContext.dependOnInheritedWidgetOfExactType 获取。

class MyInheritedWidget extends InheritedWidget {
  final UserInfo userInfo;

  MyInheritedWidget({this.userInfo, Widget child}):super(child: child);

  static MyInheritedWidget of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
    return oldWidget.userInfo != userInfo;
  }
}

updateShouldNotify 方法必须重写,此方法是判断新的共享数据和原数据是否一致,是否将通知传递给所有子组件(已注册)。重建此组件时,有时需要重建继承 InheritedWidget 组件的组件,但有时不需要。 例如,如果此组件所保存的数据与“ oldWidget”所保存的数据相同,则我们无需重建继承了“ oldWidget”所保存的数据的组件。

class _InheritedWidgetDemoState extends State {
  UserInfo _userInfo = UserInfo(name: 'lisa', age: 18);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('InheritedWidget Demo'),
      ),
      body: Center(
        child: MyInheritedWidget(
          userInfo: _userInfo,
          child: A(
            child: F(),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _userInfo = UserInfo(name: '小明', age: 18);
          });
        },
      ),
    );
  }
}

下面是A和F

class F extends StatefulWidget {
  @override
  _FState createState() => _FState();
}
class _FState extends State {
  @override
  void initState() {
    super.initState();
    print('F initState');
  }
  @override
  Widget build(BuildContext context) {
    print('F build');
    return Text('name:${MyInheritedWidget.of(context).userInfo.name}');
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('F didChangeDependencies');
  }
}

class A extends StatefulWidget {
  final Widget child;
  const A({Key key, this.child}) : super(key: key);
  @override
  _AState createState() => _AState();
}

class _AState extends State {
  @override
  void initState() {
    super.initState();
    print('A initState');
  }

  @override
  Widget build(BuildContext context) {
    print('A build');
    return Center(
      child: widget.child,
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('A didChangeDependencies');
  }
 
}

修改_userInfo时依赖 MyInheritedWidget 组件的 F 组件调用 didChangeDependencies 方法,而 A 组件没有调用 didChangeDependencies 方法,因为 A 没有依赖 MyInheritedWidget 组件。

F 组件使用 InheritedWidget 的共享数据并展示,如果 updateShouldNotify 返回 false,那么 F 组件 rebuild 时只会执行 build 函数,而 updateShouldNotify 返回 true时, F 组件 rebuild 时会执行 didChangeDependencies 和 build 函数,因此可以将类似访问服务器接口这种耗时工作放在 didChangeDependencies 函数中,这也是 didChangeDependencies 生命周期存在的意义。

使用方法

Step1:添加依赖
dependencies:
  flutter:
    sdk: flutter
   provider: ^4.0.4
Step:创建一个ChangeNotifier

我们先新建一个 NumberManager,继承 ChangeNotifier,使之成为我们的数据提供者之一。

class NumberManager with ChangeNotifier, DiagnosticableTreeMixin {
  int _firstNum = 0;
  int _secondNum = 0;

  int get firstNum => _firstNum;
  int get secondNum => _secondNum;

  void changeData1() {
    _firstNum++;
    notifyListeners();
  }

  void changeData2() {
    _secondNum++;
    notifyListeners();
  }
}
Step5:创建ChangeNotifierProvider
List providerList = [
   ChangeNotifierProvider(create: (context) => NumberManager()),
];

void main() {
  // ignore: prefer_const_constructors
  runApp(MultiProvider(
    providers: providerList,
    child: MyApp(),
  ));
  // runApp(const MyApp());
}

获取数据

1. 使用Provider.of(context)方法来引用数据

  • read读取数据,如果数据,不会主动调用build,所以数据变化后,显示的数据再没有重新build前,显示的还是之前的数据:
context.read()
 T read() {
    return Provider.of(this, listen: false);
  }
  • Provider.of(context) 可以简写为context.watch(),
context.watch()
T watch() {
   return Provider.of(this);
 }
}

通过此方法应用数据时,当前widget所在的build会在收到通知时进行重绘,所以在使用过程中如果不希望当前widget所在的类进行rebuild,可以为当前widget单独创建一个StatelessWidget:

class FirstNumWidget extends StatelessWidget {
  const FirstNumWidget({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print("----------FirstNumWidget被重执行了----------");

    //  使用Provider.of(context)方法来引用数据,每次数据变化都会重新build
    return ElevatedButton(
      child: Text(
          "consumer firstNum的值:${Provider.of(context).firstNum}"),
      onPressed: () {
        Provider.of(context, listen: false).changeData1();
      },
    );
  }
}

Consumer

具体用法如下,builder 中的参数分别是 Context context, T value, Widget child,value 即Model1,value 的类型和 Model1 类型一致,builder 方法返回的是 Widget,也就是被 Consumer 包裹的 widget,当监听的 model 值发生改变,此 widget 会被 Rebuild。

Consumer(
        builder: (context, model, child) {
          return Text('Model count:${model.count}');
        },
 )

Selector

Selectord定义

class Selector extends Selector0 {
    Key key,
    //  Widget Function(BuildContext context, T value, Widget child)
    // 用于构建 Widget
    @required ValueWidgetBuilder builder,
    // S Function(BuildContext, A)
    // 用于指定使用哪个值作为重重建判断依据
    @required S Function(BuildContext, A) selector,
    // bool Function(T previous, T next)
    // 是否重建,一般情况下不用我们实现
    ShouldRebuild shouldRebuild,
    Widget child,
  })

Selector 和 Consumer 很相似,唯一不同的是,Selector 可以自定义返回类型 ,所以Selector可以自定义是否在收到通知时进行页面重绘

Selector(
 builder: (context, count, child) => Text(
   "Selector示例演示: $count",
   style: Theme.of(context).textTheme.title,
 ),
 selector: (context, model) => model.count,
),

Selector 有 Selector0 ~ Selector6,具体使用大同小异,可以传 0 ~ 6 个 ChangeNotifer,但是 S(Value)泛型只有一个,如果需要多个个值同时判断返回 S 怎么办呢?这个时候可以使用元祖 Tuple

总结

Consumer 可以避免 widget 多余的 rebuild,当 Consumer 中监听的 value 不发生变化,其包裹的 widget 不会 Rebuild。 Selector 在 Consumer 基础上提供了更加精确的监听,还支持自定义 rebuild,可以更加灵活地控制 widget rebuild 问题。

你可能感兴趣的:(Flutter Provider)