first commit
This commit is contained in:
commit
faf67cc6d8
148 changed files with 6580 additions and 0 deletions
97
lib/utils/command.dart
Normal file
97
lib/utils/command.dart
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'result.dart';
|
||||
|
||||
typedef CommandAction0<T> = Future<Result<T>> Function();
|
||||
typedef CommandAction1<T, A> = Future<Result<T>> Function(A);
|
||||
|
||||
/// Facilitates interaction with a ViewModel.
|
||||
///
|
||||
/// Encapsulates an action,
|
||||
/// exposes its running and error states,
|
||||
/// and ensures that it can't be launched again until it finishes.
|
||||
///
|
||||
/// Use [Command0] for actions without arguments.
|
||||
/// Use [Command1] for actions with one argument.
|
||||
///
|
||||
/// Actions must return a [Result].
|
||||
///
|
||||
/// Consume the action result by listening to changes,
|
||||
/// then call to [clearResult] when the state is consumed.
|
||||
abstract class Command<T> extends ChangeNotifier {
|
||||
Command();
|
||||
|
||||
bool _running = false;
|
||||
|
||||
/// True when the action is running.
|
||||
bool get running => _running;
|
||||
|
||||
Result<T>? _result;
|
||||
|
||||
/// true if action completed with error
|
||||
bool get error => _result is Error;
|
||||
|
||||
/// true if action completed successfully
|
||||
bool get completed => _result is Ok;
|
||||
|
||||
/// Get last action result
|
||||
Result? get result => _result;
|
||||
|
||||
/// Clear last action result
|
||||
void clearResult() {
|
||||
_result = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Internal execute implementation
|
||||
Future<void> _execute(CommandAction0<T> action) async {
|
||||
// Ensure the action can't launch multiple times.
|
||||
// e.g. avoid multiple taps on button
|
||||
if (_running) return;
|
||||
|
||||
// Notify listeners.
|
||||
// e.g. button shows loading state
|
||||
_running = true;
|
||||
_result = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_result = await action();
|
||||
} finally {
|
||||
_running = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [Command] without arguments.
|
||||
/// Takes a [CommandAction0] as action.
|
||||
class Command0<T> extends Command<T> {
|
||||
Command0(this._action);
|
||||
|
||||
final CommandAction0<T> _action;
|
||||
|
||||
/// Executes the action.
|
||||
Future<void> execute() async {
|
||||
await _execute(_action);
|
||||
}
|
||||
}
|
||||
|
||||
/// [Command] with one argument.
|
||||
/// Takes a [CommandAction1] as action.
|
||||
class Command1<T, A> extends Command<T> {
|
||||
Command1(this._action);
|
||||
|
||||
final CommandAction1<T, A> _action;
|
||||
|
||||
/// Executes the action with the argument.
|
||||
Future<void> execute(A argument) async {
|
||||
await _execute(() => _action(argument));
|
||||
}
|
||||
}
|
||||
48
lib/utils/result.dart
Normal file
48
lib/utils/result.dart
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
/// Utility class to wrap result data
|
||||
///
|
||||
/// Evaluate the result using a switch statement:
|
||||
/// ```dart
|
||||
/// switch (result) {
|
||||
/// case Ok(): {
|
||||
/// print(result.value);
|
||||
/// }
|
||||
/// case Error(): {
|
||||
/// print(result.error);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
sealed class Result<T> {
|
||||
const Result();
|
||||
|
||||
/// Creates a successful [Result], completed with the specified [value].
|
||||
const factory Result.ok(T value) = Ok._;
|
||||
|
||||
/// Creates an error [Result], completed with the specified [error].
|
||||
const factory Result.error(Exception error) = Error._;
|
||||
}
|
||||
|
||||
/// Subclass of Result for values
|
||||
final class Ok<T> extends Result<T> {
|
||||
const Ok._(this.value);
|
||||
|
||||
/// Returned value in result
|
||||
final T value;
|
||||
|
||||
@override
|
||||
String toString() => 'Result<$T>.ok($value)';
|
||||
}
|
||||
|
||||
/// Subclass of Result for errors
|
||||
final class Error<T> extends Result<T> {
|
||||
const Error._(this.error);
|
||||
|
||||
/// Returned error in result
|
||||
final Exception error;
|
||||
|
||||
@override
|
||||
String toString() => 'Result<$T>.error($error)';
|
||||
}
|
||||
Reference in a new issue