Flutter:未处理的异常:错误状态:调用关闭后无法添加新事件(不同情况)
Flutter: Unhandled Exception: Bad state: Cannot add new events after calling close (NOT SAME CASE)
我正在尝试使用 BLoC 模式来管理来自 API 的数据并将它们显示在我的小部件中。我能够从 API 获取数据并处理并显示它,但我使用的是底部导航栏,当我更改选项卡并转到我的上一个选项卡时,它 returns 这个错误:
Unhandled Exception: Bad state: Cannot add new events after calling 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),
),
),
),
),
);
},
);
}
如果您需要完整的源代码,这是带有分支 submission-3
的回购协议
bloc.dispose();
是问题。
由于 bloc 是在您的 UI 代码之外初始化的,因此无需处理它们。
你为什么要在 bloc class 上实例化你的 bloc?
您必须将 bloc 实例添加到小部件树中的某处,使用具有某些提供程序逻辑的 InheritedWidget。然后在树下的小部件中,您将获取该实例并访问其流。这就是为什么将整个过程称为 'lifting up the state'.
这样,您的 bloc 在您需要时将始终处于活动状态,并且有时仍会调用 dispose。
例如一个集团提供者:
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
(所以它可以是一次性的)。
现在您必须将某个 bloc 的提供程序添加到您的小部件树中的某处,这取决于您,我个人喜欢将其添加到我的屏幕的路由之间。
在我的 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,
);
}
);
}
}
在路由的帮助下,我们的 'above' 建立了 homePage
集团。在这里,我可以在我想要的 bloc 上调用任何初始化方法,例如 .fetchAllMeals('Dessert')
,而无需使用 StatefulWidget
并在 initState
.
上调用它
现在很明显,要使此功能正常工作,您的集团必须实施 BlocBase
class
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
小部件的构建方法中,像这样获取 bloc 的可用实例:
var bloc = BlocProvider.of<MealsBloc>(context);
一个使用这种方法的简单项目here。
对于解决我的问题的答案,您可以按照以下link:This
希望你喜欢!!
我正在尝试使用 BLoC 模式来管理来自 API 的数据并将它们显示在我的小部件中。我能够从 API 获取数据并处理并显示它,但我使用的是底部导航栏,当我更改选项卡并转到我的上一个选项卡时,它 returns 这个错误:
Unhandled Exception: Bad state: Cannot add new events after calling 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),
),
),
),
),
);
},
);
}
如果您需要完整的源代码,这是带有分支 submission-3
的回购协议bloc.dispose();
是问题。
由于 bloc 是在您的 UI 代码之外初始化的,因此无需处理它们。
你为什么要在 bloc class 上实例化你的 bloc?
您必须将 bloc 实例添加到小部件树中的某处,使用具有某些提供程序逻辑的 InheritedWidget。然后在树下的小部件中,您将获取该实例并访问其流。这就是为什么将整个过程称为 'lifting up the state'.
这样,您的 bloc 在您需要时将始终处于活动状态,并且有时仍会调用 dispose。
例如一个集团提供者:
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
(所以它可以是一次性的)。
现在您必须将某个 bloc 的提供程序添加到您的小部件树中的某处,这取决于您,我个人喜欢将其添加到我的屏幕的路由之间。
在我的 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,
);
}
);
}
}
在路由的帮助下,我们的 'above' 建立了 homePage
集团。在这里,我可以在我想要的 bloc 上调用任何初始化方法,例如 .fetchAllMeals('Dessert')
,而无需使用 StatefulWidget
并在 initState
.
现在很明显,要使此功能正常工作,您的集团必须实施 BlocBase
class
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
小部件的构建方法中,像这样获取 bloc 的可用实例:
var bloc = BlocProvider.of<MealsBloc>(context);
一个使用这种方法的简单项目here。
对于解决我的问题的答案,您可以按照以下link:This
希望你喜欢!!