Closed mujtabamahmood closed 4 years ago
Hi @edgebasis !
You're trying to use a BlocListener
to render UI which will not work, since listeners are used for side effects like navigation, showing dialogs, etc. If you need to return pieces of your UI then please use BlocBuilder
instead.
As for your BlocProvider
error, make sure you have a bloc of the needed type above the widget where you're trying to access it, or else you'll get that error.
If you're still having issues please share a minimal repo/gist showcasing them.
Hi @RollyPeres , thanks for taking the time on Sunday! I tried what you said but still i get the same error. I am using a stream to fetch the data as you can see food_list.dart file. Not using the same bloc. does that affect? here is the public gist: https://gist.github.com/edgebasis/07a62df74a469ce850f320be6cbdbfa6
You're consuming ItemDetailBloc
in your ItemDetailsScreen
before you provide the bloc. Your bloc is only provided in ShowItemDetails
widget. You should move your BlocProvider
above your BlocBuilder
.
I just moved the provider to ItemDetailsScreen
widget and then moved the builder to ShowItemDetails
widget.
Now we i navigate to the ItemDetailsScreen
i get this error:
RepositoryProvider.of() called with a context that does not contain a repository of type MenuItemsRepository.
No ancestor could be found starting from the context that was passed to RepositoryProvider.of<MenuItemsRepository>().
is there anything else i need to do before navigating to the ItemDetailsScreen
?
this is how the code looks like in the ItemDetailsScreen
:
class ItemDetailsScreen extends StatelessWidget {
final int menuItemId;
ItemDetailsScreen({@required this.menuItemId});
@override
Widget build(BuildContext context) {
// final itemDetailBloc = BlocProvider.of<ItemDetailBloc>(context);
final itemDetailService = RepositoryProvider.of<MenuItemsRepository>(context);
return Scaffold(
appBar: AppBar(
title: Text(
"Item details",
style: TextStyle(color: Colors.black),
),
),
body: BlocProvider<ItemDetailBloc>(
create: (context) => ItemDetailBloc(itemDetailService),
child: SafeArea(
child: ShowItemDetails(),
),
));
}
}
class ShowItemDetails extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return BlocBuilder<ItemDetailBloc, ItemDetailState>(
builder: (context, state) {
if (state is ItemDetailSuccessful) {
return Container(
alignment: Alignment.center,
child: Container(
child: Text("Item details"),
),
);
} else if (state is ItemDetailFailure) {
return Container();
} else
return null;
});
}
}
thank you
@edgebasis you're getting that error because you're trying to obtain an instance of your repository using RepositoryProvider.of<MenuItemsRepository>(context)
but you haven't actually provided that repository in your tree. You need to create it using RepositoryProvider(create: (_) => MenuItemsRepository())
.
It's up to you if this is a globally available repository or you wanna scope it to your details page, in which case you should provide it just above BlocProvider<ItemDetailBloc>
, which you can then initialize like ItemDetailBloc(context.repository<MenuItemsRepository>())
@RollyPeres thanks for the reply. I actually went and redone the bloc architecture from the list of items and wrapped it up with a provider. I think i did what you mentioned and that is to create the repository and that was in the root (list view of the items) see below:
Container( height: 240, width: 200, child: BlocProvider( create: (context) => MenuBloc(menuItemsRepository: MenuItemsRepository()), child: FoodList(), ), ),
then in then in the FoodList
widget i reconstructed everything as:
class _FoodListState extends State<FoodList> {
MenuBloc menuBloc;
@override
void initState() {
super.initState();
menuBloc = BlocProvider.of<MenuBloc>(context);
menuBloc.add(FetchMenuEvent());
}
@override
Widget build(BuildContext context) {
return Container(
child: Container(
child: BlocListener<MenuBloc, MenuState>(
listener: (context, state) {
if (state is MenuFailureState) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(state.message),
));
}
},
child: BlocBuilder<MenuBloc, MenuState>(builder: (context, state) {
if (state is MenuInitialState) {
return _buildLoadingWidget();
} else if (state is MenuLoadingState) {
return _buildLoadingWidget();
} else if (state is MenuLoadedState) {
return _buildMenuItemsListWidget(state.menu);
} else if (state is MenuFailureState)
return _buildErrorWidget(state.message);
else
return null;
}),
),
),
);
}
I then used the bellow method to navigate to the detail page and found that i dont really need use bloc in order to construct the details screen.
void _navigateToMenuItemDetailScreen(BuildContext context, Item food) { Navigator.push(context, MaterialPageRoute(builder: (context) { return ItemDetailsScreen(foodItem: food,); })); }
it works for now!
Glad you found a solution.
I'd also convert food list widget to a stateless one and add event when bloc is created.e.g.:
BlocProvider<MenuBloc>(create: (_) => MenuBloc()..add(FetchMenuEvent()))
Thanks @RollyPeres , i will definitely look in this solution too, it makes more sense to have a stateless widget for the Food list. thank you
Hi, I am trying to build my first app in Flutter using flutter_bloc. I tried many ways to solve this but never succeeded. I have a list of items that are fetched from the API using a stream and wanted if click on an item to view the details of that item. In the ItemDetailScreen:
`class ShowItemDetails extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return BlocListener<ItemDetailBloc, ItemDetailState>( listener : (context, state) { if (state is ItemDetailSuccessful ){ return Center( child: Text("Item details"), ); }else if (state is ItemDetailFailure ){ return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children:[
Text(state.message),
FlatButton(
textColor: Theme.of(context).primaryColor,
child: Text('Retry'),
onPressed: () {
}`
somewhere in the listview widget i did this (follow link):
onTap: () => Navigator.push( context, MaterialPageRoute( builder: (context) => ItemDetailsScreen(menuItemId: food.id))), child: Container(
https://github.com/edgebasis/flutterApp/blob/master/lib/widgets/food_list.dart error i get is:
`════════ Exception caught by widgets library ═══════════════════════════════════════════════════════ The following assertion was thrown building ShowItemDetails: BlocProvider.of() called with a context that does not contain a Bloc of type ItemDetailBloc.
I am not sure if i need to do anything else before i navigate to the detail screen.
thanks