flutter_redux_machine 1.0.0-dev.5.0

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 74

Build Status codecov

Integrates redux_machine and Flutter.

StoreAccess

An InheritedWidget which provides access to the application's state store.

class MyApp extends StatelessWidget {
  Widget build(BuildContext) {
    final store = buildStore();
    return new StoreAccess(
      store: store,
      child: new MaterialApp(/* .. */),
    );
  }
}

class MyWidget extends StatelessWidget {
  Widget build(BuildContext) {
    final store = StoreAccess.of(context);
    return new Container(child: new Text(store.state.welcomeMessage));
  }
}

StoreConnectedWidget

A simple widget connected to the application's state store. Rebuilds automatically each time connected state object updates.

Usually useful in simple scenarios when there is no need to dispatch Store actions or keep additional state. For advanced use cases see StoreConnectedState.

Below is an example of a StoreConnectedWidget which reacts to online status changes:

class OnlineIcon extends StoreConnectedWidget<AppState, bool> {
  /// Note that [StoreConnectedWidget] requires a mapper function.
  OnlineIcon() : super((state) => state.isOnline);

  @override
  Widget build(BuildContext context, bool isOnline) {
    final icon = isOnline ? Icons.cloud : Icons.cloud_off;
    return new Icon(icon);
  }
}

StoreConnectedState

A state object connected to a Redux store. Useful in advanced scenarios where StoreConnectedWidget does not provide enough flexibility.

Essentially works the same way as StoreConnectedWidget, e.g. rebuilds every time connected state object is updated.

However since this is a regular Flutter State you are free to declare extra state properties or react to life cycle events (initState, dispose, etc).

This class provides three additional life cycle hooks:

  • connect() method, called only once in the beginning of the object's life cycle. Normally useful to dispatch actions to the state store.
  • disconnect() method, called only once in the end of the object's life cycle before it's disposed. Similarly useful to dispatch actions to the state store or release any other allocated resources (e.g. cancel stream subscriptions).
  • didUpdateStoreState(T oldState) method, called each time connected state object updates. This method is invoked during setState() call so it is ok to update any additonal state fields here if needed.

It is recommended to use connect() and disconnect() instead of initState() and dispose(). See documentation for more details.

class CommentsView extends StatefulWidget {
  @override
  _CommentsViewState createState() => new _CommentsViewState();
}

class _CommentsViewState
    extends StoreConnectedState<AppState, List<String>, CommentsView> {
  /// [map] function defines to which part of application state
  /// this widget connects. In this case we are only interested in the list
  /// of comments.
  @override
  List<String> map(AppState state) => state.comments;

  @override
  void connect() {
    super.connect(); // must always call super
    /// It is safe to access current state from [connect] as it's been
    /// initialized already.
    if (state == null) {
      /// In this case if it's `null` we want to trigger an API call to fetch
      /// comments.
      /// The [store] property provides access to the application's state Store.
      store.dispatch(Actions.fetchComments());
    }
  }

  @override
  Widget build(BuildContext context) {
    /// Current list of comments can be accessed from [state] property.
    if (state == null)
      return new Center(child: new CircularProgressIndicator());
    if (state.isEmpty) return new Center(child: new Text('No comments.'));
    return new ListView(
      children: state
          .map((comment) => new ListTile(title: new Text(comment)))
          .toList(),
    );
  }
}

In the above example there is one downside - there is no way to react to a possible error when fetching comments from an API.

We can improve on that by leveraging AsyncAction from redux_machine library. AsyncAction is like regular Redux action but also carries a Future with it, so we can be notified about two facts: when it completes with success or with an error.

Note that AsyncAction carries Future<void> so there is no way to attach payload to it. This is intentional is we normally should receive updated state through the state store subscription. The main purpose of AsyncAction is to tell us when it's done and if there was an error. This allows us to escape from declaring traditional trio of actions: doSomething, doSomethingSuccess and doSomethingError, which is probably the most annoying part of otherwise amazing Redux pattern.

Here is how we can rewrite CommentsView using AsyncAction:

/// Example actions.
abstract class Actions {
  static const fetchComments = const VoidActionBuilder('fetchComments');
  /// New action using [AsyncVoidActionBuidler].
  static const fetchCommentsAsync =
      const AsyncVoidActionBuidler('fetchCommentsAsync');
}

class CommentsViewWithError extends StatefulWidget {
  @override
  _CommentsViewWithErrorState createState() =>
      new _CommentsViewWithErrorState();
}

class _CommentsViewWithErrorState
    extends StoreConnectedState<AppState, List<String>, CommentsView> {
  @override
  List<String> map(AppState state) => state.comments;

  /// Additional state for optional error
  var _error;

  @override
  void connect() {
    super.connect(); // must always call super
    if (state == null) {
      final action = Actions.fetchCommentsAsync();
      /// We are only interested in failed scenario in this case.
      action.done.catchError(_handleFetchError);
      store.dispatch(action);
    }
  }

  void _handleFetchError(error) {
    /// Only update if we are still mounted, otherwise discard
    if (mounted)
      setState(() {
        _error = error;
      });
  }

  @override
  Widget build(BuildContext context) {
    if (_error != null) {
      /// A very basic example of showing an error message.
      return new Center(child: new Text(_error.toString()));
    }
    if (state == null)
      return new Center(child: new CircularProgressIndicator());
    if (state.isEmpty) return new Center(child: new Text('No comments.'));
    return new ListView(
      children: state
          .map((comment) => new ListTile(title: new Text(comment)))
          .toList(),
    );
  }
}

Features and bugs

Please file feature requests and bugs at the issue tracker.

1.0.0-dev.5.0

  • Prepare for Dart 2 stable.

1.0.0-dev.4.0

  • Added: StoreConnectedState.didUpdateStoreState life cycle hook. See README.md and dartdoc for more details.

1.0.0-dev.3.0

  • Depend on latest redux_machine and relax constraint to "^1.0.0-rc".

1.0.0-dev.2.0

  • Depend on redux_machine 1.0.0-beta.1

1.0.0-dev.1.0

  • Initial version.

example/main.dart

// Copyright (c) 2018, Anatoly Pulyaevskiy. All rights reserved. Use of this source code
// is governed by a BSD-style license that can be found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:flutter_redux_machine/flutter_redux_machine.dart';
import 'package:redux_machine/redux_machine.dart';

/// Example actions.
abstract class Actions {
  static const fetchComments = const VoidActionBuilder('fetchComments');
  static const fetchCommentsAsync =
      const AsyncVoidActionBuilder('fetchCommentsAsync');
}

/// Example application state.
class AppState {
  final bool isOnline;
  final List<String> comments;

  AppState(this.isOnline, this.comments);
}

Store<AppState> buildStore() {
  final builder =
      new StoreBuilder<AppState>(initialState: new AppState(false, null));
  // TODO: bind reducers.
  return builder.build();
}

/// Example using [StoreAccess].
class MyApp extends StatelessWidget {
  Widget build(BuildContext) {
    final store = buildStore();
    return new StoreAccess(
      store: store,
      child: new MaterialApp(/* .. */),
    );
  }
}

class MyWidget extends StatelessWidget {
  Widget build(BuildContext context) {
    final store = StoreAccess.of(context);
    return new Container(child: new Text(store.state.welcomeMessage));
  }
}

/// Example using [StoreConnectedWidget].
class OnlineIcon extends StoreConnectedWidget<AppState, bool> {
  /// Note that [StoreConnectedWidget] requires a mapper function.
  OnlineIcon() : super((state) => state.isOnline);

  @override
  Widget build(BuildContext context, bool isOnline) {
    final icon = isOnline ? Icons.cloud : Icons.cloud_off;
    return new Icon(icon);
  }
}

/// Example of using [StoreConnectedState] dispatching regular [Action] on
/// connect.
class CommentsView extends StatefulWidget {
  @override
  _CommentsViewState createState() => new _CommentsViewState();
}

class _CommentsViewState
    extends StoreConnectedState<AppState, List<String>, CommentsView> {
  /// [map] function defines to which part of application state
  /// this widget connects. In this case we are only interested in the list
  /// of comments.
  @override
  List<String> map(AppState state) => state.comments;

  @override
  void connect() {
    super.connect(); // must always call super
    /// It is safe to access current state from [connect] as it's been
    /// initialized already.
    if (state == null) {
      /// In this case if it's `null` we want to trigger an API call to fetch
      /// comments.
      /// The [store] property provides access to the application's state Store.
      store.dispatch(Actions.fetchComments());
    }
  }

  @override
  Widget build(BuildContext context) {
    /// Current list of comments can be accessed from [state] property.
    if (state == null)
      return new Center(child: new CircularProgressIndicator());
    if (state.isEmpty) return new Center(child: new Text('No comments.'));
    return new ListView(
      children: state
          .map((comment) => new ListTile(title: new Text(comment)))
          .toList(),
    );
  }
}

/// Example of using [StoreConnectedState] dispatching [AsyncAction] on
/// connect and handling error response.
class CommentsViewWithError extends StatefulWidget {
  @override
  _CommentsViewWithErrorState createState() =>
      new _CommentsViewWithErrorState();
}

class _CommentsViewWithErrorState
    extends StoreConnectedState<AppState, List<String>, CommentsView> {
  @override
  List<String> map(AppState state) => state.comments;

  /// Additional state for optional error
  var _error;

  @override
  void connect() {
    super.connect(); // must always call super
    if (state == null) {
      final action = Actions.fetchCommentsAsync();
      action.done.catchError(_handleFetchError);
      store.dispatch(action);
    }
  }

  void _handleFetchError(error) {
    /// Only update if we are still mounted, otherwise discard
    if (mounted)
      setState(() {
        _error = error;
      });
  }

  @override
  Widget build(BuildContext context) {
    if (_error != null) {
      /// A very basic example of showing an error message.
      return new Center(child: new Text(_error.toString()));
    }
    if (state == null)
      return new Center(child: new CircularProgressIndicator());
    if (state.isEmpty) return new Center(child: new Text('No comments.'));
    return new ListView(
      children: state
          .map((comment) => new ListTile(title: new Text(comment)))
          .toList(),
    );
  }
}

Use this package as a library

1. Depend on it

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


dependencies:
  flutter_redux_machine: ^1.0.0-dev.5.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter packages get

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

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_redux_machine/flutter_redux_machine.dart';
  
Version Uploaded Documentation Archive
1.0.0-dev.5.0 Jul 26, 2018 Go to the documentation of flutter_redux_machine 1.0.0-dev.5.0 Download flutter_redux_machine 1.0.0-dev.5.0 archive
1.0.0-dev.4.0 Jun 1, 2018 Go to the documentation of flutter_redux_machine 1.0.0-dev.4.0 Download flutter_redux_machine 1.0.0-dev.4.0 archive
1.0.0-dev.3.0 Apr 2, 2018 Go to the documentation of flutter_redux_machine 1.0.0-dev.3.0 Download flutter_redux_machine 1.0.0-dev.3.0 archive
1.0.0-dev.2.0 Apr 2, 2018 Go to the documentation of flutter_redux_machine 1.0.0-dev.2.0 Download flutter_redux_machine 1.0.0-dev.2.0 archive
1.0.0-dev.1.0 Mar 23, 2018 Go to the documentation of flutter_redux_machine 1.0.0-dev.1.0 Download flutter_redux_machine 1.0.0-dev.1.0 archive
Popularity:
Describes how popular the package is relative to other packages. [more]
49
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
98
Overall:
Weighted score of the above. [more]
74
Learn more about scoring.

We analyzed this package on Aug 10, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.0.0
  • pana: 0.11.8
  • Flutter: 0.5.7

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Suggestions

Package is pre-release.

Pre-release versions should be used with caution, their API may change in breaking ways.

The description is too short.

Add more detail about the package, what it does and what is its target use case. Try to write at least 60 characters.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev <3.0.0
flutter 0.0.0
redux_machine ^1.0.0-rc 1.0.0-rc.3
Transitive dependencies
collection 1.14.6 1.14.11
meta 1.1.5 1.1.6
sky_engine 0.0.99
typed_data 1.1.5 1.1.6
vector_math 2.0.6 2.0.8
Dev dependencies
flutter_test