remote_state 1.0.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 58

RemoteState #

Build & test codecov License: MIT

Tools for mapping data from remote sources in Dart, similar to Elm's RemoteData: https://elmprogramming.com/remote-data.html

PackagePub
remote_statepub package

Slaying a UI Antipattern with Flutter. #

Library inspired by a blog post by Kris Jenkins about How Elm slays a UI antipattern.

What problem does this package solve? #

You are making an API request, and you want to display or do different things based on the status of the request.

Why RemoteState, not RemoteData? #

I gained secondary inspiration from a talk by Jed Watson, A Treatise on State. As much as possible, I try to categorize state correctly in my applications.

The RemoteState approach #

Instead of using a complex object we use a single data type to express all possible request states. This approach makes it impossible to create invalid states.

Usage #

A common use case for RemoteState would be mapping it into a UI transition or component state. Here is an example that uses StateNotifier, found in examples/counter_state_notifier

counter/notifier/counter.dart #


class Counter extends StateNotifier<int> {
  Counter(): super(RemoteState.initial())

  void increment() {
    try {
      state = RemoteState.loading();

      var count = await api.increment();

      state = RemoteState.success(count);
    } catch (e) {
      state = RemoteState.error(e.message);
    }
  }
}

main.dart #

class ExampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: StateNotifierProvider<CounterNotifier, RemoteState<int>>.value(
        value: CounterNotifier(),
        child: HomePage(),
      ),
    );
  }
}

home.dart #

class HomePage extends StatelessWidget {
  const HomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    //2. Resolve counter notifier to update state
    var counterNotifier = Provider.of<CounterNotifier>(context);
    var counterState = Provider.of<RemoteState<int>>(context);

    var textStyle = Theme.of(context).textTheme.display1;
    final fabPadding = EdgeInsets.symmetric(vertical: 5.0);

    return Scaffold(
      appBar: AppBar(
        title: Text('RemoteState with StateNotifier'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            //3. Render state changes
            counterState.when(
              initial: () => Text('Not loaded', style: textStyle),
              success: (value) => Text('$value', style: textStyle),
              loading: () => Text('Loading...', style: textStyle),
              error: (_) => Text('Error', style: textStyle),
            ),
          ],
        ),
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Padding(
            padding: fabPadding,
            child: FloatingActionButton(
              heroTag: 'inc',
              child: Icon(Icons.add),
              //4. Perform increment action
              onPressed: () => counterNotifier.increment(),
            ),
          ),
          Padding(
            padding: fabPadding,
            child: FloatingActionButton(
              heroTag: 'dec',
              child: Icon(Icons.remove),
              //5. Perform decrement action
              onPressed: () => counterNotifier.decrement(),
            ),
          ),
        ],
      ),
    );
  }
}

API #

RemoteState #

RemoteState<T> is usedto annotate your request variables. It wraps all possible request states into one single union type. Use the parameters to specify.

  • T: The success value type.

RemoteState.initial #

RemoteState.initial is an instance of RemoteState that signifies the request hasn't been made yet.

RemoteState.loading #

RemoteState.loading is an instance of RemoteState that signifies the request has been made, but it hasn't returned any data yet.

RemoteState.success #

RemoteState.success is an instance of RemoteState that signifies the request has completed successfully and the new data (of type T) is available.

RemoteState.error #

RemoteState.error is an instance of RemoteState that signifies the request has failed.

Pattern matching high order functions #

When #

The when method is a high order function that accepts a method for each state and matches the request state with the appropriate callback function. All callbacks are required and must not be null.

MaybeWhen #

The maybeWhen method is a high order function that accepts a method for each state and matches the request state with the appropriate callback function or a fallback callback for missing methods. Only orElse is required.

Map #

The map method is the equivalent of when without the destructuring.

MaybeMap #

The maybeWhen method is the equivalent of when without the destructuring.

Maintainers #

References #

Unreleased #

1.0.0 - 2020-05-05 #

Added #

  • add flutter_hooks example.
  • add docs for state predicates
  • updated examples
  • removed empty, added stacktrace to error, added predicates

Changed #

  • state notifier hook per suggestions.

Documented #

  • removed empty from docs

BREAKING CHANGE #

empty is no longer supported because it is not considered a valid remote state.

v0.19.0 - 2020-04-21 #

Fixed #

  • bad documentation

v0.18.0 - 2020-04-21 #

Added #

  • more documentation to readme
  • document public apis & hide constructors
  • readme health, usage & maintainer sections

Changed #

  • state notifier example

v0.17.0 - 2020-04-21 #

Added #

  • example in packages/remote_state

Fixed #

  • add gitignore to packages/remote_state

v0.16.0 - 2020-04-21 #

Fixed #

  • move changelog and add better package description

v0.15.0 - 2020-04-21 #

Fixed #

  • space in publish.yml

[v0.14.0] - 2020-04-21 #

Added #

  • git-chglog configuration
  • add script to prepare release
  • add publish github action
  • add code coverage
  • add more tests & coverage
  • add github action to build packages
  • add bloc example
  • add provider example

Changed #

  • add name to coverage step
  • same text styles in examples
  • simplify examples
  • rename state notifier example to counter_state_notifier
  • move remote_state into packages & example into examples
  • remove android & ios generated files
  • use StateNotifier instead of ValueNotifier

Fixed #

  • update working directory in github action

Removed #

  • flutter dependency remote_state package

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:remote_state/remote_state.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(title: 'RemoteState Example'),
    );
  }
}

class HomePage extends StatefulWidget {
  HomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  //1. Declare state
  RemoteState<int> counterState = RemoteState.initial();

  @override
  void initState() {
    _loadCount();

    super.initState();
  }

  //2. Load state asynchronously
  Future<void> _loadCount() async {
    setState(() {
      counterState = RemoteState.loading();
    });
    await Future.delayed(Duration(seconds: 1));
    setState(() {
      counterState = RemoteState.success(0);
    });
  }

  Future<void> _incrementCounter() async {
    await Future.delayed(Duration(milliseconds: 200));

    setState(() {
      counterState = RemoteState.success(++_count);
    });
  }

  Future<void> _decrementCounter() async {
    await Future.delayed(Duration(milliseconds: 200));
    setState(() {
      counterState = RemoteState.success(--_count);
    });
  }

  @override
  Widget build(BuildContext context) {
    var textStyle = Theme.of(context).textTheme.display1;
    final fabPadding = EdgeInsets.symmetric(vertical: 5.0);

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            //3. Render state changes
            counterState.when(
              initial: () => Text('Not loaded', style: textStyle),
              success: (value) => Text('$value', style: textStyle),
              loading: () => Text('Loading...', style: textStyle),
              error: (_, __) => Text('Error', style: textStyle),
            ),
          ],
        ),
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Padding(
            padding: fabPadding,
            child: FloatingActionButton(
              heroTag: 'inc',
              child: Icon(Icons.add),
              //4. Perform increment action
              onPressed: _incrementCounter,
            ),
          ),
          Padding(
            padding: fabPadding,
            child: FloatingActionButton(
              heroTag: 'dec',
              child: Icon(Icons.remove),
              //5. Perform decrement action
              onPressed: _decrementCounter,
            ),
          ),
        ],
      ),
    );
  }
}

int _count = 0;

Use this package as a library

1. Depend on it

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


dependencies:
  remote_state: ^1.0.0

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter pub get

Alternatively, your editor might support pub get or 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:remote_state/remote_state.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
17
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
58
Learn more about scoring.

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

  • Dart: 2.8.1
  • pana: 0.13.8-dev

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
freezed_annotation ^0.7.1 0.7.1
Transitive dependencies
collection 1.14.12
json_annotation 3.0.1
meta 1.1.8
Dev dependencies
build_runner ^1.7.2
freezed ^0.10.6
state_notifier ^0.4.0
test ^1.12.0
test_coverage ^0.4.1