Provider的使用

Provider是flutter官方提供的用来进行状态管理的一个框架,简单的可以理解为,当一个数据对象变化时,我们希望UI中能同步的更新,它能自动的监听变化,避免了手动调用的过程。
首先导入依赖

dependencies:
  provider: ^6.0.3

它的原理其实还是观察者模式,在最顶层的widget上包裹一个Provider,创建出我们需要监听的数据,在子widget中就可以拿到变化的数据。

1、创建一个被观察的对象,这个类中包含了我们所需要监听的数据

class Counter with ChangeNotifier{
  int _count = 0;

  int get count => _count;

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

2、在最顶层的widget,用一个Provider包裹起来

child: ChangeNotifierProvider<Counter>(create: (_)=>Counter(),child: MyApp(),)

// 上面是用范型的方式指定数据对象,也可以用下面的这种方式
child: ChangeNotifierProvider.value(
    value: Counter(),
    child: MyApp()
),

注意,这里不要使用Provider,因为它不会更新UI,要使用ChangeNotifierProvider或者是

MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Counter()),
      ],
      child: const MyApp(),
    ),

如果有多个对象就用上面这种。

3、在要使用的地方将其取出来

floatingActionButton: FloatingActionButton(
  key: const Key('increment_floatingActionButton'),

  /// Calls `context.read` instead of `context.watch` so that it does not rebuild
  /// when [Counter] changes.
  onPressed: () => context.read<Counter>().increment(),
  tooltip: 'Increment',
  child: const Icon(Icons.add),
),

上面一个是context.read, 这样widget不会被重建,在需要改变的地方就要用context.watch了

Widget build(BuildContext context) {
    return Text(
      /// Calls `context.watch` to make [Count] rebuild when [Counter] changes.
        '${context.watch<Counter>().count}',
        key: const Key('counterState'),
        style: Theme.of(context).textTheme.headline4);
  }

也可以用下面的这种方式

@override
  Widget build(BuildContext context) {
    var counter = Provider.of<Counter>(context);
    return Text(
      /// Calls `context.watch` to make [Count] rebuild when [Counter] changes.
        '${counter.count}',
        key: const Key('counterState'),
        style: Theme.of(context).textTheme.headline4);
  }

不过上面的这种方式不推荐使用,因为刷新的时候其他的子widget也会跟着一起刷新,比较影响性能。

或者用Consumer的方式

Widget build(BuildContext context) {
    return Consumer<Counter>(builder: (_,counter,child){
      return Text(counter.count.toString());
    });
  }

综上来看,还是context.watch和Consumer的方式要简单一些。

Selector

上面我们说了Consumer,Selector其实和Consumer差不多,只是Consumer处理的是一个对象,但是Selector处理的更精细一些,它可以处理一个对象中的某些成员,过滤掉一些不关心的数据,还是以上面的例子,我们将Counter改成

class Counter with ChangeNotifier{
  int _count = 0;

  String _name = "";

  int get count => _count;

  String get name => _name;

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

  void setName(){
    _name += "a";
    notifyListeners();
  }
}

增加了一个name字段,下面通过一个Selector来监听它的变化

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

  @override
  Widget build(BuildContext context) {
    return Selector<Counter, String>(
      builder: (_, name, child){
      	return Text(name);
    	}, 
      selector: (_, counter) => counter.name,);
  }
}

Selector也是一个范型类,需要传入对象和成员的类型Counter, String,然后在它的selector函数中选择需要处理的成员,在builder中构建一个Widget返回。

Timer使用

timer不需要再导入就来就可以使用了,一般我们就用它来做两件事情
1、每隔一定的时间去做一件事,周期性执行
2、一段时间后执行,只执行一次。
下面看一下它的用法
1、5s后执行

Timer timer1 = Timer(const Duration(seconds: 5), (){
      print("dosomething");
    });

2、每隔1s执行一次

timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      _setNowTime();
    });

需要注意的是Timer在创建完成后就开始计时了,不需要再调用start()之类的方法, 需要取消时调用timer.cancel()取消, 第二个参数是一个回调函数。