如何在 Hot Reload 上使用 Provider 维护 Flutter Global BloC 状态?
Posted
技术标签:
【中文标题】如何在 Hot Reload 上使用 Provider 维护 Flutter Global BloC 状态?【英文标题】:How to maintain Flutter Global BloC state using Provider on Hot Reload? 【发布时间】:2019-09-07 02:41:47 【问题描述】:每当我执行热重载时,我似乎都会丢失应用程序状态。
我正在使用 BloC 提供程序来存储应用程序状态。这在 main.dart 中的 App 级别传递并在子页面上使用。在视图的初始加载时,会显示该值。我可以在应用程序中导航并且状态仍然存在。但是,当我执行热重载时,我会丢失值和看似状态。
如何解决此问题,以便在热重载时保留状态?
区块提供者
abstract class BlocBase
void dispose();
class BlocProvider<T extends BlocBase> extends StatefulWidget
BlocProvider(
Key key,
@required this.child,
@required this.bloc,
): super(key: key);
final T bloc;
final Widget child;
@override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context)
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
static Type _typeOf<T>() => T;
class _BlocProviderState<T> extends State<BlocProvider<BlocBase>>
@override
void dispose()
widget.bloc.dispose();
super.dispose();
@override
Widget build(BuildContext context)
return widget.child;
class MyApp extends StatelessWidget
// This widget is the root of your application.
@override
Widget build(BuildContext context)
return BlocProvider<ApplicationStateBloc>(
bloc: ApplicationStateBloc(),
child: MaterialApp(
title: 'Handshake',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: LoadingPage(),
)
);
class ProfileSettings extends StatefulWidget
@override
_ProfileSettingsState createState() => _ProfileSettingsState();
class _ProfileSettingsState extends State<ProfileSettings>
ApplicationStateBloc _applicationStateBloc;
@override
void initState()
super.initState();
_applicationStateBloc = BlocProvider.of<ApplicationStateBloc>(context);
@override
void dispose()
_applicationStateBloc?.dispose();
super.dispose();
Widget emailField()
return StreamBuilder<UserAccount>(
stream: _applicationStateBloc.getUserAccount,
builder: (context, snapshot)
if (snapshot.hasData)
return Text(snapshot.data.displayName, style: TextStyle(color: Color(0xFF151515), fontSize: 16.0),);
return Text('');
,
);
@override
Widget build(BuildContext context)
return BlocProvider<ApplicationStateBloc>(
bloc: _applicationStateBloc,
child: Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Column(
children: <Widget>[
emailField(),
.... // rest of code
class ApplicationStateBloc extends BlocBase
var userAccountController = BehaviorSubject<UserAccount>();
Function(UserAccount) get updateUserAccount => userAccountController.sink.add;
Stream<UserAccount> get getUserAccount => userAccountController.stream;
@override
dispose()
userAccountController.close();
【问题讨论】:
我认为你应该将_applicationStateBloc = BlocProvider.of<ApplicationStateBloc>(context);
移动到didChangeDependencies
而不是initState
方法
感谢您的建议,没有成功。 @override void didChangeDependencies() super.didChangeDependencies(); _applicationStateBloc = BlocProvider.of(context);
【参考方案1】:
我也遇到了同样的问题。继承的小部件使处理 bloc 的资源变得困难。 另一方面,有状态小部件允许处理,但在您使用的实现中,它不会将块保持在状态中,从而导致小部件重建时状态丢失。
经过一些试验,我想出了一种结合两者的方法:
class BlocHolder<T extends BlocBase> extends StatefulWidget
final Widget child;
final T Function() createBloc;
BlocHolder(
@required this.child,
@required this.createBloc
);
@override
_BlocHolderState createState() => _BlocHolderState();
class _BlocHolderState<T extends BlocBase> extends State<BlocHolder>
T _bloc;
Function hello;
@override
void initState()
super.initState();
_bloc = widget.createBloc();
@override
Widget build(BuildContext context)
return BlocProvider(
child: widget.child,
bloc: _bloc,
);
@override
void dispose()
_bloc.dispose();
super.dispose();
Bloc 持有者在 createState() 中创建 bloc 并将其持久化。它还在 dispose() 中处理 bloc 的资源。
class BlocProvider<T extends BlocBase> extends InheritedWidget
final T bloc;
const BlocProvider(
Key key,
@required Widget child,
@required T bloc,
)
: assert(child != null),
bloc = bloc,
super(key: key, child: child);
static T of<T extends BlocBase>(BuildContext context)
final provider = context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider;
return provider.bloc;
@override
bool updateShouldNotify(BlocProvider old) => false;
BlocProvider,顾名思义,只负责将块提供给嵌套的小部件。
所有的块都扩展了 BlocBase 类
abstract class BlocBase
void dispose();
这是一个用法示例:
class RouteHome extends MaterialPageRoute<ScreenHome>
RouteHome(List<ModelCategory> categories, int position): super(builder:
(BuildContext ctx) => BlocHolder(
createBloc: () => BlocMain(ApiMain()),
child: ScreenHome(),
));
【讨论】:
感谢您的建议,亚历克斯!很详细!我会尝试重现这个。【参考方案2】:您正在丢失状态,因为您的 bloc 正在 _ProfileSettingsState
的 initState()
中检索,因此,即使您热重载它也不会改变,因为该方法只调用一次 em> 构建小部件时。
在返回 BlocProvider
之前将其移至 build()
方法
@override
Widget build(BuildContext context)
_applicationStateBloc = BlocProvider.of<ApplicationStateBloc>(context);
return BlocProvider<ApplicationStateBloc>(
bloc: _applicationStateBloc,
child: Scaffold(
backgroundColor: Colors.white,
....
或didUpdateWidget
方法,该方法在小部件状态重建时调用。
请记住,如果您在集团中使用非广播流,如果您尝试收听已被收听的流,则可能会遇到异常。
【讨论】:
感谢您的建议,我已经尝试了您的建议,但似乎仍然不起作用。该块使用 BehaviorSubject。我已经完成了以下操作 -> @override void didChangeDependencies() super.didChangeDependencies(); _applicationStateBloc = BlocProvider.of(context);BehaviorSubject
的问题是它不会触发任何事件,除非value
引用本身发生变化。例如,如果您有一个BehaviorSubject<List<Something>>
,并且如果您正在处理的列表更改了一个值,您可以添加另一个引用作为值(例如List.from(oldList)
)。确保这不是您的问题,并且您可能会误导其他内容。
rxDart API 表示订阅者将自动获取添加到流中的最后一个值“一个特殊的 StreamController,它捕获已添加到控制器的最新项目,并将其作为第一个项目发送给任何新的侦听器。此主题允许向侦听器发送数据、错误和完成事件。已添加到主题的最新项目将发送给该主题的任何新侦听器。之后,任何新事件将适当地发送给侦听器. 如果没有向主题添加任何项目,则可以提供一个种子值。"
没错。我正是这个意思。您给行为主体的引用充当种子,但如果引用未更改,则不会触发任何事件,因此更改可能不会更新。以上是关于如何在 Hot Reload 上使用 Provider 维护 Flutter Global BloC 状态?的主要内容,如果未能解决你的问题,请参考以下文章
如何使 .NET 6.0 Hot Reload 在最简单的“Hello, World”控制台应用程序上工作?
Webpack:如何设置 webpack-dev-server 和 Hot Reload
Android 的 Instant Run vs Flutter Hot Reload 和 React Native Hot Reload 的区别?
Webpack / react hot reload重新加载整个页面?