remote_state 1.0.0 remote_state: ^1.0.0 copied to clipboard
Tools for mapping data from remote sources in Dart, similar to Elm's RemoteData.
RemoteState #
Tools for mapping data from remote sources in Dart, similar to Elm's RemoteData: https://elmprogramming.com/remote-data.html
Package | Pub |
---|---|
remote_state |
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.