#dart #flutter #bloc
#dart #трепетание #блок
Вопрос:
Кажется, я теряю состояние приложения всякий раз, когда выполняю горячую перезагрузку.
Я использую поставщика блоков для хранения состояния приложения. Это передается на уровне приложения в main.dart и используется на дочерней странице. При начальной загрузке представления отображается значение. Я могу перемещаться по приложению, и состояние сохраняется. Однако, когда я выполняю горячую перезагрузку, я теряю значения и, по-видимому, состояние.
Как я могу исправить эту проблему, чтобы состояние сохранялось при горячей перезагрузке?
Поставщик блоков
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();
}
}
Комментарии:
1. Я думаю, вам следует перейти
_applicationStateBloc = BlocProvider.of<ApplicationStateBloc>(context);
кdidChangeDependencies
initState
методу вместо2. Спасибо за ваше предложение, оно не сработало. @переопределение аннулирует didChangeDependencies() { super.didChangeDependencies(); _applicationStateBloc = BlocProvider.of<ApplicationStateBloc>(контекст); }
Ответ №1:
Я столкнулся с той же проблемой. Унаследованные виджеты затрудняют использование ресурсов блока. Виджет с отслеживанием состояния, с другой стороны, позволяет удалять, но в используемой вами реализации он не сохраняет блок в состоянии, вызывая потерю состояния при перестроении виджетов.
После некоторых экспериментов я придумал подход, который сочетает в себе эти два:
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();
}
}
Держатель блока создает блок в createState() и сохраняет его. Он также распоряжается ресурсами блока в dispose().
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, как следует из названия, отвечает только за предоставление блока вложенным виджетам.
Все блоки расширяют класс BlockBase
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(),
));
}
Комментарии:
1. Спасибо вам за ваше предложение, Алекс! Довольно подробно! Я попытаюсь воспроизвести это.
Ответ №2:
Вы теряете состояние, потому что ваш блок извлекается в _ProfileSettingsState
‘s initState()
таким образом, оно не изменится даже при горячей перезагрузке, потому что этот метод вызывается только один раз при создании виджета.
Либо переместите его в build()
метод, непосредственно перед возвратом BlocProvider
@override
Widget build(BuildContext context) {
_applicationStateBloc = BlocProvider.of<ApplicationStateBloc>(context);
return BlocProvider<ApplicationStateBloc>(
bloc: _applicationStateBloc,
child: Scaffold(
backgroundColor: Colors.white,
....
или к didUpdateWidget
методу, который вызывается всякий раз, когда состояние виджета восстанавливается.
Имейте в виду, что если вы используете не широковещательный поток в своем блоке, вы можете получить исключение, если попытаетесь прослушать поток, который уже прослушивается.
Комментарии:
1. Спасибо за ваши предложения, я попробовал ваше предложение, и, похоже, оно все еще не работает. Блок использует BehaviorSubject. Я сделал следующее -> @override аннулирует didChangeDependencies() { super.didChangeDependencies(); _applicationStateBloc = BlocProvider.of<ApplicationStateBloc>(контекст); }
2. Проблема в
BehaviorSubject
том, что он не будет запускать какое-либо событие, если самаvalue
ссылка не изменится. Если у вас есть, например, aBehaviorSubject<List<Something>>
и если список, над которым вы работаете, изменяет значение, вы добавляете в качестве значения другую ссылку (например.List.from(oldList)
). Убедитесь, что это не ваша проблема и что вы можете вводить в заблуждение чем-то другим.3. RxDart API указывает, что подписчики автоматически получат последнее значение, добавленное в поток «Специальный StreamController, который фиксирует последний элемент, добавленный в контроллер, и передает его в качестве первого элемента любому новому слушателю. Эта тема позволяет отправлять данные, ошибки и выполненные события слушателю. Последний элемент, который был добавлен в тему, будет отправлен всем новым слушателям темы. После этого любые новые события будут соответствующим образом отправлены слушателям. Можно указать начальное значение, которое будет выдано, если в тему не было добавлено никаких элементов «.
4. Вот именно. Вот о чем я говорю. Ссылка, которую вы даете субъекту поведения, действует как начальное значение, но если ссылка не изменилась, событие не будет запущено, поэтому изменения могут не обновляться.