favour_state 0.0.1

  • Readme
  • Changelog
  • Example
  • Installing
  • 44

favour_state #

State management solution for flutter inspired by cerebraljs and property_change_notifier

Documentation now in WIP status. Please look at example.

#Example

// state.dart

import 'package:favour_state/favour_state.dart';

class ExampleState implements StoreState<ExampleState> {
  final int counter;
  final bool enabled;

  int get controllableCounter => enabled ? counter : 0;

  const ExampleState({
    this.counter = 0,
    this.enabled = true,
  });

  @override
  ExampleState copyWith({
    int counter,
    bool enabled,
  }) {
    if ((counter == null || identical(counter, this.counter)) &&
        (enabled == null || identical(enabled, this.enabled))) {
      return this;
    }

    return ExampleState(
      counter: counter ?? this.counter,
      enabled: enabled ?? this.enabled,
    );
  }
}
// store.dart

import 'package:favour_state/favour_state.dart';

import 'state.dart';

class ExampleStore extends BaseStore<ExampleState> {
  // Any reaction is optional
  ValueReaction<ExampleState, bool> enabled;
  ValueReaction<ExampleState, int> controllableCounter;
  ValueReaction<ExampleState, ExampleState> self;

  EffectReaction<ExampleState> onCounterChange;

  // if you not declare reaction you can leave the method empty
  @override
  void initReactions() {
    // under the hood value reaction implement ValueListenable
    enabled = valueReaction((s) => s.enabled, topics: {#enabled});
    controllableCounter = valueReaction(
      (s) => s.controllableCounter,
      topics: {#enabled, #counter},
    );
    self = valueReaction((s) => s);

    // in closure of effect reaction you can use anything from class scope
    onCounterChange = effectReaction(
      // ignore: avoid_print
      (s) => print('counter changed'),
      topics: {#counter},
    );
  }

  // you can initialize your state
  @override
  ExampleState initState() => const ExampleState(counter: 1);

  Future<void> multiply(int multiplier) async {
    await run(MultiplyCounter(multiplier));
  }

  Future<void> toggle() async {
    await run(action<ExampleStore>((store, mutator, [services]) {
      // #name - should be equal to state copyWith named params
      // For example if copyWith is 'void copyWith({int counter, bool enabled})'
      // you can use #counter and #enabled to mutate state
      mutator[#enabled] = !store.state.enabled;
    }));
  }
}

// Action can be class based
class MultiplyCounter extends StoreAction<ExampleStore> {
  // constructor can declare positional and optional params
  MultiplyCounter(int multiplier)
      : super(
          // closure for action should declare this params
          (store, mutator, [services]) {
            // You can use any of this api to mutate state
            // mutator[#counter] = 1
            // mutator.changes = {#counter: 1, #enabled: false};
            // mutator.merge({#counter: 1, #enabled: false});
            // mutator.set(#counter, 1);
            mutator[#counter] = store.state.counter * multiplier;
          },
        );
}
// main.dart

import 'package:favour_state/favour_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get_it/get_it.dart';

import 'store.dart';

void bootstrapStores(AppState appState) {
  // Store initialized after registration
  // when method bootstrapServices called
  // Runtime setup into state and methods
  // initState and initReactions called

  // Store can be registered using this api
  // appState.registerStore(AnotherStore());
  // appState.registerDerivedStore(
  //   (store) => SomeStore(dependency: stores<AnotherStore>())
  // )
  // when you register derived store you can get already registered stores
  appState.registerStore(ExampleStore());
}

void main() {
  runApp(ExampleApp());
}

// ignore: use_key_in_widget_constructors
class ExampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: AppStateProvider(
          bootstrap: bootstrapStores,
          serviceProvider: GetIt.I,
          child: Builder(
            builder: (context) {
              final store = AppStateScope.store<ExampleStore>(context);
              // ignore: avoid_unnecessary_containers
              return Scaffold(
                body: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      ValueListenableBuilder(
                          valueListenable: store.controllableCounter,
                          builder: (context, value, child) => Text('$value')),
                      RaisedButton(
                        onPressed: () => store.multiply(2),
                        child: const Text('Doubly'),
                      ),
                      RaisedButton(
                        onPressed: store.toggle,
                        child: const Text('Toggle'),
                      ),
                    ],
                  ),
                ),
              );
            },
          ),
        ),
      );
}

Introduction #

Goals #

ToDo #

  • [x] core functionality
  • [x] basic tests
  • [ ] more tests
  • [ ] error handling
  • [ ] logging
  • [ ] dev tools
  • [ ] improve documentation

State #

A simple State can be declared as follows

import 'package:favour_state/favour_state.dart';

class ExampleState extends StoreState<ExampleState> {
  @override
  ExampleState copyWith() => ExampleState();
}

Any state used in the store must extend or implement StoreState<T> where T is the state type that extends or implements StoreState

Any state must implement the copyWith method. Since the state must be immutable, the copyWith method is the only way to partially change the state.


Store #

A simple Store can be declared as follows

import 'package:favour_state/favour_state.dart';

import 'state.dart';

class ExampleStore extends BaseStore<ExampleState> {
  @override
  void initReactions() {}

  @override
  ExampleState initState() => ExampleState();
}

Any Store must extend BaseStore<T> where T is the state type that extend or implement StoreState and used with this Store.

Any Store must implement initReactions and initState methods. Method initState should return instance of state which conforms to T. Method initReactions could be empty.

Internals #

Work in progress


Reactions #

Work in progress

Value Reaction #

Work in progress

Effect Reaction #

Work in progress

Internals #

Work in progress


Actions #

Class-based action #

Work in progress

Action from helper function #

Work in progress

Internals #

Work in progress


AppState #

Work in progress

Services provider #

Work in progress

Store registration #

Work in progress

Get store by type #

Work in progress

Internals #

Work in progress


Widgets #

Work in progress

AppStateProvider #

Work in progress

AppStateScope #

Work in progress

Internals #

Work in progress


Internals #

Work in progress

StoreRuntime #

Work in progress

[0.0.1] - TODO: Add release date. #

  • TODO: Describe initial release.

example/lib/main.dart

import 'package:favour_state/favour_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get_it/get_it.dart';

import 'store.dart';

void bootstrapStores(AppState appState) {
  // Store initialized after registration
  // when method bootstrapServices called
  // Runtime setup into state and methods
  // initState and initReactions called

  // Store can be registered using this api
  // appState.registerStore(AnotherStore());
  // appState.registerDerivedStore(
  //   (store) => SomeStore(dependency: stores<AnotherStore>())
  // )
  // when you register derived store you can get already registered stores
  appState.registerStore(ExampleStore());
}

void main() {
  runApp(ExampleApp());
}

// ignore: use_key_in_widget_constructors
class ExampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: AppStateProvider(
          bootstrap: bootstrapStores,
          serviceProvider: GetIt.I,
          child: Builder(
            builder: (context) {
              final store = AppStateScope.store<ExampleStore>(context);
              // ignore: avoid_unnecessary_containers
              return Scaffold(
                body: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      ValueListenableBuilder(
                          valueListenable: store.controllableCounter,
                          builder: (context, value, child) => Text('$value')),
                      RaisedButton(
                        onPressed: () => store.multiply(2),
                        child: const Text('Doubly'),
                      ),
                      RaisedButton(
                        onPressed: store.toggle,
                        child: const Text('Toggle'),
                      ),
                    ],
                  ),
                ),
              );
            },
          ),
        ),
      );
}

Use this package as a library

1. Depend on it

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


dependencies:
  favour_state: ^0.0.1

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:favour_state/favour_state.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
0
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
70
Overall:
Weighted score of the above. [more]
44
Learn more about scoring.

We analyzed this package on Jul 9, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.13
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

because of import path [favour_state] that is in a package requiring null.

Health suggestions

Fix lib/src/core.dart. (-0.50 points)

Analysis of lib/src/core.dart reported 1 hint:

line 355 col 32: Avoid using braces in interpolation when not needed.

Maintenance suggestions

The package description is too short. (-20 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.7.0 <3.0.0
flutter 0.0.0
quiver ^2.1.3 2.1.3
Transitive dependencies
collection 1.14.12 1.14.13
matcher 0.12.8
meta 1.1.8 1.2.1
path 1.7.0
sky_engine 0.0.99
stack_trace 1.9.5
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
flutter_test
mockito ^4.1.1
pedantic ^1.9.0