Provider状态管理

安装Provider

1
flutter pub add provider 

Provider全局注册

  • 如果需要在跨路由页面中共享数据,则需要放在MaterialApp上方包裹
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return ChangeNotifierProvider(
          create: (context) => LikeModel(),
          child: MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            routes: {
              "/": (context) => const MyHomePage(title: 'Flutter Demo Home Page'),
              "/detail" : (context) => const DetailPage(),
            },
          ),
        );
      }
    }
    

模型类

需要监听值的变化动态刷新页面的类需要继承ChangeNotifier 不需监听值的变化的则直接使用class即可 使用extendswith都可以

1
2
  class Test extends ChangeNotifier{
  }
1
2
  class Test with ChangeNotifier{
  }

模型类示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class LikeModel extends ChangeNotifier {
  final List<int> _likes = [];

  UnmodifiableListView<int> get items => UnmodifiableListView(_likes);

  bool hasSelected(int value) {
    return _likes.contains(value);
  }

  void add(int selected) {
    _likes.add(selected);
    // 在修改后需要通知的方法结尾调用以下代码 
    // 用于通知依赖此模型的Widget更新页面
    notifyListeners();
  }

  void remove(int selected) {
    _likes.remove(selected);
    notifyListeners();
  }
}

Provider分类

  • Provider 通过委托的方式将值传递给后代Widget 提供 create、dispose方法来处理值的创建和销毁 不会主动通知Widget值的变化
    1
    2
    3
    4
    5
    6
    7
    Provider<TestModel>(
      // Provider 默认采用懒加载方式调用create方法,默认在第一次使用值的时候才调用create
      // 可通过以下配置禁止该行为
      // lazy: false,
      create: (context) => TestModel(),
      dispose: (context, value) => {value.name = ""},
    ),
    
  • ProxyProvider
  • ChangeNotifierProxyProvider
  • MultiProvider 可以包含多个Provider
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     return MultiProvider(
          providers: [
            Provider<TestModel>(
              // lazy: false,
              create: (context) => TestModel(),
              dispose: (context, value) => {value.name = ""},
            ),
            FutureProvider<http.Response>(
              create: (_) => FetchService().fetchData(),
              initialData: http.Response("test", 200),
            ),
            StreamProvider<int>(create: (_) => Person(25).age, initialData: 15),
            ValueListenableProvider<double>.value(value: ValueNotifier(0.0))
          ],
          child: MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: const MyHomePage(title: 'Flutter Demo Home Page'),
          ),
        );
    
  • ChangeNotifierProvider 类似Provider 区别是会在值变化时通知依赖此值的Widget更新

    1
    ChangeNotifierProvider<ThemeChanger>(create: (context) => ThemeChanger()),
    

  • FutureProvider 监听Future类型并将值传递给后代Widget 必须提供初始值 会将值的变化通知后代Widget

    1
    2
    3
    4
    5
    6
    FutureProvider<http.Response>(
      create: (_) => FetchService().fetchData(),
      initialData: http.Response("test", 200),
      // 提供方法来判断是否需要通知依赖Widget更新页面
      updateShouldNotify: (previous, current) => previous != current,
    ),
    

  • StreamProvider 返回数据流,生成器方式 必须提供初始值 会将值的变化通知后代Widget
    1
    StreamProvider<int>(create: (_) => Person(25).age, initialData: 15),
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    class Person {
      final int _age;
      Person(this._age);
    
      Stream<int> get age async* {
        int i = 0;
        while (i < _age) {
          await Future.delayed(const Duration(milliseconds: 200), () => i++);
          yield i;
        }
      }
    }
    

获取数据的方法

  • context.read 只在页面刷新时读取数据,不监听数据的变化 不可以用于build方法内 可以用于回调函数内
    1
    2
    3
    4
    ElevatedButton(
      onPressed: () => {context.read<ThemeChanger>().change()},
      child: const Text("change theme"),
    ),
    
  • context.watch 会根据数据的变化而更新页面的值
    1
    Text(context.watch<TestModel>().toString()),
    
  • context.select 获取指定单个值 可监听数据更新 也可用于不可变数据
    1
    var name = context.select<TestModel, String>((value) => value.name);
    
  • 行为和context.watch相同 会监听值的变化
    1
    Provider.of<T>(context)
    
    1
    Text(Provider.of<TestModel>(context).name),
    
  • 行为和context.read相同 但是可用于build方 法 不会监听值的变化
    1
    Provider.of<T>(context,listen=false)
    
    1
    Provider.of<ThemeChanger>(context,listen=false).change()
    
  • 订阅值的另一种写法
    1
    Consumer<T>(builder:(context,T value,child)=>Widget)  
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    return ListTile(
      title: Text("List Item ${list[i]}"),
      trailing: Consumer<LikeModel>(
        builder: (context, value, child) {
          bool selected = value.hasSelected(list[i]);
          return IconButton(
            icon: Icon(selected ? Icons.favorite : Icons.favorite_border),
            color: selected ? Colors.red : null,
            onPressed: () {
              _toggleBtn(value, list[i]);
            },
          );
        },
      ),
      onTap: () => {log("${list[i]}"), _navigator(context, list[i])},
    );