「编程笔记」Flutter中局部Provider的跨Page传递

开发过Flutter项目的同学应该对于Provider不陌生,Provider是Flutter中的一个状态管理包之一,也是Flutter官方推荐的状态管理Flutter实现。Provider通过Flutter的InheritWidget实现了状态的寄存和管理,使得子Widget可以访问和修改父Widget的数据,同时父Widgets的数据被修改后,支持自动按需重构Widgets,这里不对Provider做过多介绍。

一般情况下,我们创建的Provider会置于Flutter App的最根部,也就是MaterialApp或者CupertinoApp组件上,这种情况下,无需额外的操作,就可以在应用中的任何未知,通过Consumer<T>来访问和修改Provider<T>中的数据。

但部分情况下,我们不想让Provider置于项目的最根部,详情可以查看这个StackOverFlow Question,比如一个局部的List,仅仅在项目的某一个子Widgets树部分会被访问,所以理论上,我们将Provider尽可能的放到Widgets树的更低位置是一个更优的选择。比如Page1中创建Provider,同时Page1以及其下的Page2和Page3可以访问到这个Provider,但是在Page1之上的Widgets无需也不能访问到这个Provider。理论上这是可行的,根据理论我们只需要保证Provider是Consumer的祖先即可。但这里出现了一个问题,也就是如果我们尝试在Provider生效的范围内使用Navigator.push()导航到另一个页面,我们便无法正常的在被push的页面访问到Provider。

如上图,我们如果直接使用Navigator.push(),那么被Push的页面(途中是SecondPage)便无法访问到之前在FirstPage创建的数据。

下面介绍一种解决办法,我们可以在Navigator.push的时候,在push的PageRoute外嵌套一层Provider.value(),如下图

可以看下面的代码帮助理解:

Navigator.of(context).push(DAPageRoute(
                      // push the create survey page
                      builder: (context) =>
                          ChangeNotifierProvider<DAOrgInfoProvider>.value(
                        value: orgProvider,
                        child: DACreateSurveyPage(
                          orgName: widget.orgInfo.name,
                        ),
                      ),
                    ));

可以看出,我们通过在PageRoute外层加入一个ChangeNotifierProvider.value()实现了传递Provider的效果。

注意,这里使用Provider.value()而不是Provider(),因为我们的Provider实际上不是“新建”,而是“传递”,这说明我们实际上不能新建一个DAOrgInfoProvider class的实例,而是复用之前的Provider创建时使用的实例,在这里则是orgProvider。

那么如何获取之前Provider创建时的实例呢,一个比较傻但是直观的方法就是在Navigator外再次嵌套一个Consumer来获取数据,代码如下

Consumer<DAOrgInfoProvider>(
            builder: (context, orgProvider, child) {
              return DAIconButton(
                  onPressed: () {
                    Navigator.of(context).push(DAPageRoute(
                      // push the create survey page
                      builder: (context) =>
                          ChangeNotifierProvider<DAOrgInfoProvider>.value(
                        value: orgProvider,
                        child: DACreateSurveyPage(
                          orgName: widget.orgInfo.name,
                        ),
                      ),
                    ));
                  },
                  icon: const Icon(Icons.add_rounded));
            },
          )

以上就介绍完了如何通过Navigator传值的方法