Cubit is a lightweight state management solution

Overview

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

Cubit

build coverage Star on GitHub Discord License: MIT Starware

Cubit is a lightweight state management solution. It is a subset of the bloc package that does not rely on events and instead uses methods to emit new states.

Usage

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);

  void increment() => emit(state + 1);
  void decrement() => emit(state - 1);
}

Packages

Package Pub
cubit pub package
cubit_test pub package
flutter_cubit pub package
angular_cubit pub package
hydrated_cubit pub package
replay_cubit pub package

Documentation

Dart Versions

  • Dart 2: >= 2.7.0

Maintainers

Supporters

Very Good Ventures

Starware

Cubit is Starware.
This means you're free to use the project, as long as you star its GitHub repository.
Your appreciation makes us grow and glow up. ⭐

Comments
  • [PROPOSAL] Merge Cubit into Bloc

    [PROPOSAL] Merge Cubit into Bloc

    Hello everyone! πŸ‘‹

    First of all, thanks so much for the amazing support and enthusiasm! The interest and excitement surrounding cubit has been incredible πŸ™ πŸ’™

    Before diving into the proposal, I think it's valuable to provide some context around how cubit came about...

    Context

    It was brought to my attention several weeks ago that bloc was being used in a different way by various members of the community. Rather than adding events to the bloc, many developers found it simpler to just invoke methods on the bloc in which new states could be added.

    class MyBloc extends Bloc<MyState, MyState> {
      MyBloc(): super(MyState.initial());
    
      @override
      Stream<State> mapEventToState(MyState state) async* {
        yield state;
      }
    
      void someAction() {
        final nextState = _getNextState();
        add(nextState);
      }
    }
    

    This approach, while different from the intended usage, raised some completely valid points:

    • Not all state needs/benefits from being event-driven
    • An event-driven approach introduces extra complexity (especially for new developers)
    • An event-driven approach introduces additional code

    After many invaluable discussions with many members of the Flutter community, we began experimenting with the idea of pulling events out of bloc and creating a slimmed down version called cubit. Needless to say, what started out as an experiment quickly grew (way quicker than I had imagined πŸ˜…).

    I've been thinking about what the future could look like for both cubit and bloc and I have come to the conclusion that interoperability between the two would be a big win. Since cubit is a subset of bloc, most of the existing ecosystem can be shared (bloc_test, flutter_bloc, angular_bloc, hydrated_bloc, etc...). We have gone through the exercise of refactoring the existing ecosystem to use cubit as the base (cubit_test, flutter_cubit, angular_cubit, hydrated_cubit, etc...) which brings me to the proposal (drum roll please)

    πŸ₯ πŸ₯ πŸ₯ πŸ₯ πŸ₯ πŸ₯ πŸ₯ πŸ₯ πŸ₯ πŸ₯ πŸ₯ πŸ₯

    Proposal

    πŸŽ‰ I'm proposing to merge cubit into bloc πŸŽ‰

    Justification

    1. I think most would agree that there isn't a single best way to manage state; different types of state benefit from different state management solutions. Take for example autocomplete (or some sort of real-time search functionality) -- there is a significant benefit to taking an event-driven approach because it will likely be critical to debounce keypress events in order to avoid making excessive network requests. In the same application, we might have another feature which queries user profile data and presents it in the form of a profile page. In this case, the benefit of having an event-driven approach might not be as significant and to many it might be simpler to think of that interaction as a command/method (LoadUserProfile) rather than an event (UserProfileRequested).

    2. Since cubit is a subset of bloc, the two are closely related and merging them would streamline the development and delivery process a lot. Features and fixes for cubit will almost always impact bloc so with the proposed structure we can iterate faster and ensure a higher level of quality across the entire ecosystem.

    3. Unifying the two would provide a better developer experience. Rather than having to import cubit, flutter_cubit, cubit_test, bloc, flutter_bloc, and bloc_test, the developer experience could be improved by shipping cubit as part of bloc and ensuring compatibility across the entire existing bloc ecosystem. This would mean cubits and blocs could be consumed in the UI using existing widgets like BlocBuilder, BlocListener, BlocProvider, etc... which many developers are already familiar with. There would not be a need to use CubitBuilder and BlocBuilder (when in reality their implementations are identical). Cubits and blocs could also be unit tested using the existing blocTest package.

    4. Maintaining the ecosystem would be greatly simplified because packages like hydrated_cubit could be used as mixins and be made compatible with both the Cubit and Bloc classes. In addition, the documentation at bloclibrary.dev could be updated to include an introduction to cubit, lead into bloc and showcase real-world examples of when to use one vs the other.

    5. Current and future tooling could be consolidated/reused. Rather than maintaining separate tooling for generating/using blocs and cubits we could unify things under the current bloc tooling environment and continue to iterate and improve upon the existing infrastructure.

    6. We can continue to maintain a single (awesome) community of developers via the bloc discord and github repository and have all of the resources readily available and easily accessible to both new and experienced developers.

    Consequences

    The consequences of these changes would be:

    • The cubit package would be deprecated and the cubit github repository would be archived.
    • The current cubit code would be moved into the bloc package (under the bloc github repository).
    • The bloc package would export cubit and bloc.
    • The flutter_bloc package would be made compatible with both bloc and cubit instances
    • The bloc_test package would be made compatible with both bloc and cubit instances
    • The angular_bloc package would be made compatible with both bloc and cubit instances
    • The hydrated_bloc code would be moved into the bloc github repository and the current repository would be archived.
    • The hydrated_bloc package would be made compatible with both bloc and cubit instances.
    • The replay_cubit package would be deprecated and migrated into the replay_bloc package (WIP).

    I'm anticipating there will be breaking changes so this would likely all be within the scope of the v6.0.0 release of bloc.

    If there are no major objections to this proposal, we will work as hard as we can to deliver these changes as soon as possible while ensuring a high level of quality.

    Please give this issue a πŸ‘ if you support the proposal or a πŸ‘Ž if you're against it. If you object to 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! πŸ™

    help wanted feedback wanted 
    opened by felangel 32
  • Emitted state not received by CubitBuilder

    Emitted state not received by CubitBuilder

    Describe the bug When emitting a state after awaiting a call that was itself not asynchronous (i.e. reading from a repo that has the requested data already cached in memory and does not need to read it from disk again), the new state is not received by a listening CubitBuilder instance.

    Hint: When I follow the emit call in the problematic case, the _AsyncBroadcastStreamController has no subscriptions, which I guess is a problem... There is probably a racing condition when setting these things up.

    When not using any await, the new state is the first thing the CubitBuilder gets, never seeing the initial state in the first place. When awaiting a really asynchronous operation, both states are received as expected.

    To Reproduce here is a minimal piece of code to demonstrate the problem:

    import 'package:flutter/material.dart';
    import 'package:flutter_cubit/flutter_cubit.dart';
    import 'package:cubit/cubit.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) => MaterialApp(
            title: 'Flutter Demo',
            home: CubitProvider(
                create: (BuildContext context) => TestCubit(),
                child: MyHomePage(title: 'Flutter Demo Home Page')),
          );
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
    
        context.cubit<TestCubit>().init();
      }
    
      @override
      Widget build(BuildContext context) => Scaffold(
            body: Center(
              child: CubitBuilder<TestCubit, String>(
                builder: (context, state) => Text(
                  '$state',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ),
            ),
          );
    }
    
    class TestCubit extends Cubit<String> {
      TestCubit() : super("Waiting...");
    
      Future<void> init() async {
        final data = await _doSomething(); // works when commented out
        emit(data);
      }
    
      Future<void> _doSomething() async {
        return 'Works!';
      }
    }
    

    Expected behavior The emitted state should be received by the CubitBuilder instance no matter if the call leading up to the emit is synchronous or awaiting something synchronous or asynchronous

    bug waiting for response 
    opened by saschaernst 20
  • [DISCUSSION] Should Cubit expose Stream API?

    [DISCUSSION] Should Cubit expose Stream API?

    Something I immediately noticed is that because Cubit inherits from Stream, it surfaces the entire Stream API. This seems like a leaky abstraction, and I'm wondering whether there is some way that the API could be hidden?

    One option I could see is for Cubit to have a protected/private CubitStream<State> stream property, rather than inherit from it. The most minimal change might be something like the following.

    abstract class Cubit<State> {
      @protected
      CubitStream<State> stream;
    
      State get state => _stream.state;
    }
    
    question feedback wanted 
    opened by chimon2000 12
  • ReplayCubit - List with mutated state can not be redo / undo

    ReplayCubit - List with mutated state can not be redo / undo

    Describe the bug If I execute redo/undo on a state that has been spread into a new list it seems that I cannot redo/undo the state.

    To Reproduce Create a cubit with a List of elements and change the values of the list. After that spread, the list into the emit of the cubit. The CubitBuilder gets retriggered but with the old state.

      void selectDrink(Drink drink, bool selected) {
        state.firstWhere((element) => element.name == drink.name).selected =
            selected;
        emit([...state]);
      }
    

    Expected behavior The redo and undo function should work properly and update the state for the List.

    **Logs **

    Analyzing state_tutorials...                                            
    No issues found! (ran in 2.8s)
    
    [βœ“] Flutter (Channel stable, v1.17.4, on Mac OS X 10.15.5 19F101, locale en-DE)
        β€’ Flutter version 1.17.4 at /Users/myracle/tools/flutter
        β€’ Framework revision 1ad9baa8b9 (3 weeks ago), 2020-06-17 14:41:16 -0700
        β€’ Engine revision ee76268252
        β€’ Dart version 2.8.4
    
    [βœ“] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
        β€’ Android SDK at /Users/myracle/Library/Android/sdk
        β€’ Platform android-30, build-tools 30.0.0
        β€’ Java binary at: /Users/myracle/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/193.6514223/Android
          Studio.app/Contents/jre/jdk/Contents/Home/bin/java
        β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
        β€’ All Android licenses accepted.
    
    [βœ“] Xcode - develop for iOS and macOS (Xcode 11.5)
        β€’ Xcode at /Applications/Xcode.app/Contents/Developer
        β€’ Xcode 11.5, Build version 11E608c
        β€’ CocoaPods version 1.9.1
    
    [βœ“] Android Studio (version 4.0)
        β€’ Android Studio at /Users/myracle/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/193.6514223/Android Studio.app/Contents
        β€’ Flutter plugin version 47.1.2
        β€’ Dart plugin version 193.7361
        β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
    
    [βœ“] VS Code (version 1.46.1)
        β€’ VS Code at /Applications/Visual Studio Code.app/Contents
        β€’ Flutter extension version 3.12.1
    
    [βœ“] Connected device (1 available)
        β€’ sdk gphone x86 β€’ emulator-5554 β€’ android-x86 β€’ Android 10 (API 29) (emulator)
    
    β€’ No issues found!
    
    question 
    opened by md-weber 11
  • What scopes API should look like?

    What scopes API should look like?

    I'm always frustrated when I can't use my hydrated storage in multiple directories with multiple secure access keys at one project.

    I've figured out all problems on roadmap to this feature except how to defer new scope creation, for example after user passed auth UI. And also how API should look? Give me your thoughts pleaseπŸ€—πŸ€—

    enhancement feedback wanted 
    opened by orsenkucher 5
  • Preserve intialState of a cubit

    Preserve intialState of a cubit

    Is your feature request related to a problem? Please describe. In many cases we need to reset our state to a "initial state" and currently we do this by passing a new value to override a property of our state.

    For example, my state have a list of languages based on the selected Project so everytime I need to load languages from the new selected project I need to clear/reset my list of languages right before loading new languages based on the current selected project.

      Future loadLanguages(Project project) async {
        emit(state.copyWith.call(
          languages: [], // I need the initial state here
          isLoadingLanguages: true,
          loadLanguageFailure: null // I need the initial state here too
        ));
    
        final result = await languageRepository.getProjectLanguages(project.id);
    
        var newState = state.copyWith.call(isLoadingLanguages: false);
    
        newState = result.fold(
              (l) => newState.copyWith(loadLanguageFailure: l),
              (r) => newState.copyWith(languages: r),
        );
        emit(newState);
      }
    

    Describe the solution you'd like My suggestion is to let the Cubit preserve the initial state (something like initialState property) of a the cubit so that we don't need to create it everytime we need it.

    question 
    opened by pedromassango 5
  • CubitListener behaviour skips(1)

    CubitListener behaviour skips(1)

    Hi Felix,

    I have a question to the CubitListener: Why does the subscribe method skips the first state?

    /// cubit_listener.dart
      void _subscribe() {
        if (_cubit != null) {
          _subscription = _cubit.skip(1).listen((state) {
            if (widget.condition?.call(_previousState, state) ?? true) {
              widget.listener(context, state);
            }
            _previousState = state;
          });
        }
      }
    

    I am using an AuthCubit and AuthState with freezed like here

    @freezed
    abstract class AuthState with _$AuthState {
      const factory AuthState.initial() = Initial;
      const factory AuthState.authenticated() = Authenticated;
      const factory AuthState.unauthenticated() = Unauthenticated;
    }
    
    @injectable
    class AuthCubit extends Cubit<AuthState> {
      final IAuthFacade _authFacade;
      AuthCubit(this._authFacade) : super(const AuthState.initial());
    
      void checkIfAuthenticated() async {
        final userOption = await _authFacade.getSignedInUser();
    
        final nextState = userOption.fold(
          () => const AuthState.unauthenticated(),
          (a) => const AuthState.authenticated(),
        );
    
        emit(nextState);
      }
    
      void signOut() async {
        await _authFacade.signOut();
        emit(const Unauthenticated());
      }
    }
    

    and I got a Splashscreen Widget like here:

    class SplashPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return CubitListener<AuthCubit, AuthState>(
          cubit: getIt<AuthCubit>(),
          listener: (context, state) {
            state.map(
              initial: (_) {
                 // do nothing and spin the progress indicator until
                 // state is either authenticated or unauthenticated
              },
              authenticated: (_) {
                // todo: navigate to dashboardPage
              },
              unauthenticated: (_) {
                ExtendedNavigator.of(context).pushReplacementNamed(Routes.signInPage);
              },
            );
          },
          child: Scaffold(body: Center(child: CircularProgressIndicator())),
        );
      }
    }
    

    So now to my Problem:

    When I listen to the Cubit with CubitListener, the CubitStream first yields the current state, then it will yield all controller stream states. But in the CubitListener, the first yield will be skipped and this causes the problem, that the listener method of the CubitListener will not be called when the widget is built for the first time.

    In this case, I am not able to either navigate to the dashboardPage, if the state is 'Authenticated' nor to the signInPage if I am 'Unauthenticated'.

    Obviously, if the widget is built and I emit a state not equal to the last state then, then the listener method is called, and I can to whatever I need.

    Maybe, we could have an option, if we want to skip the first yielded state?

    question 
    opened by Manuelbaun 5
  • feat(hydrated_cubit): hydrated storage compaction

    feat(hydrated_cubit): hydrated storage compaction

    Description

    With intention to address https://github.com/felangel/hydrated_bloc/issues/57 issue,
    add compact method to HydratedStorage class, but not Storage interface.
    So you'll need to downcast.

    In addition to compact, introduce compactionStrategy for HydratedStorage.build function.

    Type of Change

    • [x] ✨ 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)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    enhancement 
    opened by orsenkucher 4
  • Scale cubit with bloc capabilities

    Scale cubit with bloc capabilities

    I posed this question on Twitter and wanted to document it here as an issue.

    I like the idea of cubit as being the baseline for bloc. It would allow teams to start with applications that don't need the event sourcing features that bloc provides, and scale up portions of their applications or the entire application when the need arises.

    enhancement 
    opened by chimon2000 4
  • Observer in Cubit?

    Observer in Cubit?

    Hey there is the advantage of using BLoC over Cubit because BLoC has an observer which prints out Current State, the Event and the Next State in the Observer?

    Or is that possible inside Cubit too?

    question 
    opened by Gene-Dana 3
  • CubitModule?

    CubitModule?

    Hello, so far I love this - after reading this package's documentation.

    I have question if it is worth to add a CubitModule that would combine with MultiCubitProvider. It would have list of Cubits needed for a child, quite handy if common list of CubitProvider is used in many places.

    MultiCubitProvider(
      providers: [
        CubitProvider<CubitA>(
          create: (BuildContext context) => CubitA(),
        ),
        CubitProvider<CubitB>(
          create: (BuildContext context) => CubitB(),
        ),
        CubitProvider<CubitC>(
          create: (BuildContext context) => CubitC(),
        ),
      ],
      child: ChildA(),
    )
    

    could be replaced with:

    MultiCubitProvider(
      module: MyCubitModule(),
      child: ChildA(),
    )
    

    Optional the modules but that would require also check if same Cubit was not already provided in other module. After digging into source of this and Provider, that might be not worth adding.

    Also this can be resolved with static list of providers, if their initialization and create method isn't called until needed.

    question 
    opened by magillus 3
Releases(hydrated_cubit-v0.1.3)
Owner
Felix Angelov
software engineer by day, software engineer by night.
Felix Angelov
Building a simple Flutter app for understanding the BLoC State Management including: Cubit, Managing Route & showSnackBar.

Building a simple Flutter app for understanding the BLoC State Management including: Cubit, Managing Route & showSnackBar.

TAD 8 Dec 3, 2022
Flutter boilerplate: support Android, iOS, Web, Mac, Linux, Window with bloc(cubit) state management

Flutter boilerplate: support Android, iOS, Web, Mac, Linux, Window with bloc(cubit) state management, dynamic theme, localization, environment (.env), logger, navigation (go_router), responsiveness (mobile, tablet, desktop), lint, unit/widget/integration test and more ...

Bumbii Co., Ltd 34 Dec 29, 2022
Building a simple Flutter app * Switch Theme * for understanding the BLoC State Management including: Cubit Communications with StreamSubscription & Managing Route.

Building a simple Flutter app * Switch Theme * for understanding the BLoC State Management including: Cubit Communications with StreamSubscription & Managing Route.

TAD 1 Oct 3, 2022
πŸ’» Flutter clean architecture using the bloc & cubit library for state management

Egymation ?? Β· This application was developed using a well-defined and decoupled architecture, following TDD (test-driven programming) as a working me

Mohaned Zekry 3 Nov 21, 2022
Flutter Control is complex library to maintain App and State management. Library merges multiple functionality under one hood. This approach helps to tidily bound separated logic into complex solution.

Flutter Control is complex library to maintain App and State management. Library merges multiple functionality under one hood. This approach helps to

Roman Hornak 23 Feb 23, 2022
GetX - one of the most popular state management solution in flutter

GteX Tutorial - Udemy GetX is one of the most popular state management solution in flutter. In addition to state management, GetX provides easy way to

Md. Siam 3 May 18, 2022
This repo is an example of clean architecture using the GetX state-management solution.

GetX Clean Architecture A Flutter Clean Architecture Using GetX. This repo is forked from: https://github.com/phamdinhduc795397/flutter-getx-clean-arc

Md. Siam 78 Jan 3, 2023
⚑FQuery is a powerful async state management solution for flutter. It caches, updates and fully manages asynchronous data in your flutter apps.

⚑ FQuery is a powerful async state management solution for flutter. It caches, updates and fully manages asynchronous data in your flutter apps. It ca

Piyush 21 Dec 22, 2022
A simple state management solution that combine the power of inherited widget and rxdart

Inherited RxDart is a state management library that combine the power of InheritedWidget and RxDart, a simple and elegant state management solution for apps of any scale

Nguyα»…n Ngọc PhΖ°α»›c 14 Oct 26, 2022
A flutter state management solution.

A Flutter State Management solution with dependency injection Usage Create your business logic class and place all variables, methods inside it. impor

Masum Billah Sanjid 5 Dec 15, 2022
Lightweight i18n solution for Flutter

fast_i18n Lightweight i18n solution. Use JSON, YAML or CSV files to create typesafe translations. About this library ?? Minimal setup, create JSON fil

Tien Do Nam 186 Dec 26, 2022
This application displays the characters of the series Breaking Bad, has been used Cubit state and API .

breaking_bad A new Flutter project using bloc. Getting Started This project is a starting point for bloc state_management. A few resources to get you

null 1 Dec 24, 2021
πŸš€ User management app built in flutter using clean architecture, MVVM, get it, dio, RxDart, bloc, cubit, getX and provider

?? Go Rest app In this project, we are going to build a user management app using Flutter. We have used the Go REST API to make HTTP request methods.

Sina 99 Aug 14, 2023
State Persistence - Persist state across app launches. By default this library store state as a local JSON file called `data.json` in the applications data directory. Maintainer: @slightfoot

State Persistence Persist state across app launches. By default this library store state as a local JSON file called data.json in the applications dat

Flutter Community 70 Sep 28, 2022
An extension to the bloc state management library which lets you create State Machine using a declarative API

An extension to the bloc state management library which lets you create State Machine using a declarative API

null 25 Nov 28, 2022
A powerful state machine for MobX management, that can be used in almost any application state.

A powerful state machine for MobX management, which can be used in almost any application state. It has 3 states - loading, success, error - and is pe

Daniel Magri 8 Oct 31, 2022
Practice building basic animations in apps along with managing app state by BLoC State Management, Flutter Slider.

Practice building basic animations in apps along with managing app state by BLoC State Management including: Cubit & Animation Widget, Flutter Slider.

TAD 1 Jun 8, 2022
Shopify Tag and Product Management App using Flutter and Riverpod State Management

Myshopify App A Simple Flutter Application project to get List of Tags, Products and Product Details from shopify https://shopicruit.myshopify.com/adm

Idowu Tomiwa 5 Nov 12, 2022
πŸ‘‘ The lightweight design pattern for small management applications.

Store Pattern ?? The lightweight design pattern for small management applications. Features | Structure | Install | Usage | Documents | Technologies |

UITers 71 Sep 26, 2022