All Projects → Cretezy → flutter_super_state

Cretezy / flutter_super_state

Licence: MIT license
A simple super state management library for Flutter (with async support)

Programming Languages

dart
5743 projects

Projects that are alternatives of or similar to flutter super state

tuxi
✨ White glove service for your async needs
Stars: ✭ 14 (-36.36%)
Mutual labels:  state-management
atomic-state
Atomic State is a state management library for React
Stars: ✭ 15 (-31.82%)
Mutual labels:  state-management
nstate
A simple but powerful react state management library with low mind burden
Stars: ✭ 11 (-50%)
Mutual labels:  state-management
crypto-currency
A Flutter application showing crypto currency rates.
Stars: ✭ 16 (-27.27%)
Mutual labels:  state-management
storken
🦩 Storken is a React State Manager. Simple as `useState`.
Stars: ✭ 22 (+0%)
Mutual labels:  state-management
ngx-mobx
Mobx decorators for Angular Applications
Stars: ✭ 14 (-36.36%)
Mutual labels:  state-management
swr-internal-state
Use SWR to manage app's internal state
Stars: ✭ 32 (+45.45%)
Mutual labels:  state-management
UniTEA
Implementation of The Elm Architecture for Unity3D
Stars: ✭ 31 (+40.91%)
Mutual labels:  state-management
gstate
A crazy state management for lazy programmers
Stars: ✭ 27 (+22.73%)
Mutual labels:  state-management
NObservable
MobX like observable state management library with Blazor support
Stars: ✭ 66 (+200%)
Mutual labels:  state-management
reactive-states
Reactive state implementations (brainstorming)
Stars: ✭ 51 (+131.82%)
Mutual labels:  state-management
quick-redux
helper functions to make redux and react development quicker
Stars: ✭ 61 (+177.27%)
Mutual labels:  state-management
almy
🗄️ 673 bytes store for managing the state in your application
Stars: ✭ 26 (+18.18%)
Mutual labels:  state-management
bloc
A predictable state management library that helps implement the BLoC design pattern
Stars: ✭ 12 (-45.45%)
Mutual labels:  state-management
XUI
XUI makes modular, testable architectures for SwiftUI apps a breeze!
Stars: ✭ 100 (+354.55%)
Mutual labels:  state-management
screenmanager
Stackable Screen/State Management for the LÖVE framework.
Stars: ✭ 29 (+31.82%)
Mutual labels:  state-management
mst-effect
💫 Designed to be used with MobX-State-Tree to create asynchronous actions using RxJS.
Stars: ✭ 19 (-13.64%)
Mutual labels:  state-management
immutable-cursor
👊 Immutable cursors incorporating the Immutable.js interface over a Clojure-inspired atom
Stars: ✭ 58 (+163.64%)
Mutual labels:  state-management
hoox
Functional react state management base on hooks
Stars: ✭ 80 (+263.64%)
Mutual labels:  state-management
asyncmachine
Relational State Machine with a visual inspector
Stars: ✭ 67 (+204.55%)
Mutual labels:  state-management

flutter_super_state pub package

A simple super state management library for Flutter (with async support).

Super State uses a central store, while holds your modules. Modules are similar to Flutter's StatefulWidgets.

Pub - API Docs - GitHub

Read the Medium article, or view the video tutorial.

Setup

Add the package to your pubspec.yaml to install:

dependencies:
  flutter_super_state: ^0.2.0

See Flutter example for a full overview.

Modules

First, let's create your modules. Modules hold both state (which can be changed inside setState) and actions.

Actions are simply class methods which can call setState, do other async operations, or call actions in other modules.

import 'package:flutter_super_state/flutter_super_state.dart';

// Modules extend `StoreModule`
class CounterModule extends StoreModule {
  // Read only property, to avoid accidentally setting `counter` without calling `setState`
  int get counter => _counter;

  var _counter = 0;

  // This automatically registers your module to your store
  CounterModule(Store store): super(store);

  // Synchronous actions
  void increment() {
    setState(() {
      _counter++;
    });
  }

  void decrement() {
    setState(() {
      _counter--;
    });
  }

  // Asynchronous actions
  Future<void> incrementAsync() async {
    await Future.delayed(Duration(milliseconds: 10));

    setState(() {
      _counter++;
    });
  }

  Future<void> decrementAsync() async {
    await Future.delayed(Duration(milliseconds: 10));

    setState(() {
      _counter--;
    });
  }
}

// Another module, which uses the CounterModule
class UserModule extends StoreModule {
  bool get isLoggedIn => _isLoggedIn;

  var _isLoggedIn = false;

  UserModule(Store store): super(store);

  // Synchronous actions
  Future<void> login() async {
    // Do network request...
    await Future.delayed(Duration(milliseconds: 100));
    
    // Always set state inside `setState`, or else it will not update!
    setState(() {
      _isLoggedIn = true;
    });

    // Trigger action in another module
    await store.getModule<CounterModule>().incrementAsync();
  }
}

The read-only properties are optional but strongly recommended to prevent accidentally changing the state of a module without updating it, which would not update your views.

Always do state changes inside setState (same as a StatefulWidget)

Store

Creating a store is very simple.

import 'package:flutter_super_state/flutter_super_state.dart';


final store = Store();

// Register modules. Order does not matter. You should register all modules on initialization
CounterModule(store);
UserModule(store);

// Trigger an action
await store.getModule<UserModule>().login();

Store Provider

For Flutter, simply wrap your application inside a StoreProvider:

runApp(StoreProvider(
  // Previously created store
  store: store,
  child: MyApp(),
));

This will make the store accessibly anywhere in your application using StoreProvider.store(context), or using the builders.

Module Builder

To get a module in Flutter views, you can use ModuleBuilder:

@override
Widget build(BuildContext context) {
  return ModuleBuilder<CounterModule>(
    builder: (context, counterModule) {
      return Text(counterModule.counter.toString());
    },
  );
}

The builder will rebuild when the module calls setState.

Child builder

If you have a large part of the state which doesn't update, you can pass it as child to the ModuleBuilder and use childBuilder:

@override
Widget build(BuildContext context) {
  return ModuleBuilder<CounterModule>(
    // Will not rebuild on update
    child: Container(),
    childBuilder: (context, counterModule, child) {
      return Row(
        children: <Widget>[
          child, // Container()
          Text(counterModule.counter.toString()),
        ]
      );
    },
  );
}

Store Builder

To get your store in Flutter views, you can use StoreBuilder:

@override
Widget build(BuildContext context) {
  return StoreBuilder(
    builder: (context, store) {
      return Text(store.getModule<CounterModule>().counter.toString());
    },
  );
}

The builder will rebuild when any module calls setState. It is preferable to use ModuleBuilder which only updates when the listened module updates.

Child builder

If you have a large part of the state which doesn't update, pass it as child to the StoreBuilder and use childBuilder:

@override
Widget build(BuildContext context) {
  return StoreBuilder(
    // Will not rebuild on update
    child: Container(),
    childBuilder: (context, store, child) {
      return Row(
        children: <Widget>[
          child, // Container()
          Text(store.getModule<CounterModule>().counter.toString()),
        ]
      );
    },
  );
}

Hooks (Persistance)

You can add hooks per module for pre- and post-update. This can be useful for persisting state.

class PersistCounterModule extends StoreModule {
  int get counter => _counter;
  var _counter = 0;

  PersistCounterModule(Store store, int initialCounter = 0):
    // Initial state
    _counter = initialCounter,
    super(store);

  void increment() {
    setState(() {
      // Will call preSetState here
      _counter++;
      // Will call postSetState here
    });
  }

  void postSetState() {
    // Use whatever storage mechanism you want
    storage.set("counter", counter);
  }
}

// During initialization
PersistCounterModule(store, storage.get("counter"));

You can also use the preSetState which is called before setState is done.

It a a convention that the store parameter should come first in a module's constructor.

Streams

Both store and module expose onChange streams which are called whenever:

  • Module: The module updates
  • Store: Any module updates

Both of these only dispatch null, indicating that an update was done.

Dispose

Both the store (if extended) and modules have a dispose method that is available. You can do cleanup of any value here (don't forget to call super.dispose() though).

This method isn't usually called, as your store is active for the lifetime of your application.

Repository

Easily add repositories or other dependencies to your module by simply passing them as arguments:

class AuthRepository {
  // Optional: Useful for getting auth token for future request, etc...
  final Store store;

  AuthRepository(this.store);

  Future<void> login(String username, String password) async {
    // Do request, able to use store modules
    final authToken = await doApiRequest(/* ... */);

    return authToken;
  }
}

class AuthModule extends StoreModule {
  final AuthRepository authRepository;

  var isLoggedIn = false;

  AuthModule(
    Store store, {
    @required this.authRepository,
  }) : super(store);

  Future<void> login(String username, String password) async {
    await authRepository.login(username, password);

    setState(() {
      isLoggedIn = true;
    });
  }

  void logout() {
    setState(() {
      isLoggedIn = false;
    });
  }
}


final store = Store();
// Create repository with store
final authRepository = AuthRepository(store);
// Register module with repository
AuthModule(store, authRepository: authRepository);

Testing

You can easily test your store by simply mocking repositories:

class AuthRepositoryMock implements AuthRepository {
  final Store store;
  AuthRepositoryMock(this.store);

  @override
  Future<String> login() async {
    // Mock request
    return "mock-auth-token";
  }
}


final store = Store();
// Create repository mock
final authRepositoryMock = AuthRepositoryMock();
// Register module with mocked repository
final authModule = AuthModule(store, authRepository: authRepositoryMock);

// Call action
await authModule.login("admin", "password");

You can also use something like mockito to create real mocks:

class AuthRepositoryMock extends Mock implements AuthRepository {}


final store = Store();
final authRepositoryMock = AuthRepositoryMock();
final authModule = AuthModule(store, authRepository: authRepositoryMock);


// Stub login function
when(authRepositoryMock.login())
    .thenAnswer((_) => Future.value('mock-auth-token'));

// Call action
await authModule.login("admin", "password");

// Verify mock was called with proper arguments
verify(authRepositoryMock.login("admin", "password"))

Features and bugs

Please file feature requests and bugs at the issue tracker.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].