Flutter Advent Calendar 2021 11日目の記事です!
私は最近からFlutter開発に携わりました。
UI開発に慣れてきたものの、API周り、状態管理などの知見は浅々の浅です。
自分の学習用として、この記事を書きます。
どなたかにとっても有益な記事となることを切に願っております。
Riverpod とは何か
noteでは神パッケージとされていました。
その中から、なるほどとなった特徴をピックアップします。
-
Flutterの状態管理パッケージであること
- value, flagを元にUIを作る仕組み = 状態を持つということ
- アプリの状態 = 多様なvalue, flag
- その状態を扱いやすく管理する仕組み = 状態管理
-
providerの改良版
- 機能
- 親Widget→子Widgetのデータ受け渡しが可能
- 親側: Provider.value()
- 子側: Provider.of()
- 親Widget→子Widgetのデータ受け渡しが可能
- 機能
-
complie-safe
- ProviderNotFoundExceptionというランタイムエラーが起こらない
- 状態にProviderで囲った配下のWidgetツリー以外からアクセスすると発生するエラー
- ProviderNotFoundExceptionというランタイムエラーが起こらない
-
実装の縛りが減った
- 同じ型で、複数の 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);
}
}