Last updated:
0 purchases
remote province
RemoteState #
import 'package:remote_province/remote_province.dart';
A province is a political division within a country, typically used in countries that are federal or have a federal system of government. A state, on the other hand, is a political entity that is typically sovereign and has defined geographic boundaries.
Tools for mapping data from remote sources in Dart, similar to Elm's RemoteData: https://elmprogramming.com/remote-data.html
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 #
import 'package:remote_province/remote_province.dart';
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());
}
}
copied to clipboard
main.dart #
import 'package:remote_province/remote_province.dart';
class ExampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: StateNotifierProvider<CounterNotifier, RemoteState<int>>.value(
value: CounterNotifier(),
child: HomePage(),
),
);
}
}
copied to clipboard
home.dart #
import 'package:remote_province/remote_province.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.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(),
),
),
],
),
);
}
}
copied to clipboard
maybeWhen #
import 'package:remote_province/remote_province.dart';
var result = remoteState.maybeWhen(
initial: () => "It's initial",
loading: () => "It's loading",
// Don’t provide handlers for success and error.
orElse: () => "It's either success or error", // Wildcard
);
copied to clipboard
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 #
Ryan Edge
References #
How to fix a bad user interface
Slaying a UI Antipattern with Web Components (and TypeScript)
How Elm Slays a UI Antipattern
Slaying a UI Antipattern with Angular
Slaying a UI Antipattern with Flow
Slaying a UI Antipattern in React
Slaying a UI Antipattern in Fantasyland
Dev #
1. Fetching Dependencies #
dart pub get
copied to clipboard
2. Running Tests #
dart test
copied to clipboard
3. Code Generation (freezed, json_serializable, etc.) #
dart pub run build_runner build
copied to clipboard
For continuous rebuilding as you change the code:
dart pub run build_runner watch
copied to clipboard
4. Code Analysis #
dart analyze
copied to clipboard
5. Formatting Code #
dart format lib
copied to clipboard
6. Running the Package/Application #
dart run
copied to clipboard
7. Compiling the Package/Application #
dart compile exe <dart_file>
copied to clipboard
8. Publishing the Package #
dart pub publish
copied to clipboard
9. Release #
./scripts/prepare_release.sh
copied to clipboard
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.