上下文:这是一个带有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-{{1}}不能解决问题。
问题:如何在用户每次滑动到另一个选项卡时阻止重新构建PageStorage
?
答案 0 :(得分:0)
如您所述,问题之一是每次显示选项卡时都会重建TabBarView
。对于该问题,有一个开放主题here。因此,每次屏幕更改时都会创建一个新的Bloc实例。
注意,因为CategoryBloc
未通过BlocProvider
传递,因此您应该手动处置该集团。
一种简单的解决方案是将BlocProvider
在TabBarView
之外的层次结构中向上移动-例如,在build方法中是第一个组件。
注意就性能而言,这是可以的,因为在请求块时会延迟初始化BLOC。
现在,更为棘手的问题是创建CategoryBloc
的方式(因为具有动态构造函数)。在这里,您可以有两种解决方案:
要么将CategoryBloc
修改为可被所有类别屏幕共享-在这里,我没有太多帮助,因为我没有代码。这个想法是通过mainCategoryId
和events
向emits
发送带有结果的新状态。在这种情况下,您应该将mainCategoryId
转发到state
,并在BlocBuilder
上使用buildWhen
参数仅在mainCategoryId
与CategoryScreen id匹配时进行构建(创建屏幕时可以通过)。并且不要忘记还使用BlocProvider
子级之外的TabBarView
提供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),
),
// ...