👁️‍🗨️

Riverpodはやはりあったほうがいいと思った

2025/02/19に公開

流行りだけで使われているのか?

こんにちわJboyです。最近お仕事でRiverpodを使わずにデザインパターンだけで頑張るプロジェクトをやっております。

しかし他の案件や企業の募集要項を見ると、Riverpodを使っています。なぜ使うのだろうか考えてみる。これはおそらく昔からあるデザインパターンをDartに取り入れるのは難しいからかもしれない?

いっしよに仕事したことある人で、Dartのいけてないところがあると言われたことがありました😅
rxDart使った時かな。

これは仮ですが。。。 
nullを入れないとだめな機能があった?
他のを使いたいらしい?

void xxx() {
  xxx.add(null);
}

他には、クラスにコンストラクタ引数でインスタンス化したクラスをたくさん渡すロジックがあった。ここはRiverpodのRefを使っても引数を渡す処理を書くとそんなに変わらない気がしたけど、渡すだけなら、これでもいいが、どこかでwatch()してないと反応しないことがある。

class YYY {
  APIxxxx api;
  HTTP htt;
  ....

  ({
  // ここに引数がたくさんある。
});
}

FutureBuilderは仕事で使ったことはあまりないですが、書くコードが多くてみづらくなることがあった。ここは悩ましい。

https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

class FutureBuilderExample extends StatefulWidget {
  const FutureBuilderExample({super.key});

  
  State<FutureBuilderExample> createState() => _FutureBuilderExampleState();
}

class _FutureBuilderExampleState extends State<FutureBuilderExample> {
  final Future<String> _calculation = Future<String>.delayed(
    const Duration(seconds: 2),
    () => 'Data Loaded',
  );

  
  Widget build(BuildContext context) {
    return DefaultTextStyle(
      style: Theme.of(context).textTheme.displayMedium!,
      textAlign: TextAlign.center,
      child: FutureBuilder<String>(
        future: _calculation, // a previously-obtained Future<String> or null
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          List<Widget> children;
          if (snapshot.hasData) {
            children = <Widget>[
              const Icon(Icons.check_circle_outline, color: Colors.green, size: 60),
              Padding(
                padding: const EdgeInsets.only(top: 16),
                child: Text('Result: ${snapshot.data}'),
              ),
            ];
          } else if (snapshot.hasError) {
            children = <Widget>[
              const Icon(Icons.error_outline, color: Colors.red, size: 60),
              Padding(
                padding: const EdgeInsets.only(top: 16),
                child: Text('Error: ${snapshot.error}'),
              ),
            ];
          } else {
            children = const <Widget>[
              SizedBox(width: 60, height: 60, child: CircularProgressIndicator()),
              Padding(padding: EdgeInsets.only(top: 16), child: Text('Awaiting result...')),
            ];
          }
          return Center(
            child: Column(mainAxisAlignment: MainAxisAlignment.center, children: children),
          );
        },
      ),
    );
  }
}

昔からこの悩みを解決して欲しいので、StreamBuilder使ったときでも同じでしたが、もう少しコードを短くできないか?
読みやすくできないか???

whenを昔は使っていた。今だとswitchですかね。使うだけで全然感覚が違いましたね。個人的な感想ですが、読みやすい。Signals.dartのifの処理も短い方なので標準のモジュールを使うよりは見やすいなーと思った。

https://riverpod.dev/ja/docs/providers/notifier_provider


class TodoListView extends ConsumerWidget {
  const TodoListView({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    // rebuild the widget when the todo list changes
    final asyncTodos = ref.watch(asyncTodosProvider);

    // Let's render the todos in a scrollable list view
    return switch (asyncTodos) {
      AsyncData(:final value) => ListView(
          children: [
            for (final todo in value)
              CheckboxListTile(
                value: todo.completed,
                // When tapping on the todo, change its completed status
                onChanged: (value) {
                  ref.read(asyncTodosProvider.notifier).toggle(todo.id);
                },
                title: Text(todo.description),
              ),
          ],
        ),
      AsyncError(:final error) => Text('Error: $error'),
      _ => const Center(child: CircularProgressIndicator()),
    };
  }
}

私だと、Firestore/Supabase/REST APIを使うことが多かったので、AsyncValueとwhenやswitchを使えた方がありがたいなーと思った。

他にもあると嬉しかったのは、Notifier + freezedですね。いわゆるエンティティ(入れ物)に値を保持させて他のページで共有することができる機能が仕事で便利だった。
どうしてもReactのpropsのように、ページ1〜5までまたいで値をコンストラクタ引数で渡すのは大変でした😅

できればパッケージは使いたい。大半の企業は🪣バケツリレーなるものをしたくないから使ってると思われます。

Firestoreを使っていた頃だとなんでもProviderで管理していたことありましたが、これに関してはProviderの方が扱いやすい印象でした。

保守を考えるなら、クラスにできるところは分けるべきだと思います。

withConverterをよく使う文化の職場にいたので私の個人的な感覚なのですが、これの方が使いやすいなと思いFirestoreを使う場合は、Riverpodでないとやりたくないなーと思いました。

副業でStreamBuilderやGetX使ったことあるのですが、コードが見ずらかった😅
特定のページで、idを引数で渡せるfamilyが使えて便利な場面もあったので、Riverpod使えるなら使いたいと思った。

最近昔と書き方変えましたが、これを作っておくと色々と使い回しがきいて便利な道具だなと思うこともありました。最近は大手ばかりで、API通信ばかりなのでFirestoreの知識が個人開発でしか役に立たなくなりました🫠

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_firestore/domain/cart.dart';
part 'cart_stream.g.dart';

(keepAlive: true)
FirebaseFirestore firebaseFirestore(Ref ref) => FirebaseFirestore.instance;

(keepAlive: true)
CollectionReference<CartState> cartCollection(Ref ref) {
  return ref.read(firebaseFirestoreProvider).collection('cart').withConverter(
    fromFirestore: (snapshot, _) {
      final data = snapshot.data()!;
      return CartState.fromJson({
        'id': snapshot.id,
        ...data,
      });
    },
    toFirestore: (value, _) {
      final json = value.toJson();
      json.remove('id');
      return json;
    },
  );
}


Stream<List<CartState>> cartStream(Ref ref) async* {
  final collection = ref.read(cartCollectionProvider);
  yield* collection.snapshots().map((snapshot) => snapshot.docs.map((doc) => doc.data()).toList());
}

まとめ

Riverpodは使えた方が良い。好みはあるかもしれないが日本では使う企業は多くremiさんという個人が作っているものですが、信用があるのか多く使われている。

https://x.com/remi_rousselet

Xの投稿で、国によってはRefの使い方のせいなのかわからないですが、スパゲッティコードを作っていると批判されることもありました。
外国の人でもやるんですね😅

まあBlocとかSignals.dartもあるのでそちらも検討すればいいのではと思いました。

https://bloclibrary.dev/ja/

https://dartsignals.dev/

私もRiverpod好きなので趣味で📕本作ったりしてます。Riverpodは友達。setState()を使いすぎないように、リアクティブプログラミングするのはハードルが高いので、Riverpod使うことはまだ簡単なように感じました。

https://zenn.dev/joo_hashi/books/dac5f16428261c

Discussion