导航到另一个 pageView 或任何 View 时,Bloc 小部件 reftech 数据(重建)
Bloc widgets reftech data (rebuild) when navigating to another pageView or any View altogether
上下文: 我有一个应用 PageView
使用 BottomNavigationBar
在五个屏幕之间导航,一个页面正在使用 Bloc 模式(在未来的版本中,几乎所有页面都将使用 Bloc)这些 Bloc 正在从后端服务获取数据,并用获取的数据填充 UI。
问题: 在小部件树的任何级别的页面之间导航时。 Bloc 小部件 'reset' 并从存储库重新获取数据。
如下图所示:
Widget树如下:
- main.dart
- home.dart
- timeline.dart(屏幕截图所示)
尝试过的解决方案: 我将 AutomaticKeepAliveClientMixin
添加到页面 class 和其他页面,但它没有完成工作。
以上截图所示页面的构建方法代码如下
@override
Widget build(BuildContext context) {
super.build(context);
return DefaultTabController(
length: 4,
child: Scaffold(
backgroundColor: Colors.white,
appBar: _builAppBarWithTabs(),
body: Center(
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: 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: 80,
child: BrandScreen(
brandBloc: context.bloc(),
),
),
),
CategoryScreen(
// Second Bloc
categoryBloc: CategoryBloc(
repository: context.repository(),
),
),
Container(
width: 200,
alignment: Alignment.center,
height: 330,
child: _buildFeaturedItemsList(),
),
Placeholder(
strokeWidth: 0,
color: Colors.white,
)
],
),
),
],
),
),
),
],
),
),
),
),
);
}
here 是 class timeline.dart
的代码
here 是 class home.dart
的代码,其中包含 BottomNavigationBar
class
问题:如何在每次重建小部件时阻止块获取数据?
您以错误的方式使用了 BlocProvider 小部件
相反,您应该将 BlocProvider 提取到在页面之间导航时不重建的上层 "maby to MaterialApp widget if you are using this bloc across your app"。
然后使用 BlocBuilder 或 BlocConsumer 为子部件提供现有的 Bloc 实例,并在 bloc 状态更改时自动重建。
BlocBuilder 官方文档:
https://bloclibrary.dev/#/flutterbloccoreconcepts?id=blocbuilder
我终于明白了这个问题
事实证明,这与 bloc
无关,而是与 PageView
有关的问题。
我用 PageStorage
小部件包装了 PageView
,如下所示:
final PageStorageBucket bucket = PageStorageBucket(); //PageStorageBucket
static List<Widget> pages = [
Timeline(pageController: pageController, key: PageStorageKey('timeline')),
Search(key: PageStorageKey('search')),
LikeScreen(key: PageStorageKey('like')),
CartScreen(key: PageStorageKey('cart')),
storageService.getFromDisk('api_token') != null
? ProfileScreen(key: PageStorageKey('profile'))
: AuthChooserScreen(key: PageStorageKey('auth')),
];
@override
Widget build(BuildContext context) {
super.build(context);
return SafeArea(
top: true,
bottom: true,
child: Scaffold(
backgroundColor: Colors.white,
key: _scaffoldKey,
//Wrapped PageView with PageStorage
body: PageStorage(
bucket: bucket,
child: PageView(
children: pages,
controller: pageController,
onPageChanged: onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
),
bottomNavigationBar: _buildBottomNavigationBar(),
),
);
}
每个页面都分配了一个 PageStorageKey,用于保存和恢复可以比小部件更有效的值
感谢这篇很棒的媒体文章,它完美地解释了它:
Keeping State with the Bottom Navigation Bar in Flutter
上下文: 我有一个应用 PageView
使用 BottomNavigationBar
在五个屏幕之间导航,一个页面正在使用 Bloc 模式(在未来的版本中,几乎所有页面都将使用 Bloc)这些 Bloc 正在从后端服务获取数据,并用获取的数据填充 UI。
问题: 在小部件树的任何级别的页面之间导航时。 Bloc 小部件 'reset' 并从存储库重新获取数据。
如下图所示:
Widget树如下:
- main.dart
- home.dart
- timeline.dart(屏幕截图所示)
- home.dart
尝试过的解决方案: 我将 AutomaticKeepAliveClientMixin
添加到页面 class 和其他页面,但它没有完成工作。
以上截图所示页面的构建方法代码如下
@override
Widget build(BuildContext context) {
super.build(context);
return DefaultTabController(
length: 4,
child: Scaffold(
backgroundColor: Colors.white,
appBar: _builAppBarWithTabs(),
body: Center(
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: 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: 80,
child: BrandScreen(
brandBloc: context.bloc(),
),
),
),
CategoryScreen(
// Second Bloc
categoryBloc: CategoryBloc(
repository: context.repository(),
),
),
Container(
width: 200,
alignment: Alignment.center,
height: 330,
child: _buildFeaturedItemsList(),
),
Placeholder(
strokeWidth: 0,
color: Colors.white,
)
],
),
),
],
),
),
),
],
),
),
),
),
);
}
here 是 class timeline.dart
here 是 class home.dart
的代码,其中包含 BottomNavigationBar
class
问题:如何在每次重建小部件时阻止块获取数据?
您以错误的方式使用了 BlocProvider 小部件 相反,您应该将 BlocProvider 提取到在页面之间导航时不重建的上层 "maby to MaterialApp widget if you are using this bloc across your app"。 然后使用 BlocBuilder 或 BlocConsumer 为子部件提供现有的 Bloc 实例,并在 bloc 状态更改时自动重建。 BlocBuilder 官方文档: https://bloclibrary.dev/#/flutterbloccoreconcepts?id=blocbuilder
我终于明白了这个问题
事实证明,这与 bloc
无关,而是与 PageView
有关的问题。
我用 PageStorage
小部件包装了 PageView
,如下所示:
final PageStorageBucket bucket = PageStorageBucket(); //PageStorageBucket
static List<Widget> pages = [
Timeline(pageController: pageController, key: PageStorageKey('timeline')),
Search(key: PageStorageKey('search')),
LikeScreen(key: PageStorageKey('like')),
CartScreen(key: PageStorageKey('cart')),
storageService.getFromDisk('api_token') != null
? ProfileScreen(key: PageStorageKey('profile'))
: AuthChooserScreen(key: PageStorageKey('auth')),
];
@override
Widget build(BuildContext context) {
super.build(context);
return SafeArea(
top: true,
bottom: true,
child: Scaffold(
backgroundColor: Colors.white,
key: _scaffoldKey,
//Wrapped PageView with PageStorage
body: PageStorage(
bucket: bucket,
child: PageView(
children: pages,
controller: pageController,
onPageChanged: onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
),
bottomNavigationBar: _buildBottomNavigationBar(),
),
);
}
每个页面都分配了一个 PageStorageKey,用于保存和恢复可以比小部件更有效的值
感谢这篇很棒的媒体文章,它完美地解释了它: Keeping State with the Bottom Navigation Bar in Flutter