Почему вы создаете экземпляр своего блока в классе блока?
Вы должны добавить свой экземпляр блока где-нибудь в дереве виджетов, используя InheritedWidget с некоторой логикой провайдера.Затем в ваших виджетах вниз по дереву вы бы взяли этот экземпляр и получили доступ к его потокам.Вот почему весь этот процесс называется «поднятием состояния».
Таким образом, ваш блок всегда будет жив, когда вам это нужно, и утилизация все равно будет вызываться когда-нибудь.
Поставщик блока, например:
import 'package:flutter/material.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
State<StatefulWidget> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<_BlocProviderInherited<T>>();
_BlocProviderInherited<T> provider = context
.ancestorInheritedElementForWidgetOfExactType(type)
?.widget;
return provider?.bloc;
}
static Type _typeOf<T>() => T;
}
class _BlocProviderState<T extends BlocBase> extends State<BlocProvider<T>> {
@override
Widget build(BuildContext context) {
return new _BlocProviderInherited(
child: widget.child,
bloc: widget.bloc
);
}
@override
void dispose() {
widget.bloc?.dispose();
super.dispose();
}
}
class _BlocProviderInherited<T> extends InheritedWidget {
_BlocProviderInherited({
Key key,
@required Widget child,
@required this.bloc
}) : super(key: key, child: child);
final T bloc;
@override
bool updateShouldNotify(InheritedWidget oldWidget) => false;
}
Используется комбинация InheritedWidget
(легко доступна в дереве виджетов) и StatefulWidget
(так что она может быть одноразовой).
Теперь вы должны добавить провайдера какого-либо блока где-нибудь в ваше дерево виджетов, это ваше дело, мне лично нравится добавлять его между маршрутами моих экранов.
В маршруте моего MaterialApp
widget:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MyApp',
onGenerateRoute: _routes,
);
}
Route _routes(RouteSettings settings) {
if (settings.isInitialRoute)
return MaterialPageRoute(
builder: (context) {
final mealsbloc = MealsBloc();
mealsbloc.fetchAllMeals('Dessert');
final homePage = DesertScreen();
return BlocProvider<DesertScreen>(
bloc: mealsbloc,
child: homePage,
);
}
);
}
}
С помощью маршрутов блок был создан «выше» нашего homePage
.Здесь я могу вызывать любые методы инициализации в блоке, которые я хочу, например, .fetchAllMeals('Dessert')
, без необходимости использовать StatefulWidget
и вызывать его на initState
.
Теперь, очевидно, чтобы это работало, ваши блоки должныреализует BlocBase
класс
class MealsBloc implements BlocBase {
final _repository = Repository();
final _mealsFetcher = PublishSubject<MealsList>();
Observable<MealsList> get allMeals => _mealsFetcher.stream;
fetchAllMeals(String mealsType) async {
MealsList mealsList = await _repository.fetchAllMeals(mealsType);
_mealsFetcher.sink.add(mealsList);
}
@override
dispose() {
_mealsFetcher.close();
}
}
Обратите внимание на переопределение на dispose()
, теперь ваши блоки будут располагаться самостоятельно, просто убедитесь, что все закрыто в этом методе.
Aпростой проект с таким подходом здесь.
Чтобы завершить это, в методе сборки вашего DesertScreen
виджета, получите доступный экземпляр блока, подобный следующему:
var bloc = BlocProvider.of<MealsBloc>(context);
Простой проектиспользуя этот подход здесь .