🤼‍♂️

providerと状態管理のriverpod

2022/12/20に公開

状態管理

アプリの開発では、サーバーと通信して、結果によって、ステータス変えるのはよくあるものでした。
素の状態管理を実現するにはSRPのルールに従って、InheritedWidgetで、下記の階層(ツリー)を作りました。

問題点

  • サンプルは簡単ですが、コードの複雑性はちょっと高いですね。
  • 管理対象は増えると、管理はしつらくなります。
  • ツリーの階層は深くなる場合、色なトラプル発生しやすいです。
  • InheritedWidgetと紐付いているので、MVVCとかのアーキテクチャーの基盤は作れない。

改善目標

上記問題によって、改善の目標は明確になれました。

  • 分かりやすいコードがいい。
  • 複数状態管理対象は絡まないようにしたい。
  • ツリーの階層深い場合でも簡単に対応できます。
  • 役割を分離できて、MVVCとかのアーキテクチャー基盤に向いている。

ライブラリー選定

状態管理に関して、ライブラリーはたくさんありますが、自分はよく使うライブラリーをピックアップして、進もうと思います。

provider

main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(
      MaterialApp(
        home: ChangeNotifierProvider(
          create: (context) => DemoNetwork(),
          child: const Display(),
        ),
      ),
    );

class DemoNetwork extends ChangeNotifier {
  String _result = '通信開始。。。';

  DemoNetwork() {
    Future.delayed(
      const Duration(seconds: 3),
      () {
        _result = '通信完了しました';
        notifyListeners();
      },
    );
  }

  String get result => _result;
}

class Display extends StatelessWidget {
  const Display({Key? key}) : super(key: key);
  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Consumer<DemoNetwork>(
          builder: (context, network, child) => Text(
            network.result,
            style: Theme.of(context).textTheme.headline5,
          ),
        ),
      ),
    );
  }
}

解決したもの:

  • コードがわかりやすくなりました。
  • providerが提供しているクラスで状態管理の対象を管理できます。
  • MVVCみたいな基盤は簡単に構築できます。
  • providerはデータインジェクターみたいになり、何階層のツリーの呪いから解放できている。

ですが、長い時間でprovider使っていて、デメリットはいくつあるではないかと、ちょっと自分なりにまとめてみました。

  • Widgetから解放していないようです、BLoC(「Business Logic Component」)みたいな実装はやり難いかも。
  • 実際に使うとき、パラメーターは見つからなく、エラーになったことがよくありました。
  • 複数のProviderやConsumerは提供していなく、実装者によって管理は違います。
  • 複数のProviderやConsumerの効き範囲によって、お互いに喧嘩したことがある。

riverpod

riverpodシリーズを使って、課題に向けて解決します。

main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() => runApp(
      const MaterialApp(
        home: ProviderScope(
          child: Display(),
        ),
      ),
    );

final networkProvider = StateNotifierProvider<NetworkService, String>(
  (ref) => NetworkService(),
);

class NetworkService extends StateNotifier<String> {
  NetworkService() : super('通信開始。。。');

  void updateResult() {
    Future.delayed(
      const Duration(seconds: 3),
      () {
        state = '通信完了しました';
      },
    );
  }
}

class Display extends ConsumerWidget {
  const Display({Key? key}) : super(key: key);

  
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      body: Center(
        child: Consumer(
          builder: (context, ref, _) {
            final service = ref.read(networkProvider.notifier);
            service.updateResult();
            return Text(
              ref.watch(networkProvider),
              style: Theme.of(context).textTheme.headline5,
            );
          },
        ),
      ),
    );
  }
}

providerのChangeNotifier使い方から、StateNotifierになり、Providerのクラスは豊富になり、Service層も分離できるようになりました。
真の状態管理に達したと思います。

上記のコード表したように、要望のリストクリアできたかなぁと思います。

  • 最高にわかりやすいコードではないかと思います。
  • UIの部分と分けることができて、アーキテクチャーの構造はますます作りやすくなりました。
  • 複数のProviderは分離できて、ライフサイクルはriverpodによってコントロールできて、安心感あります。

最後

いかがでしたか、
結論として、riverpodシリーズは、状態管理を完璧に行うことができるだろう。

providerといい、riverpodといい、開発者は同じ人でわかるでしょうか。
なぜ両方を運営し続いているだろうと、自分的の認識では、
providerの立場はriverpodみたいな状態管理ライブラリーの競争者ではなく、ツールです。
例えば、flutter特有のインジェクター的なものはproviderライブラリーはできます。
providerライブラリーは同じプロジェクトの中で他の状態管理ライブラリーと共存できるです、また、使えるパターンは状態管理だけとは限らないです。

アプリ開発基盤を構築する際に、自分にとっては
ProviderRiverpodは、不可欠なライブラリになっているではないかと思います。

Discussion