LoginSignup
6
3

More than 1 year has passed since last update.

Riverpod, StateNotifier, freezedの簡単な説明

Last updated at Posted at 2021-12-11

Flutter Advent Calendar 2021 11日目の記事です!

私は最近からFlutter開発に携わりました。
UI開発に慣れてきたものの、API周り、状態管理などの知見は浅々の浅です。
自分の学習用として、この記事を書きます。
どなたかにとっても有益な記事となることを切に願っております。

Riverpod とは何か

noteでは神パッケージとされていました。
その中から、なるほどとなった特徴をピックアップします。

  • Flutterの状態管理パッケージであること
    • value, flagを元にUIを作る仕組み = 状態を持つということ
    • アプリの状態 = 多様なvalue, flag
    • その状態を扱いやすく管理する仕組み = 状態管理
  • providerの改良版
    • 機能
      • 親Widget→子Widgetのデータ受け渡しが可能
        • 親側: Provider.value()
        • 子側: Provider.of()
  • complie-safe
    • ProviderNotFoundExceptionというランタイムエラーが起こらない
      • 状態にProviderで囲った配下のWidgetツリー以外からアクセスすると発生するエラー
  • 実装の縛りが減った
    • 同じ型で、複数の Provider を使用可能
    • 使われなくなった状態を自動で破棄 (dispose)
    • computed states を実装可能
    • Provider を private スコープで宣言可能
  • Dart言語のみを使っているので、Flutter に依存しない
  • Providerのグローバル定義が可能

サンプルコード

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

void main() {
 runApp(ProviderScope(child: MyApp()));
}

final counterProvider = StateProvider<int>((ref) => 0);

class MyApp extends HookConsumerWidget {
 @override
 Widget build(BuildContext context, WidgetRef ref) {
   final int count = ref.watch(counterProvider);
   return MaterialApp(
     home: Scaffold(
       body: Center(child: Text('$count')),
       floatingActionButton: FloatingActionButton(
         onPressed: () {
           ref.read(counterProvider.notifier).state++;
         },
       ),
     ),
   );
 }
}

StateNotifier とは何か

  • 状態の変更をリスナーに通知するために使う
  • notifylisteners関数の改良版
    • 状態が変更される度にcallしなければならなかった
  • 単一Stateを保持
    • 複数のStateを保持したい場合はオブジェクトを作成

サンプルコード

class CounterStateNotifier extends StateNotifier<int> {
  CounterStateNotifier(): super(0);

  void increment() {
    state++;
  }
}

freezed とは何か

  • StateNotifierで利用するState用のクラスをimmutableにした時の冗長さを解消するもの
    • immutable = 不変

サンプルコード(freezed使用なし)

@immutable
class CounterState {
  CounterState({
    this.count = 0,
    this.isEnabled = true,
  });
  final int count;
  final bool isEnabled;
}

// 変更時
class CounterStateNotifier extends StateNotifier<CounterState> {
  CounterStateNotifier(): super(CounterStateNotifier());
  void increment() {
    state = CounterState(
      count: state.count + 1,
      isEnabled: state.isEnabled,
    );
  }
  void disableCounter() {
    state = CounterState(
      count: state.count,
      isEnabled: false,
    );
  }
}

サンプルコード(freezed使用あり)

@freezed
class CounterState with $_CounterState {
  factory CounterState({
    int? count,
    bool? isEnabled,
  }) = _CounterState;
}

// 変更時
class CounterStateNotifier extends StateNotifier<CounterState> {
  CounterStateNotifier(): super(CounterState(count: 0, isEnabled: true));
  void increment() {
    state = state.copyWith(count: state.count + 1);
  }
  void disableCounter() {
    state = state.copyWith(isEnabled: false);
  }
}
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3