備忘録です。
StateNotifierとは
Flutterの状態管理ライブラリで、state_notifier
というパッケージをベースにしています。flutter_state_notifier
は、Flutterアプリケーションの状態管理を行うための簡単な方法を提供します。state_notifier
では、状態変更の通知を受け取り、状態を更新し、状態の変更をリッスンするウィジェットを簡単に作成できます。
導入方法
flutter pub add flutter_state_notifier
flutter_state_notifier install | Flutter package
Flutter bindings for state_notifier, such as StateNotifierProvider and StateNotifierBuilder
例文
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_state_notifier/flutter_state_notifier.dart';
import 'package:provider/provider.dart';
import 'package:untitled/my_state_notifier.dart';
// これは、StateNotifier + プロバイダーを使用して再実装されたデフォルトのカウンターの例です
// カウンターが変更されるたびにコンソールに表示されます
// 状態の変化もアニメーション化されます。
// 本番環境で行います。
// これは、カスタム MyStateNotifier が Flutter に依存しないことを示しています。
// ただし、プロバイダーを読み取って、Flutter アプリで通常どおり使用できます。
void main() {
runApp(
MultiProvider(
providers: [
StateNotifierProvider<MyStateNotifier, MyState>(
create: (_) => MyStateNotifier(),
// MyState をオーバーライドしてアニメーション化する
builder: (context, child) {
return TweenAnimationBuilder<MyState>(
duration: const Duration(milliseconds: 500),
tween: MyStateTween(end: context.watch<MyState>()),
builder: (context, state, _) {
return Provider.value(value: state, child: child);
},
);
},
),
],
child: const MyApp(),
),
);
}
/// [MyStateTween].
///
/// これにより、[MyState.count] に [IntTween] が適用されます。
class MyStateTween extends Tween<MyState> {
MyStateTween({super.begin, super.end});
@override
MyState lerp(double t) {
final countTween = IntTween(begin: begin?.count, end: end?.count);
// Tween the count
return MyState(
count: countTween.lerp(t),
);
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Counter example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
context.select((MyState value) {
return value.count;
}).toString(),
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.watch<MyStateNotifier>().increment();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
// my_state_notifier.dart
import 'package:state_notifier/state_notifier.dart';
// StateNotifier を使用したカウンターの例
class MyState {
MyState({required this.count});
final int count;
}
class MyStateNotifier extends StateNotifier<MyState> {
MyStateNotifier() : super(MyState(count: 0));
void increment() {
state = MyState(count: state.count + 1000);
}
}
より詳しいコードの説明(自己解釈含む)
StateNotifierProvider
void main() {
runApp(
MultiProvider(
providers: [
StateNotifierProvider<MyStateNotifier, MyState>(
create: (_) => MyStateNotifier(),
// MyState をオーバーライドしてアニメーション化する
builder: (context, child) {
return TweenAnimationBuilder<MyState>(
duration: const Duration(milliseconds: 500),
tween: MyStateTween(end: context.watch<MyState>()),
builder: (context, state, _) {
return Provider.value(value: state, child: child);
},
);
},
),
],
child: const MyApp(),
),
);
}
MultiProviderの中にprovidersをセットします。
StateNotifierProvider<Notifier, State> (
create: (_) => Notifier(),
),
基本形はこれのようです。
Notifierを作成するのがここ、という認識であっていると思います。
なので、runApp時点でするのがマストかと。
Tween
これは後日記事にします。アニメーションのなんらかのようです。応用として認識しておきます。
context.select
Text(
context.select((MyState value) {
return value.count;
}).toString(),
style: Theme.of(context).textTheme.headlineMedium,
),
参照したい値を見ているように感じ取れます、
context.selectで取得する値を選択し、なんらかの魔法でvalueに値が入るようです。
MyStateで定義した変数の値が取得できます。
context.watch
floatingActionButton: FloatingActionButton(
onPressed: () {
context.watch<MyStateNotifier>().increment();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
イベント発生時はcontext.watchを使うようです。
今回は押下時に数値を送る必要はないですが、送る必要がある場合上記の例ではincrement()の括弧の中に書けば良さそう。
MyStateNotifierのincrementを引数を受け取るように変える必要はありますが。
StateNotifier<StateClass>
class MyState {
MyState({required this.count});
final int count;
}
class MyStateNotifier extends StateNotifier<MyState> {
MyStateNotifier() : super(MyState(count: 0));
void increment() {
state = MyState(count: state.count + 1000);
}
}
上記の例ではincrementが呼ばれたときにstateをMyStateクラスのインスタンスで上書きしているように見えます。