Snippets

A collection of copy-pasteable solutions to common development problems, ReArch style. Everything shown here is one way to solve the corresponding problem; there are likely other equally-valid solutions as well.

If you have any suggestions of snippets to add, please open a new issue here!

Sign In Flow

And how to relay the state of an asynchronous action in the UI while it occurs via "mutations."

/// Provides a [Random].
// NOTE: encapsulating this enables easy implementation switching later on.
Random randomCapsule(CapsuleHandle use) => Random();

/// Provides a function that signs the user in.
Future<void> Function(String, String) signInAction(CapsuleHandle use) {
  // NOTE: we have this `use` call up top (before it is actually needed)
  // because you must never use a CapsuleHandle across an asynchronous gap
  // (which includes in a function callback (below) or after an await).
  // But don't worry, ReArch will give you a reminder if you ever forget.
  final random = use(randomCapsule);

  return (email, password) {
    // Dummy implementation that is fallible
    return Future.delayed(const Duration(seconds: 1), () {
      if (random.nextBool()) throw Exception('Sign in failed');
    });
  };
}

@rearchWidget
Widget signInForm(WidgetHandle use) {
  final (email, setEmail) = use.state('');
  final (password, setPassword) = use.state('');

  final (state: signInState, mutate: mutateSignIn, clear: _) =
      use.mutation<void>();
  final rawSignIn = use(signInAction);
  void signIn() => mutateSignIn(rawSignIn(email, password));

  return Column(
    children: [
      TextField(onChanged: setEmail),
      TextField(onChanged: setPassword),
      ElevatedButton.icon(
        icon: const Icon(Icons.person_rounded),
        onPressed: signInState is AsyncLoading ? null : signIn,
        label: const Text('Sign In'),
      ),
      switch (signInState) {
        null => const SizedBox.shrink(), // no sign-in attempted yet
        AsyncData<void>() => const Text('Signed In'),
        AsyncLoading<void>() => const CircularProgressIndicator(),
        AsyncError<void>(:final error) => Text('$error'),
      },
    ],
  );
}