😇

なぜriverpod generatorは、デフォルトでautoDisposeするのか?

2024/06/12に公開

Tips💡

riverpod generatorを最近使っているのですが、なぜデフォルトでautoDisposeしてstateを破棄するのか考えておりました。

ピンと来たのは、画面遷移して前のページに戻って、また戻ってきたときに、生成されたテキストが残っていたことですね。昔のコード。StateNotifierを使うとわかりやすかったです。autoDisposeつけていないと、今いるページで招待コードを生成して、前のページに戻り戻ってくると、テキストが表示されたままでした。

final invitationCodeProvider = StateNotifierProvider.autoDispose<InvitationCodeNotifier, String>((ref) => InvitationCodeNotifier());

class InvitationCodeNotifier extends StateNotifier<String> {
  InvitationCodeNotifier() : super('');

  void generateInvitationCode([int length = 8]) {
    const charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZ';
    final random = Random.secure();
    final randomStr =  List.generate(length, (_) => charset[random.nextInt(charset.length)]).join();
    // invitationCodeに代入
    state = randomStr;
  }
}

毎回書くのは大変だろうと思っておそらく開発者のremiさんはつけたのでしょう。今だとこんな感じかな???
後々、データベースを使うのでこのコードだと保存はできないですね笑。これは実験用で作りました。

import 'dart:math';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'invitation_code_notifier.g.dart';


class InvitationCodeNotifier extends _$InvitationCodeNotifier {
  
   build() {
    return '';
  }

  void generateInvitationCode([int length = 8]) {
    const charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZ';
    final random = Random.secure();
    final randomStr =  List.generate(length, (_) => charset[random.nextInt(charset.length)]).join();
    // invitationCodeに代入
    state = randomStr;
  }
}

困ったのは、.toStringをつけないと、Riverpod2.0では怒られてしまうところですね😅

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

// Package imports:
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:workstream_prod/application/usecase/invitation_code/invitation_code_notifier.dart';
import 'package:workstream_prod/core/theme/app_color.dart';
import 'package:workstream_prod/presentation/component/square_button.dart';

class InvitationCodePage extends HookConsumerWidget {
  const InvitationCodePage({super.key});
  
  Widget build(BuildContext context, WidgetRef ref) {

    final invitationCode = ref.watch(invitationCodeNotifierProvider);

    return Scaffold(
      backgroundColor: AppColor.grey,
      appBar: AppBar(
        title: const Text('招待コードを作成'),
      ),
      body: ScreenUtilInit(
        designSize: ScreenUtil.defaultSize,
        minTextAdapt: true,
        splitScreenMode: true,
        builder: (_, child) => Center(
          child: Column(
            children: [
              SizedBox(height: 20.h),
              Padding(
                padding: const EdgeInsets.only(left: 24.0, right: 24.0),
                child: Container(
                  color: AppColor.white,
                  width: double.infinity,
                  height: 190.h,
                  child: Column(
                    children: [
                      const SizedBox(height: 25),
                      ClipRRect(
                        borderRadius: BorderRadius.circular(100),
                        child: Container(
                          color: AppColor.grey,
                          child: const Icon(
                            Icons.person,
                            size: 100,
                          ),
                        ),
                      ),
                      const SizedBox(height: 25),
                      // ? : 三項演算子で、invitationCodeが空文字の場合、'招待コードを作成'ボタンを表示。それ以外の場合は、invitationCodeを表示
                      invitationCode.toString().isEmpty
                          ? Padding(
                              padding: const EdgeInsets.only(left: 20.0, right: 20.0),
                              child: SquareButton(
                                buttonTitle: '招待コードを作成',
                                buttonColor: AppColor.navy,
                                textColor: AppColor.grey,
                                fontSize: 20,
                                onPressed: () {
                                  ref.read(invitationCodeNotifierProvider.notifier).generateInvitationCode();
                                },
                              ),
                            )
                            : Text(
                              invitationCode.toString(),
                              style: const TextStyle(fontSize: 20, fontFamily: 'Roboto'),),
                    ],
                  ),
                ),
              ),
              SizedBox(height: 20.h),
              Padding(
                padding: const EdgeInsets.only(left: 24.0, right: 24.0),
                child: Container(
                  color: AppColor.white,
                  width: double.infinity,
                  height: 190.h,
                  child: Column(
                    children: [
                      const SizedBox(height: 25),
                      const Padding(
                        padding: EdgeInsets.only(left: 20.0, right: 20.0),
                        child: Align(
                          alignment: Alignment.topLeft,
                          child: Text('仲間を追加', style: TextStyle(fontSize: 22, fontFamily: 'Roboto'),)),
                      ),
                      const SizedBox(height: 19),
                     const Padding(
                       padding: EdgeInsets.only(left: 20.0, right: 20.0),
                       child: Align(
                          alignment: Alignment.topLeft,
                          child: Text('達成目標を追加したいユーザ\nの招待コードを追加してください', style: TextStyle(fontSize: 16, fontFamily: 'Roboto'),)),
                     ),
                      const SizedBox(height: 25),
                      Padding(
                        padding: const EdgeInsets.only(left: 20.0, right: 20.0),
                        child: SquareButton(
                          buttonTitle: '招待コードを入力',
                          buttonColor: AppColor.navy,
                          textColor: AppColor.grey,
                          fontSize: 20,
                          onPressed: () {
                            /// TODO: 招待コードDialogの実装
                            showDialog<void>(
                              context: context,
                              builder: (BuildContext context) {
                                return AlertDialog(
                                  title: const Text('招待コードを入力してください', style: TextStyle(fontSize: 16.0)),
                                  content: TextFormField(
                                    // 文字数8桁の制限
                                    maxLength: 8,
                                    // 英語は大文字に変換
                                    inputFormatters: [
                                      UpperCaseTextFormatter(),
                                    ],
                                    decoration: const InputDecoration(hintText: '招待コードを入力してください'),
                                  ),
                                  actions: <Widget>[
                                    TextButton(
                                      onPressed: () {
                                        Navigator.of(context).pop();
                                      },
                                      child: const Text('キャンセル'),
                                    ),
                                    TextButton(
                                      onPressed: () {
                                        Navigator.of(context).pop();
                                      },
                                      child: const Text('OK'),
                                    ),
                                  ],
                                );
                              },
                            );
                          },
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

/// [英語を大文字に変換するカスタムクラス]
class UpperCaseTextFormatter extends TextInputFormatter {
  
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    return TextEditingValue(
      text: newValue.text.toUpperCase(),
      selection: newValue.selection,
    );
  }
}

まとめ

ジェネレーターが登場するまでは、状態を破棄するautoDisposeをつけるのを意識しなければならなかったですが今は必要無くなったので、逆に状態を破棄しないようにするコードを書くことを意識するだけになりましたね。

破棄しないことも意識しないと、変数で値を渡す処理を作った時に、昔やらかして、アップロードしたファイルの状態が消えてしまったことがありました😅

Discussion