前言

Flutter女装商城项目中使用的为Provide 1.x (估计停止维护了) ,但是于项目结尾更改为Provider版本为3.x,现最新版本为 4.0.5 [2020年4月13日],所以我尝试将版本升级到最新版本,但是出现了或多或少的问题,通过查阅资料最终解决,现将解决方案记录如下。

Provider简介

Provider为开发者提供了状态管理的方案,可以通过Provider实现状态的统一管理。

个人理解:一个全局的状态存储变量,然后当这个变量改变时,其他使用该状态的进行全局页面的动态改变,有一种牵一发而动全身的感觉。

使用方法

  1. 添加provider依赖
dependencies:
  ...
  # 状态管理
  provider: ^4.0.5
  ...	
  1. 新建Provider(以切换页面为例)
import 'package:flutter/material.dart';

// 切换底部导航栏

// 需要 混入[mixins] ChangeNotifier 
class CurrentIndexProvider with ChangeNotifier {

  int currentIndex = 0;

  changeIndex(int newIndex) {
    currentIndex = newIndex;
    notifyListeners();
  }
}

mixins 也是Dart实现多继承的一种方式,除此之外还有 继承[extends] 、 实现[implements], 且以上三种方式可以同时存在,但顺序要求extends -> mixins -> implements

  1. 引入provider
import 'package:provider/provider.dart';
  1. 创建顶层共享状态数据
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 需要用 MultiProvider 包裹
    return MultiProvider(
      providers: 
        // 添加页面的状态(其他Provider也是加在此位置)
        ChangeNotifierProvider(create: (_) => CurrentIndexProvider()),
      ],
      child: Container(
        child: MaterialApp(
          title: KString.mainTitle,
          // Flutter女装商城
          debugShowCheckedModeBanner: False,
          // 路由
          onGenerateRoute: Application.router.generator,
          // 定制主题
          theme: ThemeData(
            primaryColor: KColor.primaryColor,
          ),
          home: IndexPage(),
        ),
      ),
    );
  }
}
  1. 获取或修改当前状态【重点⭐️】
// 取到当前索引状态值
int currentIndex = Provider.of<CurrentIndexProvider>(context).currentIndex;

// 页面跳转
Provider.of<CurrentIndexProvider>(context, listen: False).changeIndex(index);

注意 ⭐️:在 Provider.of(context) 中有一个 bool 类型的 listen 参数,它代表了是否监听数据变化,默认为 true。

如果只是简单的获取值,获取状态,必须加入listen: False,否则会报:

Tried to listen to a value exposed with provider, from outside of the widget tree.

This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: False`.

To fix, write:
Provider.of<$T>(context, listen: False);

It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.

如果需要对当前页面或者当前Widget直接刷新,不能加listen: False,否则不会对当前页面进行实时的刷新。

  1. Consumer组件更新
@override
  Widget build(BuildContext context) {
    return Container(
      // 宽度
      width: ScreenUtil().setWidth(750),
      // 颜色
      color: Colors.white,
      // 用 Consumer 包裹
      child: Consumer<CartProvider>(
        builder:
            (BuildContext context, CartProvider cartProvide, Widget child) {
          return Container(
            ...
          );
        },
      ),
    );
  }

如果需要对其复杂页面组件进行动态的修改,可以将其包裹在 Consumer 中,该组件可以限制控件刷新范围,导致调用的 context 页面范围刷新。
其中builder 可以简写为 builder: (context, cartProvide, _){…}