A predictable state management library that helps implement the BLoC design pattern

Overview

Bloc

build codecov Star on Github style: effective dart Flutter Website Awesome Flutter Flutter Samples License: MIT Discord Bloc Library


A predictable state management library that helps implement the BLoC design pattern.

Package Pub
bloc pub package
bloc_test pub package
flutter_bloc pub package
angular_bloc pub package
hydrated_bloc pub package
replay_bloc pub package

Sponsors

Our top sponsors are shown below! [Become a Sponsor]


Try the Flutter Chat Tutorial   💬

Overview

Bloc Architecture

The goal of this library is to make it easy to separate presentation from business logic, facilitating testability and reusability.

Documentation

Migration

Examples

Dart

  • Counter - an example of how to create a CounterBloc (pure dart).

Flutter

  • Counter - an example of how to create a CounterBloc to implement the classic Flutter Counter app.
  • Form Validation - an example of how to use the bloc and flutter_bloc packages to implement form validation.
  • Bloc with Stream - an example of how to hook up a bloc to a Stream and update the UI in response to data from the Stream.
  • Complex List - an example of how to manage a list of items and asynchronously delete items one at a time using bloc and flutter_bloc.
  • Infinite List - an example of how to use the bloc and flutter_bloc packages to implement an infinite scrolling list.
  • Login Flow - an example of how to use the bloc and flutter_bloc packages to implement a Login Flow.
  • Firebase Login - an example of how to use the bloc and flutter_bloc packages to implement login via Firebase.
  • Github Search - an example of how to create a Github Search Application using the bloc and flutter_bloc packages.
  • Weather - an example of how to create a Weather Application using the bloc and flutter_bloc packages. The app uses a RefreshIndicator to implement "pull-to-refresh" as well as dynamic theming.
  • Todos - an example of how to create a Todos Application using the bloc and flutter_bloc packages.
  • Timer - an example of how to create a Timer using the bloc and flutter_bloc packages.
  • Firestore Todos - an example of how to create a Todos Application using the bloc and flutter_bloc packages that integrates with cloud firestore.
  • Shopping Cart - an example of how to create a Shopping Cart Application using the bloc and flutter_bloc packages based on flutter samples.
  • Dynamic Form - an example of how to use the bloc and flutter_bloc packages to implement a dynamic form which pulls data from a repository.
  • Wizard - an example of how to build a multi-step wizard using the bloc and flutter_bloc packages.
  • GraphQL - an example of how to use the bloc and flutter_bloc packages with graphql to retrieve data from api.graphql.jobs.
  • Fluttersaurus - an example of how to use the bloc and flutter_bloc packages to create a thesaurus app -- made for Bytconf Flutter 2020.

Web

  • Counter - an example of how to use a CounterBloc in an AngularDart app.
  • Github Search - an example of how to create a Github Search Application using the bloc and angular_bloc packages.

Flutter + Web

  • Github Search - an example of how to create a Github Search Application and share code between Flutter and AngularDart.

Articles

Books

  • Flutter Complete Reference - A book about the Dart programming language (version 2.10, with null safety support) and the Flutter framework (version 1.20). It covers the bloc package (version 6.0.3) in all flavors: bloc, flutter_bloc hydrated_bloc, replay_bloc, bloc_test and cubit.

Extensions

  • IntelliJ - extends IntelliJ/Android Studio with support for the Bloc library and provides tools for effectively creating Blocs for both Flutter and AngularDart apps.
  • VSCode - extends VSCode with support for the Bloc library and provides tools for effectively creating Blocs for both Flutter and AngularDart apps.

Community

Learn more at the following links, which have been contributed by the community.

Packages

Video Tutorials

Written Resources

Extensions

Maintainers

Comments
  • [Proposal] Replace `mapEventToState` with `on<Event>` in `Bloc`

    [Proposal] Replace `mapEventToState` with `on` in `Bloc`

    Hello everyone! 👋

    First of all, I want to thank everyone for the amazing support and community that has grown around the bloc library! 🙏💙

    Context

    This proposal aims to address 3 problems with the current mapEventToState implementation:

    1. Predictability
    2. Learning Curve and Complexity
    3. Boilerplate

    Predictability

    Due to an issue in Dart, it is not always intuitive what the value of state will be when dealing with nested async generators which emit multiple states. Even though there are ways to work around this issue, one of the core principles/goals of the bloc library is to be predictable. Therefore, the primary motivation of this proposal is to make the library as safe as possible to use and eliminate any uncertainty when it comes to the order and value of state changes.

    Learning Curve and Complexity

    Writing blocs requires an understanding of Streams and async generators. This means developers must understand how to use the async*, yield, and yield* keywords. While these concepts are covered in the documentation, they are still fairly complex and difficult for newcomers to grasp.

    Boilerplate

    When writing a bloc, developers must override mapEventToState and then handle the incoming event(s). Often times this looks something like:

    @override
    Stream<State> mapEventToState(Event event) async* {
      if (event is EventA) {
        yield* _mapEventAToState(event);
      } else if (event is EventB) {
        yield* _mapEventBToState(event);
      }
    }
    
    Stream<State> _mapEventAToState(EventA event) async* {...}
    Stream<State> _mapEventBToState(EventB event) async* {...}
    

    The important logic usually lives inside _mapEventAToState and _mapEventBToState and mapEventToState ends up mainly being setup code to handle determining which mapper to call based on the event type. It would be nice if this could be streamlined.

    Proposal 🥁

    I am proposing to remove the mapEventToState API in favor of on<Event>. This would allow developers to register event handlers by calling on<Event> where Event is the type of event being handled. on<Event> would provide a callback (Event event, Emitter<State>) {...} which would be invoked when an event of type Event is added to the bloc. Developers could then emit one or more states in response to the incoming event.

    For example, if we look at the CounterBloc for reference, the current implementation might look something like:

    abstract class CounterEvent {}
    class Increment extends CounterEvent {}
    
    class CounterBloc extends Bloc<CounterEvent, int> {
      CounterBloc() : super(0);
    
      @override
      Stream<int> mapEventToState(CounterEvent event) async* {
        if (event is Increment) {
          yield state + 1;
        }
      }
    }
    

    With the proposed changes the CounterBloc would look something like:

    abstract class CounterEvent {}
    class Increment extends CounterEvent {}
    
    class CounterBloc extends Bloc<CounterEvent, int> {
      CounterBloc() : super(0) {
        on<Increment>((event, emit) => emit(state + 1));
      }
    }
    

    If we wanted to support multiple events:

    abstract class CounterEvent {}
    class Increment extends CounterEvent {}
    class Decrement extends CounterEvent {}
    
    class CounterBloc extends Bloc<CounterEvent, int> {
      CounterBloc() : super(0) {
        on<Increment>((event, emit) => emit(state + 1));
        on<Decrement>((event, emit) => emit(state - 1));
      }
    }
    

    For more complex logic it can be refactored to look like:

    abstract class CounterEvent {}
    class Increment extends CounterEvent {}
    class Decrement extends CounterEvent {}
    
    class CounterBloc extends Bloc<CounterEvent, int> {
      CounterBloc() : super(0) {
        on<Increment>(_onIncrement);
        on<Decrement>(_onDecrement);
      }
    
      void _onIncrement(Increment event, Emitter<int> emit) {
        emit(state + 1);
      }
    
      void _onDecrement(Decrement event, Emitter<int> emit) {
        emit(state - 1);
      }
    }
    

    These changes address the predictability issues mentioned above because it can be guaranteed that the bloc's state will update immediately when emit is called ensuring that cases like this behave as expected:

      void _onIncrement(Increment event, Emitter<int> emit) {
        emit(state + 1); // state starts off at 0 so we emit 1
        emit(state + 1); // state is 1 now so we emit 2
      }
    

    In addition, developers don't have to use async generators (async*, yield, yield*) which can introduce complexity and undesired behavior in advanced cases.

    This allows developers to focus on the logic by directly registering an event handler for each type of event which streamlines the bloc code a bit further.

    An added benefit is the added consistency across Cubit and Bloc -- both trigger state changes via emit and the transition from Cubit to Bloc should become simpler.

    class CounterCubit extends Cubit<int> {
      CounterCubit() : super(0);
    
      void increment() => emit(state + 1);
    }
    

    Becomes

    abstract class CounterEvent {}
    class Increment extends CounterEvent {}
    
    class CounterBloc extends Bloc<CounterEvent, int> {
      CounterBloc() : super(0) {
        on<Increment>(_onIncrement);
      }
    
      void _onIncrement(Increment event, Emitter<int> emit) {
        emit(state + 1);
      }
    }
    

    Or as mentioned above (for simple cases)

    abstract class CounterEvent {}
    class Increment extends CounterEvent {}
    
    class CounterBloc extends Bloc<CounterEvent, int> {
      CounterBloc() : super(0) {
        on<Increment>((event, emit) => emit(state + 1));
      }
    }
    

    These changes will obviously be breaking changes that impact blocs (cubits will remain unaffected) so they would all be within the scope of a v8.0.0 release.

    The changes would be made in a way that only impacts the bloc mapEventToState implementation. The way blocs are used and tested will be 100% backward compatible which means the changes will be scoped to just the mapEventToState code and can ideally be automated via a code mod. There should be no impact to the remaining ecosystem (flutter_bloc, bloc_test, hydrated_bloc, replay_bloc, etc...).

    Please give this issue a 👍 if you support the proposal or a 👎 if you're against it. If you disagree with the proposal I would really appreciate it if you could comment with your reasoning.

    Thanks so much for all of the continued support and looking forward to hearing everyone's thoughts on the proposal! 🙏


    08/31 UPDATE

    Hey everyone, just wanted to give a quick update:

    We currently have the v8.0.0 branch which replaces mapEventToState with on<Event>; however, we were able to make on<Event> backward compatible with mapEventToState 🎉 . You can view the changes as part of the v7.2.0 branch.

    The current plan is to roll out bloc v7.2.0 in the coming days which will deprecate mapEventToState, transformEvents, and transformTransitions and will introduce the new on<Event> API. We will have a comprehensive migration guide explaining all of the changes and how to migrate over. During this time, we encourage everyone to upgrade to bloc v7.2.0 and start to migrate blocs one at a time. In the meantime, we'll be working on development releases of bloc v8.0.0.

    As part of v8.0.0 all deprecated APIs from v7.2.0 will be removed and the tentative plan is to publish a stable v8.0.0 release about a month after v7.2.0 has been release. This should give everyone some time to incrementally migrate and for any adjustments to be made. In addition, v7.x.x will still receive bug fixes for the foreseeable future so there should be no pressure/urgency to jump to v8.0.0.

    Let us know if you have any questions/concerns.

    Thanks for everyone's feedback, patience, and continued support! 💙 🙏

    09/02 UPDATE

    We just published bloc v7.2.0-dev.1 which introduces the on<Event> API and is backwards compatible which should allow you to migrate incrementally. 🎉 ✨

    bloc-v7 2 0

    Release Notes: https://github.com/felangel/bloc/releases/tag/bloc-v7.2.0-dev.1

    Please try it out and let us know if you have any feedback/questions, thanks! 🙏 💙

    09/09 UPDATE

    We just published bloc v7.2.0-dev.2 🎉 Release Notes: https://github.com/felangel/bloc/releases/tag/bloc-v7.2.0-dev.2

    09/10 UPDATE

    We just published bloc v7.2.0-dev.3 🎉 Release Notes: https://github.com/felangel/bloc/releases/tag/bloc-v7.2.0-dev.3

    09/21 UPDATE

    The time has come 🥁 🥁 🥁

    bloc v7.2.0 is now out 🎉

    📦 update now: https://pub.dev/packages/bloc/versions/7.2.0 📔 migration guide: https://bloclibrary.dev/#/migration?id=v720

    enhancement candidate discussion feedback wanted pkg:bloc 
    opened by felangel 134
  • [Feature Request] MultiBlocBuilder

    [Feature Request] MultiBlocBuilder

    Sometimes I would need to access different blocs in the same widget, and the code goes pretty messy. I am thinking maybe we can have something like MultiBlocBuilder which serve the similar purpose of MultiRepositoryProvider and MultiBlocProvider.

    enhancement candidate discussion feedback wanted 
    opened by PhilipChng 80
  • Bad state: Cannot add new events after calling close

    Bad state: Cannot add new events after calling close

    I can't trace the issue on my code, not sure it's my code problem or the library

    StateError: Bad state: Cannot add new events after calling close
      File "broadcast_stream_controller.dart", line 253, in _BroadcastStreamController.add
      File "subject.dart", line 124, in Subject._add
      File "subject.dart", line 118, in Subject.add
      File "bloc.dart", line 73, in Bloc._bindStateSubject.<fn>
      File "stream.dart", line 816, in Stream.forEach.<fn>.<fn>
      File "stream_pipe.dart", line 11, in _runUserCode
      File "stream.dart", line 816, in Stream.forEach.<fn>
      File "zone.dart", line 1132, in _rootRunUnary
      File "zone.dart", line 1029, in _CustomZone.runUnary
      File "zone.dart", line 931, in _CustomZone.runUnaryGuarded
      File "stream_impl.dart", line 336, in _BufferingStreamSubscription._sendData
      File "stream_impl.dart", line 263, in _BufferingStreamSubscription._add
      File "broadcast_stream_controller.dart", line 379, in _SyncBroadcastStreamController._sendData
      File "broadcast_stream_controller.dart", line 291, in _BroadcastStreamController._add
      File "zone.dart", line 1132, in _rootRunUnary
      File "zone.dart", line 1029, in _CustomZone.runUnary
      File "zone.dart", line 931, in _CustomZone.runUnaryGuarded
      File "stream_impl.dart", line 336, in _BufferingStreamSubscription._sendData
      File "stream_impl.dart", line 591, in _DelayedData.perform
      File "stream_impl.dart", line 707, in _StreamImplEvents.handleNext
      File "stream_impl.dart", line 667, in _PendingEvents.schedule.<fn>
      File "zone.dart", line 1120, in _rootRun
      File "zone.dart", line 1021, in _CustomZone.run
      File "zone.dart", line 923, in _CustomZone.runGuarded
      File "zone.dart", line 963, in _CustomZone.bindCallbackGuarded.<fn>
      File "zone.dart", line 1124, in _rootRun
      File "zone.dart", line 1021, in _CustomZone.run
      File "zone.dart", line 923, in _CustomZone.runGuarded
      File "zone.dart", line 963, in _CustomZone.bindCallbackGuarded.<fn>
      File "schedule_microtask.dart", line 41, in _microtaskLoop
      File "schedule_microtask.dart", line 50, in _startMicrotaskLoop
    
    question 
    opened by anderscheow 75
  • Doc request: How to use bloc across pages?

    Doc request: How to use bloc across pages?

    Let's extended counter example, where all is working well.

    Imagine I add a thir fab action that uses navigator.pushReplacement to navigate a different page.

    The destination page needs to see counter value.

    How to?

    question 
    opened by realtebo 57
  • [Discussion] Improve Bloc to Bloc/Stream Integration

    [Discussion] Improve Bloc to Bloc/Stream Integration

    Is your feature request related to a problem? Please describe. When using bloc, it is fairly common to have blocs which need to react to other blocs or Streams. The current recommended approach involves directly subscribing to the Stream within the bloc:

    class MyBloc extends Bloc {
      final OtherBloc otherBloc;
      StreamSubscription otherBlocSubscription;
    
      MyBloc(this.otherBloc) {
        otherBlocSubscription = otherBloc.listen((state) {
            // React to state changes here.
            // Add events here to trigger changes in MyBloc.
        });
      }
    
      @override
      Future<void> close() {
        otherBlocSubscription.cancel();
        return super.close();
      }
    }
    

    This is not ideal because it requires one or more subscriptions to be managed manually and introduces additional complexity which increases with the number of consumed Streams.

    Describe the solution you'd like

    UPDATED PROPOSAL: https://github.com/felangel/bloc/issues/765#issuecomment-573368016

    ~~It might be better to potentially introduce a new widget -- something like BlocSink which handles the subscription to the desired Stream and exposes callbacks which allow developers to conveniently add an event to the desired bloc.~~

    BlocSink<int, TickerBloc, TickerEvent>( // stream data type, bloc type, event type
      stream: Stream.periodic(Duration(seconds: 1), (x) => x).take(10),
      bloc: BlocProvider.of<TickerBloc>(context), // optional - will look up bloc via context by default
      onData: (tick, add) => add(Tick(tick)),
      onError: (error, add) => add(TickError(error)), // optional
      onDone: (add) => add(TickFinished()), // optional
      child: MyChild(),
    )
    

    ~~In the above example, BlocSink would subscribe to the Stream.periodic and each time data is emitted, it would add a Tick event to the TickerBloc with the current tick. If an error is emitted, it would add a TickError with the current error and once the Stream has completed, it will add a TickFinished event.~~

    enhancement candidate discussion feedback wanted pkg:bloc 
    opened by felangel 52
  • Bad state: Cannot add new events after calling close after the bloc is disposed.

    Bad state: Cannot add new events after calling close after the bloc is disposed.

    I got this error while trying a scenario:

    1. I need to fetch some data from the API.
    2. To simulate the fetching delay time, i use await Future.delayed(Duration(seconds: 2)); in the mapEventToState bloc. Then i yield some state.
    3. I dispose the bloc when the page is popped.
    4. When i press back (dispose) the page before the fetching finished (under 2 seconds), the error below happened. The apps did not crashed though.

    Seems like it is similar with #52

    E/flutter (14028): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: Bad state: Cannot add new events after calling close
    E/flutter (14028): #0      _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:249:24)
    E/flutter (14028): #1      Subject._add (package:rxdart/src/subjects/subject.dart:124:16)
    E/flutter (14028): #2      Subject.add (package:rxdart/src/subjects/subject.dart:118:5)
    E/flutter (14028): #3      Bloc._bindStateSubject.<anonymous closure> (package:bloc/src/bloc.dart:86:23)
    E/flutter (14028): #4      Stream.forEach.<anonymous closure>.<anonymous closure> (dart:async/stream.dart:814:45)
    E/flutter (14028): #5      _runUserCode (dart:async/stream_pipe.dart:11:23)
    E/flutter (14028): #6      Stream.forEach.<anonymous closure> (dart:async/stream.dart:814:11)
    E/flutter (14028): #7      _rootRunUnary (dart:async/zone.dart:1132:38)
    E/flutter (14028): #8      _CustomZone.runUnary (dart:async/zone.dart:1029:19)
    E/flutter (14028): #9      _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
    E/flutter (14028): #10     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
    E/flutter (14028): #11     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:263:7)
    E/flutter (14028): #12     _SyncBroadcastStreamController._sendData (dart:async/broadcast_stream_controller.dart:375:20)
    E/flutter (14028): #13     _BroadcastStreamController._add (dart:async/broadcast_stream_controller.dart:287:5)
    E/flutter (14028): #14     _rootRunUnary (dart:async/zone.dart:1132:38)
    E/flutter (14028): #15     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
    E/flutter (14028): #16     _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
    E/flutter (14028): #17     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
    E/flutter (14028): #18     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:263:7)
    E/flutter (14028): #19     _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:132:11)
    E/flutter (14028): #20     _ForwardingStream._handleData (dart:async/stream_pipe.dart:98:10)
    E/flutter (14028): #21     _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:164:13)
    E/flutter (14028): #22     _rootRunUnary (dart:async/zone.dart:1132:38)
    E/flutter (14028): #23     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
    E/flutter (14028): #24     _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
    E/flutter (14028): #25     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
    E/flutter (14028): #26     _DelayedData.perform (dart:async/stream_impl.dart:591:14)
    E/flutter (14028): #27     _StreamImplEvents.handleNext (dart:async/stream_impl.dart:707:11)
    E/flutter (14028): #28     _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:667:7)
    E/flutter (14028): #29     _rootRun (dart:async/zone.dart:1120:38)
    E/flutter (14028): #30     _CustomZone.run (dart:async/zone.dart:1021:19)
    E/flutter (14028): #31     _CustomZone.runGuarded (dart:async/zone.dart:923:7)
    E/flutter (14028): #32     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:963:23)
    E/flutter (14028): #33     _rootRun (dart:async/zone.dart:1124:13)
    E/flutter (14028): #34     _CustomZone.run (dart:async/zone.dart:1021:19)
    E/flutter (14028): #35     _CustomZone.runGuarded (dart:async/zone.dart:923:7)
    E/flutter (14028): #36     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:963:23)
    E/flutter (14028): #37     _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
    E/flutter (14028): #38     _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
    E/flutter (14028): 
    
    question 
    opened by haqqi 47
  • [Proposal?] 'Stream<State> get state' is confusing

    [Proposal?] 'Stream get state' is confusing

    I've been developing a relatively large app using Bloc and I think my top 1 issue with Bloc is with state. No, not what you are thinking. I mean, the state variable/getter method. image

    It has happened to me once, then again, then again, then today. I always cast it, like (currentState as ContrastLoadedState) and I keep trying state before realising my mistake. Just check the screenshot above. It makes no sense for me that currentState returns a State while state returns a Stream<State>.

    In BlocBuilder you get a state: https://github.com/felangel/bloc/blob/805cf865fe0972e7f1bd24f08598d624066434fb/examples/flutter_shopping_cart/lib/catalog/my_catalog.dart#L52

    In your own sample you use state = currentState: https://github.com/felangel/bloc/blob/805cf865fe0972e7f1bd24f08598d624066434fb/examples/flutter_shopping_cart/lib/cart/bloc/cart_bloc.dart#L31

    I think you could change state to stateStream and currentState to state or maybe just rename state to stateStream, that way there wouldn't be a state and IntelliJ/VSCode would be smart enough to just suggest currentState.

    I'm not sure if this has been a source of bugs/confusion in the past, but it has been a great frustration for me.

    enhancement deprecation 
    opened by bernaferrari 43
  • Migrate to bloc v7.2.0 question and log issue

    Migrate to bloc v7.2.0 question and log issue

    Hi @felangel first of all, thank you for providing such a great package for managing state in Flutter. I like it a lot! I have two questions. First, Since I am trying to migrate my bloc to bloc v7.2.0. I am not sure if this is the correct way. Since I am using Freezed to generate state and event. Here are my old and new migrated codes.

    Event:

    
    @freezed
    class ImagesEvent with _$ImagesEvent {
      const factory ImagesEvent.imagesFetched() =
          _ImagesFetched;
      const factory ImagesEvent.imagesFetchedNextPage() =
          _ImagesFetchedNextPage;
    }
    
    

    State:

    
    @freezed
    class ImagesState with _$ImagesState {
      const factory ImagesState.initial() = _Initial;
      const factory ImagesState.isLoading() = _IsLoading;
      const factory ImagesState.success() = _Success;
      const factory ImagesState.failure() = _Failure;
    }
    
    

    Old Bloc:

    
    @override
      Stream<ImagesState> mapEventToState(
        ImagesEvent event,
      ) async* {
        yield* event.map(
          imagesFetched: (e) async* {
            yield ImagesState.isLoading(
              );
    
            final failureOrSuccess =
                await repository.getImages();
    
            yield failureOrSuccess.fold(
              (l) => ImagesState.failure(),
              (r) => ImagesState.success(),
            );
          },
          imagesFetchedNextPage: (e) async* {
            yield ImagesState.isLoading(
              );
    
            final failureOrSuccess =
                await repository.getImages();
    
            yield failureOrSuccess.fold(
              (l) => ImagesState.failure(),
              (r) => ImagesState.success(),
            );
          },
        );
      }
    
    

    Migrated Bloc:

    class ImagesBloc
        extends Bloc<ImagesEvent, ImagesState> {
    
      ImagesBloc()
          : super(_Initial()) {
        on<ImagesEvent>((event, emit) async {
          await event.map(
            imagesFetched: (_) async {
    
                emit( ImagesState.isLoading());
    
                final failureOrSuccess =
                    await repository.getImages();
    
                emit(
                  failureOrSuccess.fold(
                    (l) => ImagesState.failure(),
                    (r) => ImagesState.success(),
                  ),
                );
            },
            imagesFetchedNextPage: (_) async {
    
                emit(ImagesState.isLoading());
    
                final failureOrSuccess =
                    await repository.getImages();
    
                emit(
                  failureOrSuccess.fold(
                    (l) => ImagesState.failure(),
                    (r) => ImagesState.success(),
                  ),
                );
            });
        });
      }
    }
    
    

    Second is, initially when I am not providing async after on<ImagesEvent> and before event.map() like below. I get an error log from runZonedGuarded (also below). It seems to have a /n new line error. The new line is not showing correctly. The upper part (blue text) is the error message that I manually print out.

    Another is I already add the async in front of imagesFetched: (_) async {} do I have to add async to the on<ImagesEvent> and event.map() ?

    
        on<ImagesEvent>((event, emit) /*async*/ {
          /*async*/ event.map(
            imagesFetched: (_) async {
    
                emit( ImagesState.isLoading());
    
                final failureOrSuccess =
                    await repository.getImages();
    
                emit(
                  failureOrSuccess.fold(
                    (l) => ImagesState.failure(),
                    (r) => ImagesState.success(),
                  ),
                );
            },
            //....
    }
    

    Error Message: bloc_issue

    Thanks a lot!

    question 
    opened by AscentionOne 41
  • How to dispatch an event many times

    How to dispatch an event many times

    Hi there, first of all let me thank you for the awesome libraby. I face a problem with my block. I cannot send an event multiple times. It seems that mapEventToState is an asynchronous function, so currently can be equal to the state, so the data cannot be returned. You can give me a solution. Thanks very much. Sorry, my english is bad.

    question 
    opened by hahai96 40
  • How to show/hide Dialog when state changed?

    How to show/hide Dialog when state changed?

    Hello I want to know how to show/hide Dialog when state changed. For example in a bloc that have state isLoading i want to show a Dialog, and when the state isNotLoading i want to hide this.

    But I read for hide dialog i need to use Navigator.pop(), so when I used that the Navigator pop the screen

    I am trying with something like this:

    bloc.state.listen(
      (state) {
        if(state.isLoading){
          showDialog(
            // ...
          )        
        }else{
          // Here I want to hide the dialog
          // This Pop the current page :(
         Navigator.of(context).pop() 
        }
      }
    }
    

    Also, I implement using a Container with Stack, but If I put in the body of Scafold, the Container with the loading widget not overlap the AppBar. so for overlap the AppBar i need to put this in a Column in the body of the Scaffold.

    I don't like this form, so i want to find how to use showDialog or maybe other solution.

    thanks,

    question 
    opened by basketball-ico 36
  • [Proposal] Allow blocs to finish processing pending events on close

    [Proposal] Allow blocs to finish processing pending events on close

    Is your feature request related to a problem? Please describe. Currently, if close is called on a bloc while an event is being processed, the subsequent state changes (transitions) will be ignored.

    For example, given the following bloc:

    class AsyncBloc extends Bloc<AsyncEvent, AsyncState> {
      @override
      AsyncState get initialState => AsyncState.initial();
    
      @override
      Stream<AsyncState> mapEventToState(AsyncEvent event) async* {
        yield state.copyWith(isLoading: true);
        await Future<void>.delayed(Duration(milliseconds: 500));
        yield state.copyWith(isLoading: false, isSuccess: true);
      }
    }
    

    The current behavior is:

    test('close while events are pending does not emit new states', () {
        final expectedStates = <AsyncState>[
            AsyncState.initial(),
        ];
        final states = <AsyncState>[];
        asyncBloc.listen(states.add);
    
        asyncBloc.add(AsyncEvent());
        asyncBloc.close();
        expect(states, expectedStates);
    });
    

    Describe the solution you'd like It might be better to modify close to allow for any pending events to be processed to completion. This would help with testing and would also allow developers to know when a bloc has been closed as the process is async.

    test('close while events are pending finishes processing pending events', () async {
        final expectedStates = <AsyncState>[
            AsyncState.initial(),
            AsyncState.initial().copyWith(isLoading: true),
            AsyncState.initial().copyWith(isSuccess: true),
        ];
        final states = <AsyncState>[];
        asyncBloc.listen(states.add);
    
        asyncBloc.add(AsyncEvent());
        await asyncBloc.close();
        expect(states, expectedStates);
    });
    

    See #611 for more more context and #638 for a draft.

    enhancement 
    opened by felangel 35
  • Bloc state data retaining for all pages

    Bloc state data retaining for all pages

    @felangel Please Help. Hello Team, I am new to bloc architecture. I want to achieve this while pushing the same screen again and again in the navigation stack. Each page has a list of different data. Currently what is happening topmost screen data is showing on all previous screens while popping. I want to achieve this with the bloc. Please guide me here Thanks.

    question waiting for response 
    opened by rahuloexe 3
  • refactor(flutter_bloc): remove unnecessary single child widget mixins

    refactor(flutter_bloc): remove unnecessary single child widget mixins

    Status

    HOLD

    Breaking Changes

    NO

    Description

    • refactor(flutter_bloc): remove unnecessary single child widget mixins (closes #3446)

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] 🛠️ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [X] 🧹 Code refactor
    • [ ] ✅ Build configuration change
    • [ ] 📝 Documentation
    • [ ] 🗑️ Chore
    pkg:flutter_bloc refactor 
    opened by felangel 1
  • Only one event fired (or considered). when adding event to Two different Bloc

    Only one event fired (or considered). when adding event to Two different Bloc

    Description I have two Bloc that I send events to on tapping on a widget. However only one of the two blocs respond to the event.

    Here is the code(snippet): GestureDetector onTap: () { BlocProvider.of< ShowAudioControlCubit>( context) [hideAudioControl(); BlocProvider.of< AudioPlayerActionBloc> ( context) add (PauseSoundEvent ( ) ) ; }, child: Icon( Icons. close, sizes 34, ), // Icon // GestureDetector 1)

    Screenshot 2022-12-17 at 00 11 24

    Is there something I can do to fix this issue?

    question waiting for response 
    opened by Devlonoah 1
  • chore(hydrated_bloc): usage questions

    chore(hydrated_bloc): usage questions

    Description

    Hello everybody!

    I hope you are all well!

    I already apologize before, in case I come to ask something silly, I'm still a beginner in development with Flutter.

    I would like to know more details about the use of hydrated_bloc and if it would be useful for the case in which I have a certain challenge.

    The case is as follows: We are currently developing an application that needs information from the logged in user at different times. We thought of using some internal storage process such as shared preferences (or other), and in each block that the authentication data was used (token, user id), we would have a usecase that would carry out the process of retrieving the authentication data. In another project we made this flow, but it really was a very verbose process to carry out.

    In another project we used a session concept, where this type of internal storage was not available and its use was made with a singleton class that exposed the authentication data, but what was the problem with this approach? When the user fully closed the app, the singleton was cleared and ended up giving null authentication data when the user stops using the app.

    A friend introduced me to the site bloclibrary.dev, and browsing it I ended up finding hydrated_bloc, and I believe that with it I could solve this case. In question what would it be? I would have a block responsible for carrying out the authentication processes and in this case, the hydrated_bloc would be used to store the authenticated user, then the questions would be the following:

    1 - in another block that needed to use some information from the authentication data, would it be possible to use what is stored in the block that uses hydrated_bloc? 2 - would it be better to use another resource in this case? 3 - would there be a smarter way to do this?

    Again, I apologize if I said anything wrong and thank you very much for your help.

    chore 
    opened by ffeliciodeveloper 1
  • fix: How to use bloc with Navigator or routes in general

    fix: How to use bloc with Navigator or routes in general

    Description Hello, in my app im using multiple blocs and Im trying to figure out how to handle blocs with navigation. I have a button on my RoundScreen which fires an action which is executed and should logout a user and show a SplashScreen, but the UI doesn't prompt me to it, it only does the function in my logout event removing tokens and stuff like this.

    First of all I tried to add Blocprovider.values when im calling Navigator.push, but the UI won't update. Second I tried to implement AppRouter, Im following the documentation integrating AppRouter to my app. This seems to be the right way and it is actually working, but in the Example I need to manually dispose my bloc which I dind't figure out yet, since my bloc has arguments to pass which needs a context which I don't have in my AppRouter class. When Im not able to dispose it im getting some errors here in my app because the blocs arent disposed. But in this way I already get the user to the SplashScreen.

    Reading further on there is another example showing how to make the blocs accassable globally. I already do have this implemented, but it's telling me that there is no bloc in scope if im removing the Blocprovider.value and don't use the AppRouter. I hope you can assist me on what im missing and help me understanding how to work with my usecase. Furthermore I want to understand why the globally approach isn't working and I want to understand why the approach with routing works and the Navigator.push doesnt. Also I ofcourse want to know the best practise on this usecase and which of the 3 ways is the right one. :)

    Steps To Reproduce This is my main.dart at the moment:

      @override
      Widget build(BuildContext context) {
        return MultiRepositoryProvider(
          providers: [
            RepositoryProvider(create: (context) => UserRepository()),
            RepositoryProvider(create: (context) => SocketRepository()),
            RepositoryProvider(create: (context) => SecureStorageRepository()),
            RepositoryProvider(create: (context) => DatabaseRepository()),
            RepositoryProvider(create: (context) => EventRepository()),
            RepositoryProvider(create: (context) => AgoraRepository()),
          ],
          child: MultiBlocProvider(
                    providers: [
                      BlocProvider<AuthBloc>(
                        create: (context) => AuthBloc(
                          RepositoryProvider.of<UserRepository>(context),
                          RepositoryProvider.of<SocketRepository>(context),
                          RepositoryProvider.of<SecureStorageRepository>(context),
                        )..add(AppStarted()),
                      ),
                      BlocProvider<ShopBloc>(
                          create: (context) => ShopBloc(
                            RepositoryProvider.of<DatabaseRepository>(context),
                            RepositoryProvider.of<EventRepository>(context),
                          )),
                      BlocProvider<LiveeventsBloc>(
                          create: (context) => LiveeventsBloc(
                            RepositoryProvider.of<EventRepository>(context),
                          RepositoryProvider.of<SocketRepository>(context),
                          )),
                      BlocProvider<NavigationCubit>(
                          create: (context) => NavigationCubit()),
                      BlocProvider<EventTabCubit>(
                          create: (context) => EventTabCubit()),
                          BlocProvider<EventButtonCubit>(
                          create: (context) => EventButtonCubit()),
                          BlocProvider<ReportCubit>(
                          create: (context) => ReportCubit(RepositoryProvider.of<
                                                                SecureStorageRepository>(
                                                            context),)),
                    ],
                    child: OverlaySupport.global(
              child: MaterialApp(
                  title: 'bae',
                  onGenerateRoute: _router.onGenerateRoute,
                  debugShowCheckedModeBanner: false,
                  theme: FMThemeData.fmThemeData,
                //  home: const AppWrapper(),
                  ))),
        );
      }
    
      @override
      void dispose() {
        _router.dispose();
        super.dispose();
      }
    

    And this is my Approuter right now:

    class AppRouter {
    
      Route? onGenerateRoute(RouteSettings settings) {
        switch (settings.name) {
          case '/':
            return MaterialPageRoute(
              builder: (_) => const AppWrapper(),
            );
    
          case '/eventround':
            return MaterialPageRoute(
              builder: (_) => BlocProvider<LivevideoCubit>(
                create: (context) => LivevideoCubit(),
                child: BlocProvider<LiveeventsBloc>(
                  create: (context) => LiveeventsBloc(
                    RepositoryProvider.of<EventRepository>(_),
                    RepositoryProvider.of<SocketRepository>(_),
                  ),
                  child: BlocProvider<VideocallBloc>(
                    create: (context) => VideocallBloc(
                      RepositoryProvider.of<AgoraRepository>(_),
                      RepositoryProvider.of<SecureStorageRepository>(_),
                    )..add(JoinTestEvent()),
                    child: EventRound(
                      '',
                      DateTime.now(),
                      DateTime.now(),
                      0,
                      'https://images.pexels.com/photos/774909/pexels-photo-774909.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2',
                      0,
                      '',
                      0,
                      '',
                    ),
                  ),
                ),
              ),
            );
          //   )
    
          default:
            return null;
        }
      }
    
      void dispose() {
        // How to dispose here?
      }
    }
    

    Im not able to dispose one of my blocs since compared to the documentation they have arguments which needs to be passed with a context which i dont have. For example:

    class LiveeventsBloc extends Bloc<LiveeventsEvent, LiveeventsState> {
      final EventRepository _eventRepository;
      final SocketRepository _socketRepository;
    late final StreamSubscription eventStatusSubscription;
      LiveeventsBloc(this._eventRepository, this._socketRepository)
          : super(LiveeventsInitial()) {
    ...
    

    The Appwrapper checks if there is a valid login and looks like this:

    class AppWrapper extends StatelessWidget {
      const AppWrapper({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return BlocBuilder<AuthBloc, AuthState>(builder: (context, state) {
          if (state is UninitializedState) {
            // Handle the initial state when the app isn't fully started.
            return const Scaffold(body: Center(child: CircularProgressIndicator()));
          } else if (state is AuthenticateLoadingState) {
            // We are loading something, show spinner since it can take some time
            return const Scaffold(body: Center(child: CircularProgressIndicator()));
          } else if (state is UnauthenticatedState) {
            // User is not authenticated, show the splash/start screen
            return const SplashScreen();
          } else if (state is ErrorAuthenticateState) {
            return SplashScreen(err: state.err);
          }
          else if (state is AuthenticatedState) {
            return const RootScreen();
          }
    
          // Default screen
          return const SplashScreen();
        });
      }
    }
    

    If there is a valid login we are prompted to the RootScreen, if not we are having the SplashScreen.

    Now for testing purposes I have a onPressed Button function at my RootScreen navigating to a new Screen called Round:

    ...
      onPressed: () => {
                            
                                     Navigator.of(context).pushNamed('/eventround')
                                     
                                          },
    ...
    

    Before using the pushnamed I was doing it the way described in the beginning, which didn't work, it brought me to the Round, but when I fired my event there which should logout the user it did the functionality but the UI wasnt updating prompting me to the SplashScreen. At this time in my main.darti didnt have any AppRouter setup and was showing the AppWrapper under home parameter in myapp. This was the part I had in my Rootscreen which is now the Navigator.of(context).pushNamed('/eventround'):

    ...
    Navigator.push(
                                          context,
                                          MaterialPageRoute(
                                              builder: (context) => BlocProvider.value(
                                                    value:
                                                        LiveeventsBloc(
                                                      RepositoryProvider.of<
                                                          EventRepository>(context),
                                                      RepositoryProvider.of<
                                                              SocketRepository>(
                                                          context),
                                                    )..add(LoadLiveeventData(
                                                            eventId:
                                                                eventbuttonstate
                                                                    .eventId!)),
                                                    child:
                                                        BlocProvider<ReportCubit>.value(
                                                      value:
                                                          ReportCubit(
                                                        RepositoryProvider.of<
                                                                SecureStorageRepository>(
                                                            context),
                                                      ),
                                                      child: EventRound(
                                                          
                                                    ),
                                                  )),
                                        ),
    ...
    

    When im putting the same logic with a button on any another place in my app where im not navigating, it works perfectly fine prompting the user to the splashscreen, which made me focus on the navigating part which occurs the error. So could you please help me understand what I'm missing here and which of the 3 ways is the right one and how to set it up?

    Thanks in advance

    bug 
    opened by md186 1
  • Sharing blocs

    Sharing blocs

    Hi thanks for the great library.

    I got a question related to sharing code when using bloc.

    Let's imagine there is an app for desktop and mobile which got a feature. On mobile the feature is very basic. On desktop the same feature is more complex with additional functionality. That said what is the correct path to go from here. Either implementing 1 platform independent Bloc or 2 platform dependent Blocs. Should Blocs be platform independent or not in general ?

    documentation 
    opened by jtdLab 0
Releases(intellij_bloc-v3.4.0)
Owner
Felix Angelov
software engineer by day, software engineer by night.
Felix Angelov
MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.

Language: English | Português | Chinese mobx.dart MobX for the Dart language. Supercharge the state-management in your Dart apps with Transparent Func

MobX 2.2k Dec 27, 2022
Another state management solution

VxState VxState is a state management library built for Flutter apps with focus on simplicity. It is inspired by StoreKeeper & libraries like Redux, V

Pawan Kumar 42 Dec 24, 2022
A flutter boilerplate project with GetX state management.

flutter_getx_boilerplate Languages: English (this file), Chinese. Introduction During my study of Flutter, I have been using the flutter_bloc state ma

Kevin Zhang 234 Jan 5, 2023
Flutter MVU architecture/state management package

mvu_flutter No mutability. No builders. No connectors. No reducers. No StreamControllers and subscription management. A truly declarative state manage

Yakov Karpov 7 Jul 29, 2021
Simple global state management for Flutter

Slices Slices is a minimalist state manegement, focused specifically for applications that needs a global state where different "pieces" of the applic

Erick 5 Jun 15, 2021
The modular state management solution for flutter.

The modular state management solution for flutter. Easy debugging : each event is predictable and goes into a single pipeline Centralized state : soli

Aloïs Deniel 44 Jul 6, 2022
London App Brewery State Management Project

todey_flutter A new Flutter application. Getting Started This project is a starting point for a Flutter application. A few resources to get you starte

null 0 Nov 1, 2021
Flutter State Management with provider :rocket:

Flutter - Gerenciamento de Estados com Provider Objetivos ao completar os estudos Aprenda a gerenciar o estado da sua aplicação com Single Source of T

Tiago Barbosa 0 Dec 6, 2021
A simple way to access state while robust and testable.

A state-management library that: catches programming errors at compile time rather than at runtime removes nesting for listening/combining objects ens

Remi Rousselet 3.9k Jan 3, 2023
A lightweight, yet powerful way to bind your application state with your business logic.

binder A lightweight, yet powerful way to bind your application state with your business logic. The vision As other state management pattern, binder a

Romain Rastel 172 Nov 16, 2022
Manage the state of your widgets using imperative programming concepts.

Imperative Flutter Manage the state of your widgets using imperative programming concepts. Setup Intall imperative_flutter package in pubspec.yaml dep

Jeovane Santos 5 Aug 20, 2022
Timer based on provider state manager

timer_provider Timer based on provider state manager Getting Started This project is a starting point for a Flutter application. A few resources to ge

Елизавета Лободина 0 Nov 6, 2021
Trip management mobile Application

HereYouGO Trip management mobile Application This app will help you Track your expense during your trips. Track your trip destinations and the sub tri

Krupali Mehta 4 Jul 7, 2022
Petrus Nguyễn Thái Học 193 Dec 29, 2022
Flutter bloc example - An app for State management using BLoC pattern in Flutter

Flutter BLoC My first app for State management using BLoC pattern in Flutter Col

Harshil Patel 1 Jun 16, 2022
(Complete flutter application) Exam and training app as social media, prepared with Firebase backend services, Bloc State management, Singleton design pattern, Unit and widget tests, firebase mocking, Custom local libraries, etc.

(Complete flutter application) Exam and training app as social media, prepared with Firebase backend services, Bloc State management, Singleton design pattern, Unit and widget tests, firebase mocking, Custom local libraries, etc.

Ismael Shakverdiev 45 Jul 14, 2022
Flutter Login Page Bloc Pattern App Flutter Login Page Bloc Pattern App

gdgbloc A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if this

Pawan Kumar 99 Oct 20, 2022
An extension to the bloc state management library which automatically persists and restores bloc states.

⚠️ Attention: This repository has been moved to https://github.com/felangel/bloc and is now read-only! An extension to the bloc state management libra

Felix Angelov 189 Nov 17, 2022
App HTTP Client is a wrapper around the HTTP library Dio to make network requests and error handling simpler, more predictable, and less verbose.

App HTTP Client App HTTP Client is a wrapper around the HTTP library Dio to make network requests and error handling simpler, more predictable, and le

Joanna May 44 Nov 1, 2022
A Dart dependency injection library aimed to be flexible, predictable and easy to use.

dino Dino is a Dart dependency injection library with optional code generation. It was inspired by DI in .NET and aimed to be flexible, predictable an

null 3 Dec 20, 2022