从一个选项卡滑动到另一个选项卡时,TabView 不保留状态
Posted
技术标签:
【中文标题】从一个选项卡滑动到另一个选项卡时,TabView 不保留状态【英文标题】:TabView does not preserve state when swiping from one tab to another 【发布时间】:2020-11-22 21:06:11 【问题描述】:上下文:这里的页面有一个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
],
),
),
);
这里是函数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)
],
),
),
],
),
),
),
),
],
),
),
);
尝试过的解决方案:
1 - 我尝试了AutomaticKeepAliveClientMixin
,但结果证明mixin
在使用BottomNavigationBar
切换到另一个页面时保留页面的状态。
2 - PageStorage
没有解决问题。
问题:如何阻止TabView
在用户每次滑动到另一个标签时重新构建?
【问题讨论】:
也许尝试将_buildAppBarWithTabs()
和defaultViewforCategory
更改为小部件而不是函数?我在某处读到它在这样做时以不同的方式构建小部件树..
【参考方案1】:
正如您所说,问题之一是每次显示选项卡时都会重建TabBarView
。该问题是一个开放主题here。因此,每次屏幕更改时都会创建一个新的 Bloc 实例。
注意因为CategoryBloc
不是使用BlocProvider
传递的,所以您应该手动处理该集团。
这里的一个简单解决方案是将BlocProvider
在TabBarView
之外的层次结构中向上移动 - 例如,构建方法中的第一个组件。
注意从性能角度来看,这是可以的,因为当请求一个 bloc 时,BLOC 会延迟初始化。
现在更微妙的问题是CategoryBloc
的创建方式(因为有一个动态构造函数)。在这里你可以有两个解决方案:
您要么修改CategoryBloc
以供所有类别屏幕共享 - 在这里我无法为您提供太多帮助,因为我没有它的代码。这个想法是通过events
和emits
发送mainCategoryId
一个带有结果的新状态。在这种情况下,您应该将mainCategoryId
转发到state
,并且在BlocBuilder
上,仅当mainCategoryId
与CategoryScreen id 匹配时才使用buildWhen
参数构建(可以在创建屏幕时传递) .并且不要忘记在 TabBarView
孩子之外使用 BlocProvider
提供 CategoryBloc。
或将 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),
),
// ...
【讨论】:
以上是关于从一个选项卡滑动到另一个选项卡时,TabView 不保留状态的主要内容,如果未能解决你的问题,请参考以下文章
使用tabview更改选项卡时如何使SwiftUI中的计时器保持触发
用户切换浏览器选项卡时如何从 JApplet 中隐藏 JDialog?