【Flutter】StateNotifierについて

Flutter

備忘録です。

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クラスのインスタンスで上書きしているように見えます。

Buy me a coffee!

Flutterアプリ開発
シェアする
sogaをフォローする
タイトルとURLをコピーしました