Tech Sheets

RiverpodでStateNotifierを使って状態を扱う

最終更新日:2024-08-25

RiverpodのStateNotifierを使うと状態を扱うことができます。Androidで言うところのViewModelっぽいものを作るときに便利そうです。

StateNotifierを継承したクラスを作る

例としてログイン画面の状態を扱う LoginViewModel を作ってみます。状態部分は sealed class にしてみます。

class LoginViewModel extends StateNotifier<UiState> {
  final AccountRepository accountRepository;

  LoginViewModel({
    required this.accountRepository,
  }) : super(const Idle());

  void login(String email, String password) async {
    state = const InProgress();
    try {
      final account = await accountRepository.login(email, password);
      state = Success(account: account);
    } catch (e) {
      state = Error(e: e);
    }
  }
}

// 状態を表すクラス
sealed class UiState {
  const UiState();
}

class Idle extends UiState {
  const Idle();
}

class InProgress extends UiState {
  const InProgress();
}

class Success extends UiState {
  final Account account;
  Success({required this.account});
}

class Error extends UiState {
  final Object e;
  Error({required this.e});
}

StateNotifierProviderを作る

StateNotifierProvider を使って、LoginViewModel の作り方を指示します。 WidgetRef があるので依存するオブジェクトを read() で取得することもできます。

final _viewModelProvider = StateNotifierProvider<LoginViewModel, UiState>((ref) {
  final accountRepository = ref.read(accountRepositoryProvider);
  return LoginViewModel(
    accountRepository: accountRepository,
  );
});

watchで状態を取得する

ConsumerWidgetbuild() の中で ref.watch() を使うと状態を取得・監視することができます。状態が変化したタイミングで再度 build() が実行されます。

class LoginScreen extends ConsumerWidget {
  @override
  void build(BuildContext context, WidgetRef ref) {
    final uiState = ref.watch(_viewModelProvider);
    return switch (uiState) {
    }
  }
}

StateNotifierを取得する

ref.read(provider.notifier) で、 StateNotifier を取得することができます。状態更新のためのメソッドを呼びたいときに使います。

class LoginScreen extends ConsumerWidget {
  @override
  void build(BuildContext context, WidgetRef ref) {
    // 取得する
    final viewModel = ref.read(_viewModelProvider.notifier);
    ...
    ElevatedButton(onPressed: () {
      viewModel.login();
    }, child: const Text("Login"));
  }
}

一覧に戻る