Giter VIP home page Giter VIP logo

remote_state's Introduction

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

Package Pub
remote_state pub 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

class CounterNotifier extends StateNotifier<RemoteState<int>> {
  var _counterClient = CounterClient();

  CounterNotifier() : super(RemoteState.initial()) {
    getCount();
  }

  getCount() async {
    state = RemoteState.loading();

    state = await RemoteState.guard(() => _counterClient.getCount());
  }

  increment() async {
    state = await RemoteState.guard(() => _counterClient.increment());
  }

  decrement() async {
    state = await RemoteState.guard(() => _counterClient.decrement());
  }
}
class ExampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: StateNotifierProvider<CounterNotifier, RemoteState<int>>.value(
        value: CounterNotifier(),
        child: HomePage(),
      ),
    );
  }
}
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.headline4;
    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.

RemoteState.guard

RemoteState.guard is a static function that converts a Future to RemoteState. It will emit RemoteState.error if the future fails or RemoteState.success if the future completes.

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.

State Predicates

isInitial

The isInitial predicate returns true if we haven't asked for data yet.

isLoading

The isLoading predicate returns true if we're loading.

isSuccess

The isSuccess predicate returns true if we've successfully loaded some data.

isError

The isError predicate returns true if we've failed to load some data.

Maintainers

References

remote_state's People

Contributors

chimon2000 avatar ryan-sf avatar

Stargazers

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

Watchers

 avatar  avatar

remote_state's Issues

[Feature]: Error enhancements

Is your feature request related to a problem? Please describe.
Would like the ability to pass more dynamic objects on error state.

Describe the solution you'd like
Make error dynamic instead of a string, and allow stack trace as an optional parameter.

Describe alternatives you've considered
n/a

Additional context
n/a

[Bug]: RemoteState.Guard will return success for a null value in release mode and error in debug mode

Describe the bug
If you pass a future to RemoteState.Guard that returns a null value, in release mode it'll go through as a success but in debug mode the assert that the value can't be null will be prevent it and an error state is returned.

Expected behavior
An error state should be returned for for both instances or a different state might need to be addressed.

Screenshots
N/A

Smartphone (please complete the following information):

  • No device specific

[Feature]: Remove Empty state

Is your feature request related to a problem? Please describe.
To make this library more in line with pure RemoteData implementations, we should remove the Empty state, which is not reflective of the state of an asynchronous operation but rather the state of the data itself.

Describe the solution you'd like
Deprecate Empty state, and update examples. Will be removed in 1.0 version.

Describe alternatives you've considered
An alternative would be to leave this state in, however that would mean that the data model is polluted with UI concerns.

Additional context

Issues with related scope creep concerns:

devexperts/remote-data-ts#22 (comment)
krisajenkins/remotedata#9

[Feature]: Add flutter_hooks example

Is your feature request related to a problem? Please describe.

It would be nice to have an example of remote_state working with flutter_hooks.

Describe the solution you'd like

Implement a flutter_hooks version of the counter example app.

Describe alternatives you've considered

n/a

Additional context

n/a

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.