😇

whenをネストしたくないから、switch & recordを使った! 

2024/06/14に公開

🤔やってみたいこと

riverpodを使って、プロバイダーを2個 ref.watch  + when するとデータを2種類 Widget に渡せるが、 これだとなんかカッコ悪い😅

enum Selected { edit, delete }

class FeedPage extends HookConsumerWidget {
  const FeedPage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final selectedMenu = useState<Selected>(Selected.edit);
    final tUser = ref.watch(tUserListProvider);
    final tTask = ref.watch(tTaskListProvider);
    return Scaffold(
      appBar: AppBar(),
      backgroundColor: AppColor.grey,
      floatingActionButton: FloatingActionButton(
        backgroundColor: AppColor.navy,
        foregroundColor: AppColor.white,
        // ボタンを丸くする
        shape: const CircleBorder(),
        onPressed: () async {
          context.goNamed(RouterPath.CREATE_TASK);
        },
        child: const Icon(Icons.add),
      ),
body: tUser.when(
        data: (tUser) => tTask.when(
          data: (tTask) => ListView.builder(
            itemCount: min(tUser.length, tTask.length),
            itemBuilder: (context, index) {
              return Card(
                child: Material(
                  color: const Color(0xFFFFFFFF),
                  child: ListTile(
                    onTap: () {
                      context.goNamed(
                        RouterPath.FEED_DETAIL,
                        extra: tTask[index],
                      );
                    },
                    title: Text(tUser[index].job_id),
                    subtitle: Text(tTask[index].task_name),
                    trailing: PopupMenuButton<Selected>(
                      onSelected: (result) {
                        selectedMenu.value = result;
                      },
                      itemBuilder: (context) => <PopupMenuEntry<Selected>>[
                        PopupMenuItem<Selected>(
                          value: Selected.edit,
                          child: const Text('編集'),
                          onTap: () {
                            /// TODO: 編集の処理
                          },
                        ),
                        PopupMenuItem<Selected>(
                          value: Selected.delete,
                          child: const Text('削除'),
                          onTap: () {
                            /// TODO: 削除の処理
                          },
                        ),
                      ],
                    ),
                  ),
                ),
              );
            },
          ),
          loading: () => const CircularProgressIndicator(),
          error: (error, stackTrace) => Text('Error: $error'),
        ),
        loading: () => const CircularProgressIndicator(),
        error: (error, stackTrace) => Text('Error: $error'),
      ),

なんとかならないのか....
ネストするのやめたい😅

🚀やってみたこと

Dart3.0から登場した Records を使うと実現できるらしい???
https://dart.dev/language/records

SwiftのTupleみたいに、複数の戻り値を返すことができます。
https://swift.codelly.dev/guide/基本の型/Tuple型.html

こんな感じでね

多分、recordだろう?

// Dart imports:
import 'dart:math';

// Flutter imports:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

// Package imports:
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

// Project imports:
import '../../core/theme/app_color.dart';
import '../../infrastructure/supabase_provider/t_task_provider.dart';
import '../../infrastructure/supabase_provider/t_user_provider.dart';
import '../router/router_path.dart';

enum Selected { edit, delete }

class FeedPage extends HookConsumerWidget {
  const FeedPage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final selectedMenu = useState<Selected>(Selected.edit);
    final tUser = ref.watch(tUserListProvider);
    final tTask = ref.watch(tTaskListProvider);
    return Scaffold(
      appBar: AppBar(),
      backgroundColor: AppColor.grey,
      floatingActionButton: FloatingActionButton(
        backgroundColor: AppColor.navy,
        foregroundColor: AppColor.white,
        shape: const CircleBorder(),
        onPressed: () async {
          context.goNamed(RouterPath.CREATE_TASK);
        },
        child: const Icon(Icons.add),
      ),
      body: switch((tTask, tUser)) {
        (AsyncData _, AsyncData _) => ListView.builder(
          itemCount: min(tUser.asData!.value.length, tTask.asData!.value.length),
          itemBuilder: (context, index) {
            return Card(
              child: Material(
                color: const Color(0xFFFFFFFF),
                child: ListTile(
                  onTap: () {
                    context.goNamed(
                      RouterPath.FEED_DETAIL,
                      extra: tTask.asData!.value[index],
                    );
                  },
                  title: Text(tUser.asData!.value[index].job_id),
                  subtitle: Text(tTask.asData!.value[index].task_name),
                  trailing: PopupMenuButton<Selected>(
                    onSelected: (result) {
                      selectedMenu.value = result;
                    },
                    itemBuilder: (context) => <PopupMenuEntry<Selected>>[
                      PopupMenuItem<Selected>(
                        value: Selected.edit,
                        child: const Text('編集'),
                        onTap: () {
                        },
                      ),
                      PopupMenuItem<Selected>(
                        value: Selected.delete,
                        child: const Text('削除'),
                        onTap: () {
                        },
                      ),
                    ],
                ),
              ),
              ),);
          },
        ),
        (AsyncLoading _, AsyncLoading _) => const CircularProgressIndicator(),
        (AsyncError _, AsyncError _) => const Text('Error'),
         (_, _) => const Text('No Data'),
      },
    );
  }
}

Riverpodのref.watchで、2つのプロバイダーを監視する。これをrecordに引数が2個いるので、渡す。
(AsyncData _, AsyncData _) _を2個つけろと警告が出てきたのでつけたらいけた🙌

コードが変化もしれないが、これしか思いつかなかった😅

 body: switch((tTask, tUser)) {
        (AsyncData _, AsyncData _) => ListView.builder(

AsyncLoadingAsyncErrorにも_を2個つけろと警告が出てきたのでつけたらいけた🙌

((AsyncLoading _, AsyncLoading _) => const CircularProgressIndicator(),
        (AsyncError _, AsyncError _) => const Text('Error'), _, AsyncLoading _) => const CircularProgressIndicator(),
        (AsyncError _, AsyncError _) => const Text('Error'),

🙂最後に

結構頑張りました😅
以前やってもなぜできなかったのか....

Discussion