![](/img/trans.png)
[英]flutter: Unhandled Exception: Bad state: Cannot add new events after calling close
[英]Flutter: Unhandled Exception: Bad state: Cannot add new events after calling close (NOT SAME CASE)
我正在嘗試使用BLoC模式來管理來自API的數據,並將其顯示在我的小部件中。 我能夠從API提取數據並對其進行處理並顯示,但我使用的是底部導航欄,當我更改選項卡並轉到上一個選項卡時,它將返回此錯誤:
未處理的異常:錯誤狀態:調用close后無法添加新事件。
我知道這是因為我正在關閉流,然后嘗試對其進行添加,但是我不知道如何解決它,因為不處理發布主題將導致內存泄漏。
我知道也許這個問題與這個問題幾乎相同。
但是我已經實現了它,在我的情況下它不起作用,所以我用不同的代碼提出問題,希望有人可以幫助我解決我的情況。 希望您能理解,謝謝。
這是我的BLoC代碼:
import '../resources/repository.dart';
import 'package:rxdart/rxdart.dart';
import '../models/meals_list.dart';
class MealsBloc {
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);
}
dispose() {
_mealsFetcher.close();
}
}
final bloc = MealsBloc();
這是我的UI代碼:
import 'package:flutter/material.dart';
import '../models/meals_list.dart';
import '../blocs/meals_list_bloc.dart';
import '../hero/hero_animation.dart';
import 'package:dicoding_submission/src/app.dart';
import 'detail_screen.dart';
class DesertScreen extends StatefulWidget {
@override
DesertState createState() => new DesertState();
}
class DesertState extends State<DesertScreen> {
@override
void initState() {
super.initState();
bloc.fetchAllMeals('Dessert');
}
@override
void dispose() {
bloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: getListDesert()
);
}
getListDesert() {
return Container(
color: Color.fromRGBO(58, 66, 86, 1.0),
child: Center(
child: StreamBuilder(
stream: bloc.allMeals,
builder: (context, AsyncSnapshot<MealsList> snapshot) {
if (snapshot.hasData) {
return _showListDessert(snapshot);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white)
));
},
),
),
);
}
Widget _showListDessert(AsyncSnapshot<MealsList> snapshot) => GridView.builder(
itemCount: snapshot == null ? 0 : snapshot.data.meals.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
child: Card(
elevation: 2.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5))),
margin: EdgeInsets.all(10),
child: GridTile(
child: PhotoHero(
tag: snapshot.data.meals[index].strMeal,
onTap: () {
showSnackBar(context, snapshot.data.meals[index].strMeal);
Navigator.push(
context,
PageRouteBuilder(
transitionDuration: Duration(milliseconds: 777),
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) =>
DetailScreen(
idMeal: snapshot.data.meals[index].idMeal),
));
},
photo: snapshot.data.meals[index].strMealThumb,
),
footer: Container(
color: Colors.white70,
padding: EdgeInsets.all(5.0),
child: Text(
snapshot.data.meals[index].strMeal,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold, color: Colors.deepOrange),
),
),
),
),
);
},
);
}
如果您需要完整的源代碼,這是分支提交3的倉庫
bloc.dispose();
是問題。
由於塊是在您的UI代碼外部初始化的,因此無需處理它們。
為什么要在bloc類上實例化bloc?
您必須在小部件樹中的某個位置添加bloc實例,並使用帶有某些提供程序邏輯的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
小部件的潰敗中:
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()
的重寫,從現在開始,您的塊將自行處置,只需確保關閉此方法上的所有內容即可。
這里有一個使用這種方法的簡單項目。
為此,請在DesertScreen
小部件的build方法上,獲取可用的bloc實例,如下所示:
var bloc = BlocProvider.of<MealsBloc>(context);
一個簡單的項目使用這種方法在這里 。
對於解決我的問題的答案,你可以按照下面的鏈接: 此
我希望你喜歡它!!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.