🤿

riverpodのfamilyでモーダルに値を渡す

2023/12/20に公開

Overview

riverpodのfamilyを使ったユースケースについて今回ご紹介しようと思います。とはいってもあまり参考にならないかもしれないです💦

なぜ記事を書こうかと思ったかというと、業務で他のページやモーダルに値を渡すときに、familyを使うとのことで試してみました。他にも方法はあります。

https://riverpod.dev/ja/docs/concepts/modifiers/family

summary

今回はモーダルを表示して、そこに値を渡してみましょう!

プロバイダーを使ってグローバルに値を渡すのをやってみようと思います。単純すぎて面白くないと思いますが、紹介している例がないな〜と思って記事を書いてます(^^;;

Listを使った方法も紹介してますが、これは仕事で似たようなことをしたのでダミーのデータですが作ってみました。

使用例
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// Userクラス
class User {
  final int id = 34;
  final String name = 'Jboy';
  final String email = 'Jboy@co.com';
}

// idをfamilyでモーダルに渡す
final idProvider = Provider.family<int, int>((ref, id) => id);

// UserクラスをListとfamilyでモーダルに渡す
final userProvider = Provider.family<User, int>((ref, id) {
  final user = User();
  return user;
});

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    // Userを格納したList
    final users = <User>[];
    // Userを10個作成してListに格納
    for (var i = 0; i < 5; i++) {
      users.add(User());
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text('Family Example'),
      ),
      body: Column(
        children: [
          Center(
            child: ElevatedButton(
              onPressed: () {
                final id = ref.read(idProvider(1));
                showModalBottomSheet(
                  context: context,
                  builder: (context) {
                    return _Modal(idParameter: id);
                  },
                );
              },
              child: const Text('Show Modal'),
            ),
          ),
          const SizedBox(height: 20),
          Expanded(
            child: ListView.builder(
              itemCount: users.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(users[index].name),
                  subtitle: Text(users[index].email),
                  onTap: () {
                    // indexのidをモーダルに渡す
                    final id = ref.read(idProvider(users[index].id));
                    showModalBottomSheet(
                      context: context,
                      builder: (context) {
                        // indexのidをモーダルに渡す
                        return _Modal(idParameter: id);
                      },
                    );
                  },
                );
              },
            ),
          )
        ],
      ),
    );
  }
}

// モーダルのコンポーネント
class _Modal extends ConsumerWidget {
  const _Modal({Key? key, required this.idParameter}) : super(key: key);

  final int idParameter;

  
  Widget build(BuildContext context, WidgetRef ref) {
    final id = ref.watch(idProvider(idParameter));
    return Center(
      child: Text('モーダルに渡されたID: $id', style: const TextStyle(fontSize: 30)),
    );
  }
}

// ignore: unused_element
class _UserModal extends ConsumerWidget {
  const _UserModal({Key? key, required this.idParameter}) : super(key: key);

  final int idParameter;

  
  Widget build(BuildContext context, WidgetRef ref) {
    final user = ref.watch(userProvider(idParameter));
    return Center(
      child: Column(
        children: [
          Text('モーダルに渡されたID: ${user.id}', style: const TextStyle(fontSize: 30)),
          Text('モーダルに渡されたUser: ${user.name}',
              style: const TextStyle(fontSize: 30)),
        ],
      ),
    );
  }
}

こんな感じで表示されます:


riverpod generatorを使用した例だとこのようになります:

使用例
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'family_generator.g.dart';


int idValue(IdValueRef ref, int id) {
  return id;
}

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Family Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            final id = ref.read(idValueProvider(1));
            showModalBottomSheet(
              context: context,
              builder: (context) {
                return _Modal(idParameter: id);
              },
            );
          },
          child: const Text('Show Modal'),
        ),
      ),
    );
  }
}

// モーダルのコンポーネント
class _Modal extends ConsumerWidget {
  const _Modal({Key? key, required this.idParameter}) : super(key: key);

  final int idParameter;

  
  Widget build(BuildContext context, WidgetRef ref) {
    return Container(
      height: 300,
      color: Colors.white,
      child: Center(
        child: Text(
          'id: $idParameter',
          style: Theme.of(context).textTheme.headline4,
        ),
      ),
    );
  }
}

実行するときは、main.dartはこのように書いてください:

import 'package:family_app/view/family_example.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const FamilyExample(),
    );
  }
}

thoughts

他にもモーダルや次のページに値を渡す方法はありますが、割と簡単な方のfamilyを使ってみました。でもこれはデメリットもあって、複数のページでプロバイダーを使っているときに、後でfamily追加したら、引数を渡すのが必須になるので、そこが欠点ですね。

他の方法でやっても何かしら、デメリットはあるんですけどね...

Discussion