flutter_bloc 2.0.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 100
Flutter Bloc Package

Pub Build Status codecov style: effective dart 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...

If the bloc parameter is omitted, BlocBuilder will automatically perform a lookup using BlocProvider and the current BuildContext.

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

Only specify the bloc if you wish to provide a bloc that will be scoped to a single widget and isn't accessible via a parent BlocProvider and the current BuildContext.

BlocBuilder<BlocA, BlocAState>(
  bloc: blocA, // provide the local bloc instance
  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 state and the widget will rebuild. If condition returns false, builder will not be called with state and no rebuild will occur.

BlocBuilder<BlocA, BlocAState>(
  condition: (previousState, state) {
    // return true/false to determine whether or not
    // to rebuild the widget with state
  },
  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 dependency injection (DI) widget so that a single instance of a bloc can be provided to multiple widgets within a subtree.

In most cases, BlocProvider should be used to build new blocs which will be made available to the rest of the subtree. In this case, since BlocProvider is responsible for creating the bloc, it will automatically handle closing the bloc.

BlocProvider(
  builder: (BuildContext context) => BlocA(),
  child: ChildA(),
);

In some cases, BlocProvider can be used to provide an existing bloc to a new portion of the widget tree. This will be most commonly used when an existing bloc needs to be made available to a new route. In this case, BlocProvider will not automatically close the bloc since it did not create it.

BlocProvider.value(
  value: BlocProvider.of<BlocA>(context),
  child: ScreenA(),
);

then from either ChildA, or ScreenA we can retrieve BlocA with:

BlocProvider.of<BlocA>(context)

MultiBlocProvider is a Flutter widget that merges multiple BlocProvider widgets into one. MultiBlocProvider improves the readability and eliminates the need to nest multiple BlocProviders. By using MultiBlocProvider 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:

MultiBlocProvider(
  providers: [
    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 BlocWidgetListener and an optional Bloc 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 (NOT including initialState) unlike builder in BlocBuilder and is a void function.

If the bloc parameter is omitted, BlocListener will automatically perform a lookup using BlocProvider and the current BuildContext.

BlocListener<BlocA, BlocAState>(
  listener: (context, state) {
    // do stuff here based on BlocA's state
  },
  child: Container(),
)

Only specify the bloc if you wish to provide a bloc that is otherwise not accessible via BlocProvider and the current BuildContext.

BlocListener<BlocA, BlocAState>(
  bloc: blocA,
  listener: (context, state) {
    // do stuff here based on BlocA's state
  }
)

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

BlocListener<BlocA, BlocAState>(
  condition: (previousState, state) {
    // return true/false to determine whether or not
    // to call listener with state
  },
  listener: (context, state) {
    // do stuff here based on BlocA's state
  }
  child: Container(),
)

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

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

to:

MultiBlocListener(
  listeners: [
    BlocListener<BlocA, BlocAState>(
      listener: (context, state) {},
    ),
    BlocListener<BlocB, BlocBState>(
      listener: (context, state) {},
    ),
    BlocListener<BlocC, BlocCState>(
      listener: (context, state) {},
    ),
  ],
  child: ChildA(),
)

RepositoryProvider is a Flutter widget which provides a repository to its children via RepositoryProvider.of<T>(context). It is used as a dependency injection (DI) widget so that a single instance of a repository can be provided to multiple widgets within a subtree. BlocProvider should be used to provide blocs whereas RepositoryProvider should only be used for repositories.

RepositoryProvider(
  builder: (context) => RepositoryA(),
  child: ChildA(),
);

then from ChildA we can retrieve the Repository instance with:

RepositoryProvider.of<RepositoryA>(context)

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

RepositoryProvider<RepositoryA>(
  builder: (context) => RepositoryA(),
  child: RepositoryProvider<RepositoryB>(
    builder: (context) => RepositoryB(),
    child: RepositoryProvider<RepositoryC>(
      builder: (context) => RepositoryC(),
      child: ChildA(),
    )
  )
)

to:

MultiRepositoryProvider(
  providers: [
    RepositoryProvider<RepositoryA>(
      builder: (context) => RepositoryA(),
    ),
    RepositoryProvider<RepositoryB>(
      builder: (context) => RepositoryB(),
    ),
    RepositoryProvider<RepositoryC>(
      builder: (context) => RepositoryC(),
    ),
  ],
  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 state - 1;
        break;
      case CounterEvent.increment:
        yield state + 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<CounterBloc, int>(
        builder: (context, 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.add(CounterEvent.increment);
              },
            ),
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.remove),
              onPressed: () {
                counterBloc.add(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.

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

Dart Versions #

  • Dart 2: >= 2.0.0

Maintainers #

2.0.0 #

1.0.0 #

Updated to bloc: ^1.0.0 and Documentation Updates

0.22.1 #

Minor Bugfixes and Documentation Updates

0.22.0 #

Updated to bloc: ^0.16.0 and Documentation Updates

0.21.0 #

Updated to bloc: ^0.15.0 and Documentation Updates

0.20.1 #

  • Minor Updates to Package Dependencies
  • Documentation Updates

0.20.0 #

  • Add Automatic Bloc Lookup to BlocBuilder and BlocListener (#415)
  • Support for BlocProvider instantiation and look-up within the same BuildContext (#415)
  • Documentation Updates

0.19.1 #

Add optional condition to BlocListener to control listener calls (#406) and Documentation Updates

0.19.0 #

Addresses #354

BlocProvider #

  • Refactor BlocProvider to extend Provider
  • Rename BlocProviderTree to MultiBlocProvider

ImmutableProvider #

  • Refactor ImmutableProvider to extend Provider
  • Rename ImmutableProvider to RepositoryProvider
  • Rename ImmutableProviderTree to MultiRepositoryProvider

BlocListener #

  • Rename BlocListenerTree to MultiBlocListener

Documentation #

  • Inline documentation updates/improvements

0.18.3 #

Fix BlocProvider bug where copyWith does not preserve dispose value (#376).

0.18.2 #

Fix BlocListener bug where listener gets called even when no state change occurs (#368).

0.18.1 #

Minor Documentation Updates

0.18.0 #

Expose ImmutableProvider & ImmutableProviderTree to enable developers to provide immutable values, such as repositories, throughout the widget tree (#364) and Documentation Updates

0.17.0 #

Update BlocProvider to automatically dispose the provided bloc (#349) and Documentation Updates

0.16.0 #

Update BlocProvider to expose builder and dispose (#344 and #347) and Documentation Updates

0.15.1 #

Fix null initial previousState in BlocBuilder condition (#328) and Documentation Updates

0.15.0 #

Added optional condition to BlocBuilder to control widget rebuilds (#315) and Documentation Updates

0.14.0 #

Updated to bloc: ^0.14.0 and Documentation Updates

0.13.0 #

Updated to bloc: ^0.13.0 and Documentation Updates

0.12.0 #

Added BlocListenerTree and Documentation Updates

0.11.1 #

Broaden Dart version range and Minor Documentation Updates

0.11.0 #

Updated to bloc: ^0.12.0 and Documentation Updates

0.10.1 #

Invoke BlocWidgetListener on initial state and Documentation Updates

0.10.0 #

Added BlocListener and Documentation Updates

0.9.1 #

Minor Updates to Documentation.

0.9.0 #

Updated to bloc: ^0.11.0 and Documentation Updates

0.8.0 #

Updated to bloc: ^0.10.0 and Documentation Updates

0.7.1 #

Minor Updates to Documentation.

0.7.0 #

Added BlocProviderTree and Documentation Updates

0.6.3 #

Updated to bloc:^0.9.3 and Minor Updates to Documentation

0.6.2 #

Additional Minor Updates to Documentation

0.6.1 #

Minor Updates to Documentation

0.6.0 #

Updated to bloc: ^0.9.0

0.5.4 #

Additional Minor Updates to Documentation

0.5.3 #

Additional Minor Updates to Documentation

0.5.2 #

Minor Updates to Documentation

0.5.1 #

BlocProvider performance improvements

0.5.0 #

Updated to bloc: ^0.8.0

0.4.12 #

Additional Minor Updates to Documentation

0.4.11 #

Additional Minor Updates to Documentation

0.4.10 #

Additional BlocBuilder enhancements

  • BlocBuilder no longer filters out States giving developers full control

Minor Updates to Documentation and Examples

0.4.9 #

Additional BlocBuilder enhancements

  • BlocBuilder no longer has a dependency on RxDart
  • Using bloc: ">=0.7.5 <0.8.0"

0.4.8 #

Additional BlocProvider performance improvements

0.4.7 #

Minor Updates to Documentation and Examples

0.4.6 #

Bug Fixes

  • Fixed bug where BlocBuilder would return initial state instead of the latest state

0.4.5 #

Additional Minor Updates to Documentation

0.4.4 #

Minor updates to documentation and improved error reporting in BlocProvider

0.4.3 #

BlocBuilder performance improvements

0.4.2 #

BlocProvider performance improvements

0.4.1 #

Minor Updates to Documentation

0.4.0 #

Updated to bloc: ^0.7.0

0.3.1 #

Minor Updates to Documentation

0.3.0 #

Updated to bloc: ^0.6.0

0.2.1 #

Minor Updates to Documentation

0.2.0 #

Updates to BlocBuilder and BlocProvider

  • BlocBuilder does not automatically dispose a Bloc. Developers are now responsible for determining when to call Bloc.dispose()
  • BlocProvider support for of(context) with generics
    • Support for multiple nested BlocProviders with different Bloc Types.

0.1.1 #

Minor Updates to Documentation

0.1.0 #

Initial Version of the library.

  • Includes the ability to connect presentation layer to Bloc by using the BlocBuilder Widget.
  • Includes BlocProvider, a DI widget that allows a single instance of a bloc to be provided to multiple widgets within a subtree.

example/lib/main.dart

import 'dart:async';

import 'package:flutter/material.dart';

import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class SimpleBlocDelegate extends BlocDelegate {
  @override
  void onEvent(Bloc bloc, Object event) {
    super.onEvent(bloc, event);
    print(event);
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print(transition);
  }

  @override
  void onError(Bloc bloc, Object error, StackTrace stacktrace) {
    super.onError(bloc, error, stacktrace);
    print(error);
  }
}

void main() {
  BlocSupervisor.delegate = SimpleBlocDelegate();
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<ThemeBloc>(
      builder: (context) => ThemeBloc(),
      child: BlocBuilder<ThemeBloc, ThemeData>(
        builder: (context, theme) {
          return MaterialApp(
            title: 'Flutter Demo',
            home: BlocProvider(
              builder: (context) => CounterBloc(),
              child: CounterPage(),
            ),
            theme: theme,
          );
        },
      ),
    );
  }
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: BlocBuilder<CounterBloc, int>(
        builder: (context, 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: () => BlocProvider.of<CounterBloc>(context)
                  .add(CounterEvent.increment),
            ),
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.remove),
              onPressed: () => BlocProvider.of<CounterBloc>(context)
                  .add(CounterEvent.decrement),
            ),
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.update),
              onPressed: () =>
                  BlocProvider.of<ThemeBloc>(context).add(ThemeEvent.toggle),
            ),
          ),
        ],
      ),
    );
  }
}

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 state - 1;
        break;
      case CounterEvent.increment:
        yield state + 1;
        break;
    }
  }
}

enum ThemeEvent { toggle }

class ThemeBloc extends Bloc<ThemeEvent, ThemeData> {
  @override
  ThemeData get initialState => ThemeData.light();

  @override
  Stream<ThemeData> mapEventToState(ThemeEvent event) async* {
    switch (event) {
      case ThemeEvent.toggle:
        yield state == ThemeData.dark() ? ThemeData.light() : ThemeData.dark();
        break;
    }
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_bloc: ^2.0.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_bloc/flutter_bloc.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
100
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
100
Learn more about scoring.

We analyzed this package on Nov 11, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.6.0
  • pana: 0.12.21
  • Flutter: 1.9.1+hotfix.6

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0 <3.0.0
bloc ^2.0.0 2.0.0
flutter 0.0.0
provider ^3.0.0 3.1.0+1
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.7 1.1.8
rxdart 0.22.6
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
effective_dart ^1.1.1
flutter_test