Flutter Bloc Package

Pub Build Status codecov Flutter.io Awesome Flutter Flutter Samples Star on GitHub Gitter License: MIT


A Flutter package that helps implement the BLoC pattern.

This package is built to work with bloc.

Bloc Widgets

BlocBuilder is a Flutter widget which requires a Bloc and a builder function. BlocBuilder handles building the widget in response to new states. BlocBuilder is very similar to StreamBuilder but has a more simple API to reduce the amount of boilerplate code needed. The builder function will potentially be called many times and should be a pure function that returns a widget in response to the state.

See BlocListener if you want to "do" anything in response to state changes such as navigation, showing a dialog, etc...

BlocBuilder(
  bloc: BlocA(),
  builder: (context, state) {
    // return widget here based on BlocA's state
  }
)

If you want fine-grained control over when the builder function is called you can provide an optional condition to BlocBuilder. The condition takes the previous bloc state and current bloc state and returns a boolean. If condition returns true, builder will be called with currentState and the widget will rebuild. If condition returns false, builder will not be called with currentState and no rebuild will occur.

BlocBuilder(
  bloc: BlocA(),
  condition: (previousState, currentState) {
    // return true/false to determine whether or not
    // to rebuild the widget with currentState
  },
  builder: (context, state) {
    // return widget here based on BlocA's state
  }
)

BlocProvider is a Flutter widget which provides a bloc to its children via BlocProvider.of<T>(context). It is used as a DI widget so that a single instance of a bloc can be provided to multiple widgets within a subtree. By default, BlocProvider automatically disposes the provided bloc when the BlocProvider widget is disposed. In a few edge cases, such as when using BlocProvider to provide an existing bloc to another route, it might be necessary to prevent automatic disposal of the bloc. In those cases, the dispose property can be set to false.

// Automatically disposes the instance of BlocA
// Recommended usage for most cases.
BlocProvider(
  builder: (BuildContext context) => BlocA(),
  child: ChildA(),
);

// Does not automatically dispose the instance of BlocA.
BlocProvider(
  builder: (BuildContext context) => BlocA(),
  dispose: false,
  child: ChildA(),
);

then from ChildA we can retrieve BlocA with:

BlocProvider.of<BlocA>(context)

BlocProviderTree is a Flutter widget that merges multiple BlocProvider widgets into one. BlocProviderTree improves the readability and eliminates the need to nest multiple BlocProviders. By using BlocProviderTree we can go from:

BlocProvider<BlocA>(
  builder: (BuildContext context) => BlocA(),
  child: BlocProvider<BlocB>(
    builder: (BuildContext context) => BlocB(),
    child: BlocProvider<BlocC>(
      builder: (BuildContext context) => BlocC(),
      child: ChildA(),
    )
  )
)

to:

BlocProviderTree(
  blocProviders: [
    BlocProvider<BlocA>(
      builder: (BuildContext context) => BlocA(),
    ),
    BlocProvider<BlocB>(
      builder: (BuildContext context) => BlocB(),
    ),
    BlocProvider<BlocC>(
      builder: (BuildContext context) => BlocC(),
    ),
  ],
  child: ChildA(),
)

BlocListener is a Flutter widget which takes a Bloc and a BlocWidgetListener and invokes the listener in response to state changes in the bloc. It should be used for functionality that needs to occur once per state change such as navigation, showing a SnackBar, showing a Dialog, etc...

listener is only called once for each state change (including initialState) unlike builder in BlocBuilder and is a void function.

BlocListener(
  bloc: _bloc,
  listener: (context, state) {
    if (state is Success) {
      Navigator.of(context).pushNamed('/details');
    }
  },
  child: BlocBuilder(
    bloc: _bloc,
    builder: (context, state) {
      if (state is Initial) {
        return Text('Press the Button');
      }
      if (state is Loading) {
        return CircularProgressIndicator();
      }
      if (state is Success) {
        return Text('Success');
      }
      if (state is Failure) {
        return Text('Failure');
      }
    },
  }
)

BlocListenerTree is a Flutter widget that merges multiple BlocListener widgets into one. BlocListenerTree improves the readability and eliminates the need to nest multiple BlocListeners. By using BlocListenerTree we can go from:

BlocListener<BlocAEvent, BlocAState>(
  bloc: BlocA(),
  listener: (BuildContext context, BlocAState state) {},
  child: BlocListener<BlocBEvent, BlocBState>(
    bloc: BlocB(),
    listener: (BuildContext context, BlocBState state) {},
    child: BlocListener<BlocCEvent, BlocCState>(
      bloc: BlocC(),
      listener: (BuildContext context, BlocCState state) {},
      child: ChildA(),
    ),
  ),
)

to:

BlocListenerTree(
  blocListeners: [
    BlocListener<BlocAEvent, BlocAState>(
      bloc: BlocA(),
      listener: (BuildContext context, BlocAState state) {},
    ),
    BlocListener<BlocBEvent, BlocBState>(
      bloc: BlocB(),
      listener: (BuildContext context, BlocBState state) {},
    ),
    BlocListener<BlocCEvent, BlocCState>(
      bloc: BlocC(),
      listener: (BuildContext context, BlocCState state) {},
    ),
  ],
  child: ChildA(),
)

Usage

Lets take a look at how to use BlocBuilder to hook up a CounterPage widget to a CounterBloc.

counter_bloc.dart

enum CounterEvent { increment, decrement }

class CounterBloc extends Bloc<CounterEvent, int> {
  @override
  int get initialState => 0;

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.decrement:
        yield currentState - 1;
        break;
      case CounterEvent.increment:
        yield currentState + 1;
        break;
    }
  }
}

counter_page.dart

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final CounterBloc _counterBloc = BlocProvider.of<CounterBloc>(context);

    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: BlocBuilder<CounterEvent, int>(
        bloc: _counterBloc,
        builder: (BuildContext context, int count) {
          return Center(
            child: Text(
              '$count',
              style: TextStyle(fontSize: 24.0),
            ),
          );
        },
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () {
                _counterBloc.dispatch(CounterEvent.increment);
              },
            ),
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.remove),
              onPressed: () {
                _counterBloc.dispatch(CounterEvent.decrement);
              },
            ),
          ),
        ],
      ),
    );
  }
}

At this point we have successfully separated our presentational layer from our business logic layer. Notice that the CounterPage widget knows nothing about what happens when a user taps the buttons. The widget simply tells the CounterBloc that the user has pressed either the increment or decrement button.

Dart Versions

  • Dart 2: >= 2.0.0

Examples

  • 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.
  • 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.

Maintainers

Libraries

flutter_bloc