GithubHelp home page GithubHelp logo

felangel / cubit Goto Github PK

View Code? Open in Web Editor NEW
593.0 23.0 27.0 3.06 MB

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.

Home Page: https://pub.dev/packages/cubit

License: MIT License

Dart 96.13% Kotlin 0.13% Ruby 3.30% Swift 0.41% Objective-C 0.04%
dart dart-package dart-library dartlang stream flutter flutter-package state-management

cubit's Introduction

⚠️ 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. ⭐

cubit's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cubit's Issues

Why do we need to specify the cobit state type in the cubitBuilder?

Is there a chance to have this:

CubitBuilder<CubitA>(
  cubit: cubitA, // provide the local cubit instance
  builder: (context, state) {
    // return widget here based on CubitA's state
  }
)

instead of:

CubitBuilder<CubitA, CubitAState>(
  cubit: cubitA, // provide the local cubit instance
  builder: (context, state) {
    // return widget here based on CubitA's state
  }
)

This can reduce the boilerplate of this package.
I think that it is possible to get the cobit state type from the cubit subclass.

Snippets

It would be nice if there were snippets for officially hosted IDEs - Android Studio and VS Code.

I would expect the following to be covered:

  • Cubit
  • CubitObserver
  • HydratedCubit
  • MultiCubitListener
  • CubitProvider
  • CubitConsumer
  • CubitBuilder
  • CubitListener

Provide a example in how to emit a complex object state

I'm trying to use Cubit this a complex state object and it is hard to emit state.

Here is my implementation:

@freezed
abstract class AuthState with _$AuthState {
  const factory AuthState({
    @Default(false) bool isLoggedIn,
  }) = _AuthState;
}

class AuthStateCubit extends Cubit<AuthState> {
  AuthStateCubit(this._preferences) : super(AuthState());

  final SharedPreferences _preferences;

  void updateLoginStatus() {
    state.copyWith.call(
      isLoggedIn: _preferences.getBool('_is_logged_in') ?? false;
    );
    emit(state);
  }
}

The idea here is to mantain the previous state and only update what have changed. Here it is almost implosible to emit a new state (inside the updateLoginStatus method). I can do:

emit(state.copyWith.call(
        isLoggedIn: _preferences.getBool('_is_logged_in') ?? false
    ));

But this sintax doesn't look good and since I can't mutate the state property it is hard to do so. Do you have any suggestion?

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?

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?

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!

Thoughts

https://github.com/felangel/cubit/blob/master/packages/flutter_cubit/lib/src/cubit_provider.dart#L8
https://github.com/felangel/cubit/blob/master/packages/flutter_cubit/lib/src/cubit_provider.dart#L33
https://github.com/felangel/cubit/blob/master/packages/flutter_cubit/lib/src/cubit_provider.dart#L108
https://github.com/felangel/cubit/blob/master/packages/flutter_cubit/lib/src/cubit_provider.dart#L148

Otherwise, I like this idea. It's just like Bloc, but it replaces Events with some kind of action methods. This also simplifies the need to check the type of Event in mapEventToStates. There should be some study etc. about Sink vs methods, but I cannot think any bad case when methods are less valuable. The main part of Bloc/Cubic is that States are immutable & distributed using Stream.

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.

[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! 🙏

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.

[HELP] Emit state from a stream in repository

Hi, I've used a lot your Bloc package and I'm trying this for the first time, so I've some questions about data management with a repository.
In my code, I've got a repository with a method getStream witch returns a stream of data.
My Cubit is structured like this:

class TestCubit extends Cubit<TestState> {
   final TestRepo repo = TestRepo();
   TestCubit(): super(TestStateLoading());
   
   void fetch() {
      var newState = ...
      emit(newState);
   }
}

How can I convert the value from each value of the stream to the new state?

[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;
}

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

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.

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🤗🤗

Allow cubit to override the provider

I would like to be able to override the dispose method on my Cubit. My reason being that I need to kill a timer. My workaround is to create the timer elsewhere and pass the cubit into the constructor.

Cubit Delegate/Supervisor missing?

HI,
I am new to your package Cubit and used previously the Bloc package. There is the supervisor, which you can utilize to debug etc.
Do you have it in Cubit as well, if not, how can I work around it?

Thanks again for your work!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.