Bloc: flutter bloc about use multiple states at the same time

Created on 29 Mar 2019  路  39Comments  路  Source: felangel/bloc

can blocBuillder receive multiple states like (context, state:[stateA, stateB]) from different bloc?

question

Most helpful comment

Hi @MonNguyen2502 馃憢

You can just wrap any parts of the UI which depend on the network connection in a BlocBuilder and pass the rootbloc in order to rebuild UI in response to state changes in the rootbloc.

Hope that helps 馃憤

All 39 comments

Hi @windinvip 馃憢

Currently BlocBuilder only responds to state changes for a single bloc. Can you please describe the use-case you have? In most cases, I think you should have separate BlocBuilders for the separate parts of the UI that need to rebuild in response to those states.

Closing for now but feel free to comment with additional details and I鈥檓 happy to continue the conversation 馃憤

@felangel I have rootbloc that covers the entire app, and rootbloc listens to the network connection event.
How the pages in the app can know when the network connection changes. I don't want every page to listen to the network connection

Hi @MonNguyen2502 馃憢

You can just wrap any parts of the UI which depend on the network connection in a BlocBuilder and pass the rootbloc in order to rebuild UI in response to state changes in the rootbloc.

Hope that helps 馃憤

@felangel If you do so, the blocbuilder will be nested, there are any problems when that happens

The blocbuilder will be called every time the father's blocbuilder state changes, so the state of the blocbuilder will be called again.

There鈥檚 no problem with nesting it if that鈥檚 how you want to structure your application. You might want to handle the network connectivity state within the feature blocs instead. You can have each feature bloc depend on a network bloc and the features bloc state could vary depending on if the user has a network connection or not. That way you eliminate the need to nest another BlocBuilder.

@felangel Thank you very much, hope you will think about using multi bloc for blocbuilder.

@felangel I also ran into this problem when I use bloc pattern in my application.
Let's say I have two two blocs, a notification bloc which receives a new notification from a server and a tab bloc which receives an index of tab when a user presses a bottom navigation tab like Todo tutorial in flutter bloc website.
I have a UI that has an app bar and a bottom navigation bar. Whenever a new notification is received, it updates an icon in the app bar to show the app has an unread notification. This new notification also updates a badge in a bottom navigation bar to show that category is updated.
As you mentioned above, nesting a bloc builder can solve this problem but it can also lead to a redundant update.
In this scenario, it would be handy if we have a BlocBuilder that takes a multiple blocs to handle states from two blocs. This is a case that I am facing but I think there will be a case that multiple blocs can update the same UI. I'm fairly new to bloc pattern and flutter so please correct me if this is a bad practice.
Thank you for this awesome library and a tutorial!

@njovy thanks for the positive feedback and for the great question!

Can you please explain a bit more what you mean when you say, "it can lead to a redundant update"?
You can wrap only the UI that needs to change in response to a notification in a BlocBuilder rather than wrapping everything. If it's possible, it'd be awesome if you can share a sample app that illustrates your current setup so that I can give more specific recommendations about how to structure your blocs. Let me know and thanks again 馃憤

@felangel Thanks for the quick reply. I will set up a demo repository as soon as it's ready.

@felangel Please take a look at this demo and see if this bloc usage is okay. I was wrong about a redundant update. Sorry for the confusion. I basically wrapped the only UI to change as you suggested. Also, I was wondering if a bloc can be used in a multiple bloc builder. The demo has a notification bloc which just toggles a state every 10 seconds. When a notification state is changed, it updates an action button and a bottom navigation icon to show a new notification is there. Please let me know if I structure blocs correctly.
One more question, the notification bloc should be available globally. Is it okay to have a bloc holder so it can be accessed when app is running? What is a proper or preferred way to have a globally available bloc?

@njovy thanks for the demo! I'll take a look in the next few hours 馃憤

@njovy sorry for the delay but I just took a quick look at your demo and it looks good to me 馃憤
Regarding your comment about using a bloc in a multiple bloc builder, can you please provide an example of how you would like to use the multiple bloc builder? I think that would help me better understand what you're trying to accomplish.

I would recommend defining the notification bloc in the root widget and making it globally available using BlocProvider/BlocProviderTree (like you already have). That approach makes it easy to access the bloc from anywhere in your application while also making it easy to test your individual widgets by providing a mock bloc.

Hope that helps! 馃憤

@felangel Thank you for taking your time looking at my demo. Your comment is always helpful. I understand that we all are busy with our stuff so no worries. The same notification bloc is used with two BlocBuilders here and here. Whenever a notification state is updated, the two builder gets called so that's what I meant using it from multiple BlocBuilder. It looks like this is totally fine.

No problem 馃槃

Using the same bloc in multiple bloc builders for different parts of the UI is perfectly normal and is how I envisioned BlocBuilder being used. I don鈥檛 think there鈥檚 anything wrong with your approach 馃憤

@felange Wow so fast hehe.l I really appreciate your help! Thank you again :D

This is a great thread and an area I have been struggling with mentally. It feels like you need a separate block for each variable you want to track globally?

Could I ask about If there is normal to use two related-bloc in single page? What I mean It's when you want to split the page(screen) up and down, and the upper page use POST request, so according to Bloc firebase example, I had three state.

  final bool isSubmitting;
  final bool isSuccess;
  final bool isFailure;

but what If I have another POST that in the bottom page? So I need to use like this?

  final bool isSubmittingUp;
  final bool isSuccessUp;
  final bool isFailureUp;
  final bool isSubmittingDown;
  final bool isSuccessDown;
  final bool isFailureDown;

So I thought this is time to use Two Bloc(um... just call It, Cat bloc[top], Dog bloc[bottom]), but there is a dependency about If Upper one not loaded correctly, the bottom page should not be able to clicked.

final bool isDogButtonValid;

Hence, Dog button's state is handled by DogBloc isDogButtonValid, and It also need to be control by isSubmitting by CatBloc. Revoke my question, Is this a normal case to use two dependency-bloc or One big bloc?

Hi @Tokenyet in general I would use a bloc per feature. I'm not sure if in this case Dog and Cat are two separate features or not so I can't really provide guidance without additional information. If you can provide a link to a sample app I would love to take a look and give suggestions for how to structure your blocs 馃憤

@felangel I do loved your library but here I think this is an issue we all got. So example, I have a MyProfileScreen who is a screen where I do show the user info at the top and the user photos at the bottom. A bit like you see in instagram. The issue I got is first I did think bloc is global, but after reading all your medium post etc I figured out I need a bloc by screen. But now look like in my case I will need 2 different bloc right ? One for fetching the current user info, and one for fetch the current user photos ? If yes great will change this, but I just do feel if you think about another tutorial, this case would be awesome to explain. Coming from react-native flutter is so great, but state management is so hard and your lib is the best one I find. Great JOB

How you will manage folder structure here for this case ? Right now I did name my bloc to the screen they attach but now with this case should I name my bloc as Feature ? Like current_user_info_bloc and current_user_photos_bloc ? And should I put this in same folder because they are related or no and keep a folder by bloc ?

Hey @EQuimper 馃憢
Thanks for the positive feedback, I really appreciate it!

Regarding your question, I'd recommend having a ProfileBloc which manages the state of the current user's profile. Then you can have a PhotosBloc which manages the state of the user's photos. The Profile widget could then use BlocBuilder with the ProfileBloc and render the profile info while the Photos widget could use BlocBuilder with the PhotosBloc and render the user's photos.

If you have additional layers of logic per application feature you can add more blocs like a FavoritePhotosBloc which has a dependency on the PhotosBloc and filters the photos etc...

Regarding folder structure, I'd structure my app based on feature with my blocs, widgets, and models in the appropriate feature directory.

I'll keep your request in mind for the next tutorial 馃槃

Hope that helps 馃憤

Hi..can you help me i am facing with issue

Issue
I started developing ecommerce application where on the home screen i want to show latest products nd offer product nd flash sale products
What i did was i have created product bloc with multiple events.

@venku111 can you please provide a link to a sample app which illustrates the issue you're having? Thanks!

Thanks for the support
Here is the Demo Link for the application https://github.com/venku111/Ohrange

Let me clearly explain the issue what I am facing

Let us Assume I am having a variety of products with different category
Now I want to show the top 12 products from each category on my home page on the slider

So, I Created bloc folder inside that I created a new folder as product bloc with events and listeners and the bloc with MultiBlocProvider On main.dart file
return MultiBlocProvider(
providers: [
BlocProvider(
create: (BuildContext context) => CategoryBloc(categoryRepository: CategoryRepository()),
),
BlocProvider(
create: (BuildContext context) => SubCategoryBloc(subCategoryRepository: SubCategoryRepository()),
),
BlocProvider(
create: (BuildContext context) => ProductBloc(productRepository: ProductsRepository()),
),
// BlocProvider(
// create: (BuildContext context) => SpecailProductsBloc(),
// ),
],

      child:MaterialApp(
            title: 'Ohrange',
            initialRoute: '/',
            onGenerateRoute: RouteGenerator.generateRoute,
            debugShowCheckedModeBanner: false,
            theme: theme,
      ),
       );

and Home screen looks like this
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}

class _HomePageState extends State {

CategoryBloc _categoryBloc;
ProductBloc _productBloc;

@override
void initState() {
final _categoryBloc = BlocProvider.of(context);
_categoryBloc.add(FetchCategoryEvent());
_productBloc = BlocProvider.of(context);
// print(_categoryBloc.add(FetchCategoryEvent()));
_productBloc.add(GetLatestProductsEvent(type: "latest_products"));
// _productBloc.add(GetTopSellingProductsEvent(type: "top_selling"));
// _productBloc.add(GetBestofferProductsEvent(type: "best_offers"));
// _productBloc.add(GetFeaturedProductsEvent(type: "featured"));

  super.initState();

}
@override
void dispose() {
super.dispose();
}

@override
Widget build(BuildContext context) {
// _categoryBloc = BlocProvider.of(context);
Widget _itemNew(String title) {
return Padding(
padding: const EdgeInsets.only(top: 15.0, bottom: 0.0),
child: Container(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(right: 20.0, left: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.subtitle
),
InkWell(
onTap: null,
child: Text("See More",maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.title),
),
],
),
),
SingleChildScrollView(
child: BlocListener(
bloc: _productBloc,
listener: (context, state) {
print(state);
},
child: BlocBuilder(
bloc: _productBloc,
builder: (context,state){
print(state);
if(state is ProductInitialState){
return CircularLoadingWidget(height: 200,);
}else if(state is ProductLoadingState){
return CircularLoadingWidget(height: 200,);
}else if(state is ProductLoadedState){
return Container(
margin: EdgeInsets.only(right: 10.0, bottom: 15.0),
height: 300.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index)=> Item(state.products[index],context),
itemCount: state.products.length,
),
);
}else if(state is ProductErrorState){
return CircularLoadingWidget(height: 500,);
}
}
),
)
)
],
),
),
);
}

return SingleChildScrollView(
    padding: EdgeInsets.symmetric(horizontal: 0, vertical: 10),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisAlignment: MainAxisAlignment.start,
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 10),
          child:PrepareCategory(),
       ),
       Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20),
          child: SearchBarWidget(),
        ),
        _itemNew("Latest Products"),
        // _itemNew("Featured"),
      ],
    ),
  );

}

Hi @venku111 I opened a pull request to refactor to have a HomeBloc which handles determining the top products as opposed to using the products and categories bloc directly in the UI. Hope that helps 馃憤

Thats great 馃憣 Thanks a lot wonderful support

My Scenario : In login page for validating login button created one streamcontroller and validating it successfully . with StreamBuilder stream am able to do that.
After user clicked on login button making api call and getting response ,based on it need to change UI state , how to access Stream property inside same widget .

Sample code :
For Enabling and disabling button :

 Widget loginButtonWidget() {
    return StreamBuilder<bool>(
        stream: loginBloc.validateLoginButton,
        builder: (context, snapshot) {
        onPressed: snapshot.hasData
                      ? () {
                          _loginButtonPressed(context);
                        }
                      : null,
}

In _loginButtonPressed event making API call ,now how to get and change states based on api response .

@XamMobileDev please avoid posting questions that are not related to the bloc library here. That question is better suited for StackOverflow or the flutter community slack 馃憤

Hi, sir i need help, im newbie

i have a UI like this
image

usually, for download i will use a bloc with 1 event and 3 states
event:
DownloadItem(),
states:
Downloading(),
DownloadSucceed()
DownloadFailed()

there is no problem if it's only for single download to manage the states
but how if i want multiple download ? i mean like how to add or dispatch this event (DownloadItem()) multiple and stream multiple states at the same time?
do i have to create different bloc for each item ?
what is better solution ?

thank you sir

Hi @fajarsiedd 馃憢
You can take a look at the complex list example for inspiration 馃憤

Hi sir, how to update the same state many times

what i want to do is

  • for looping
  • update state item to isUploading = true
  • post api then check and set if it's succes or failed
  • then update the state again

i write code like this, but the state only change on the first loop, i need to hot reload then the state changed

class ParseExcelBloc extends Bloc<CreateStudentEvent, CreateStudentState> {
  @override
  CreateStudentState get initialState => InitialCreateStudentState();

  @override
  Stream<CreateStudentState> mapEventToState(CreateStudentEvent event) async* {
    if (event is ParseExcel) {
      yield Loading();

      final parsedExcel = await ApiClient().parseExcel(event.file);

      if (parsedExcel != null) yield ExcelParsed(parsedExcel: parsedExcel, file: event.file);
      else yield ParseExcelFailed();
    }
    if (event is SaveParsedExcel) {
      final currentState = state;

      if (currentState is ExcelParsed) {
        for (var student in currentState.parsedExcel.data) {
          student.role = 'murid';
          student.rombel = event.rombelId;
          student.isUploading = true;

          yield ExcelParsed(parsedExcel: currentState.parsedExcel);

          final result = await ApiClient().createStudent(student);

          if (result) student.isSuccess = true;
          else student.isFailed = true;

          yield ExcelParsed(parsedExcel: currentState.parsedExcel);
        }
      }
    }
  }
}

You can continue to yield the same state over and over. Just don鈥檛 extend Equatable and make sure you鈥檙e yielding a new state instance.

Oh thank you sir, my problem solved 馃憤馃憤

Hi @felangel I have the same problem with yielding the same state over and over I remove the Equatable extension and it works. However my unit tests' expect has been broken. How can I use expect if I am not using Equatable. Thanks!

@afermin if you want you can use a custom matcher like:

expect: [
  TypeMatcher<MyState>().having((state) => s.property, 'property', expectedProperty);
]

@felangel Many thanks for your answer. I'm new working with flutter. I took a look on the Equatable code and I made my own Equatable abstract class which compare as long as it is running in the test Environment or if a defined attribute (that is for false for default) is override as true. What do you think about it?

Hi, I am trying to show map and details in one page. For that I am making two event call(one for map second for details) from same bloc. But, before first event call finishes, second event fires up. Because of which I am unable to capture last state of my event. I wanted to know if I can fire multiple event concurrently or not. If yes, what is the process. Please help.

@iammadhukar For calling multiple events at the same time, You can use the double dot notations when providing the bloc.
Like:

BlocProvider(
  create: (context) => SomeBloc()..add(event1())..add(Event2()),
  child: ...
);

Or the better way can be creating two blocs one for the map and another for the details. Then call the events with respect to the feature, whenever you want to.

@felangel I think there might be something more precise for it. Please have a look.
Thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Reidond picture Reidond  路  3Comments

MahdiPishguy picture MahdiPishguy  路  3Comments

zjjt picture zjjt  路  3Comments

shawnchan2014 picture shawnchan2014  路  3Comments

wheel1992 picture wheel1992  路  3Comments