从一个选项卡滑动到另一个选项卡时,TabView 不保留状态

Wal*_*hed 7 state-management flutter flutter-bloc

上下文:这里的页面可以TabView在所有这些选项卡使用的选项卡之间导航flutter_bloc(版本 6.0.1)。

问题:当滑动到任何选项卡时,状态不会被保留,整个小部件树正在重建,如下面的 gif 所示

滑动时未保留页面视图状态

这是build()方法:

 @override
  Widget build(BuildContext context) {
    super.build(context);
    return DefaultTabController(
      initialIndex: 0,
      length: 3,
      child: Scaffold(
        backgroundColor: Colors.white,
        appBar: _buildAppBarWithTabs(),
        body: TabBarView(
          children: <Widget>[
            defaultViewforCategory(1), //Women
            defaultViewforCategory(3), //Men
            defaultViewforCategory(2), //Kids
          ],
        ),
      ),
    );
  }
Run Code Online (Sandbox Code Playgroud)

下面是函数的实现 defaultViewforCategory()

Widget defaultViewforCategory(int mainCategoryId) {
    return PageStorage(
      bucket: bucket,
      key: PageStorageKey(mainCategoryId),
      child: ConstrainedBox(
        constraints: BoxConstraints(maxWidth: 1200),
        child: ListView(
          scrollDirection: Axis.vertical,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(bottom: 150),
              child: Container(
                height: 800,
                child: RefreshIndicator(
                  onRefresh: () => refreshTimeline(),
                  child: CustomScrollView(
                    scrollDirection: Axis.vertical,
                    slivers: <Widget>[
                      SliverToBoxAdapter(
                        child: MasonryGrid(
                          column: getResponsiveColumnNumber(context, 1, 2, 6),
                          children: <Widget>[
                            // First Bloc
                            BlocProvider(
                              create: (context) {
                                BrandBloc(repository: _brandRepository);
                              },
                              child: Container(
                                width: 200,
                                alignment: Alignment.center,
                                height: 90,
                                child: BrandScreen(
                                  brandBloc: context.bloc(),
                                ),
                              ),
                            ),
                            CategoryScreen(
                              // Second Bloc
                              categoryBloc: CategoryBloc(
                                  mainCategoryId: mainCategoryId,
                                  repository: _categoryRepository),
                            ),

                            // -------------- Featured Items--------------------------
                            Container(
                              width: 200,
                              alignment: Alignment.center,
                              height: 350,
                              child: _buildFeaturedItemsList(mainCategoryId),
                            ),
                            Placeholder(strokeWidth: 0, color: Colors.white)
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
Run Code Online (Sandbox Code Playgroud)

尝试过的解决方案: 1 - 我尝试过,AutomaticKeepAliveClientMixin但结果证明mixin在使用BottomNavigationBar.

2 -PageStorage没有解决问题。

问题:如何阻止TabView每次用户滑动到另一个选项卡时重建?

jor*_*iel 1

正如您所说,问题之一是TabBarView每次显示选项卡时都会重建。对于这个问题,这里是一个开放的话题。因此,每次屏幕更改时都会创建一个全新的 Bloc 实例。

注意由于CategoryBloc未通过使用,BlocProvider您应该手动处置该块。

这里一个简单的解决方案是将BlocProvider层次结构中的向上移动到TabBarView构建方法中的第一个组件之外。

注意从性能角度来看,这是可以的,因为当请求块时,BLOC 会被延迟初始化。

现在更微妙的问题是创建的方式CategoryBloc(因为有一个动态构造函数)。这里你可以有两种解决方案:

  1. 要么您将CategoryBloc屏幕修改为可由所有类别共享 - 在这里我无法为您提供太多帮助,因为我没有它的代码。这个想法是mainCategoryId通过结果发送新状态eventsemits在这种情况下,您应该将 转发到mainCategoryIdstate并且仅当匹配 CategoryScreen id(可以在创建屏幕时传递)时才BlocBuilder使用buildWhen参数进行构建。并且不要忘记在子级之外mainCategoryId提供使用的 CategoryBloc 。BlocProviderTabBarView

  2. 或者将 CategoryBloc 创建移到 TabBarView 之外并缓存它们以供进一步访问。我在下面创建了一个例子来强调这一点。

    // ...
    
    ///
    /// Categories blocs cache.
    ///
    Map<int, CategoryBloc> _categoriesBlocs;
    
    ///
    /// Creates UNIQUE instances of CategoryBloc by id.
    ///
    CategoryBloc getCategoryBlocById(int id) {
        // If you don't already have a bloc for that particular id, create a new one
        // and cache it (by saving it in the Map)
        this._categoriesBlocs.putIfAbsent(
            id,
            () => CategoryBloc(
            mainCategoryId: id,
                      repository: _categoryRepository,
            ));
    
        // Return the cached category bloc
        return this._categoriesBlocs[id];
      }
    
    ///
    /// This is very important. Because we manually create the BLOCs we have 
    /// to manually dispose them 
    ///
    @override
    void dispose() {
        for (var bloc in this._categoriesBlocs) {
         bloc.displose();
        }
        super.dispose();
    }
    
    @override
    Widget build(BuildContext context) {
        super.build(context);
    
        return MultiBlocProvider(
            providers: [
                BlocProvider(
                    create: (context) => BrandBloc(repository: _brandRepository),
                )
            ],
            child: DefaultTabController(
                initialIndex: 0,
                length: 3,
                child: Scaffold(
                    backgroundColor: Colors.white,
                    appBar: _buildAppBarWithTabs(),
                    body: TabBarView(
                        children: <Widget>[
                            defaultViewforCategory(1), //Women
                            defaultViewforCategory(3), //Men
                            defaultViewforCategory(2), //Kids
                        ],
                    ),
                ),
            ),
        );
    }
    
      // ...
    
      CategoryScreen(
      // Second Bloc
      // Now, here you will get the same BLOC instance every time
          categoryBloc: getCategoryBlocById(mainCategoryId),
      ),
    
      // ...
    
    Run Code Online (Sandbox Code Playgroud)