質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

Q&A

解決済

1回答

2175閲覧

riverpodで複数のstateを監視する方法につきまして

mako_0221

総合スコア87

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

0グッド

0クリップ

投稿2022/08/14 17:34

編集2022/08/15 06:18

version:flutter_riverpod: ^1.0.3

riverpodを利用して複数のStateを管理しようと考えております。
bool ObscureTextFunction(suffixIcon, ref) { //hereの箇所で悩んでおります。共通化する部分をクラスや関数でまとめた結果、複数のStateを識別することができなくなってしまいました。
つまり、パスワード確認用のsuffixIconがタップされた場合、「パスワード」「パスワード確認用」いずれも動作してしまいます。理由は承知しており、どちらがタップされたのかがObscureTextFunction()上識別できていないからであり、これを識別することは可能でしょうか?

実は同じように識別不能ということでreuse可能な要素が多いにも関わらず、SuffixIconWidgetSuffixIconWidgetConfirmをそれぞれ作成したという経緯がございます。クラスを分けさえすれば識別はできてしまうためです。

Dart

1//main.dart 2import 'package:cards/view/textfield.dart'; 3import 'package:flutter/material.dart'; 4import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 6void main() { 7 runApp( 8 ProviderScope( 9 child: MaterialApp( 10 title: 'Cards Demo', 11 home: RegisterWidget(), 12 ), 13 ), 14 ); 15} 16 17class RegisterWidget extends ConsumerWidget { 18 19 Widget build(BuildContext context, WidgetRef ref) { 20 return Scaffold( 21 body: Padding( 22 padding: const EdgeInsetsDirectional.fromSTEB(20, 50, 20, 0), 23 child: Column( 24 children: [ 25 Container( 26 width: double.infinity, 27 height: 50, 28 // color: Colors.grey, 29 alignment: Alignment.topLeft, 30 child: Image.asset('images/logo.png'), 31 ), 32 Container( 33 padding: const EdgeInsetsDirectional.fromSTEB(10, 0, 10, 0), 34 margin: const EdgeInsets.only(top: 30), 35 width: double.infinity, 36 // color: Colors.blue, 37 child: Column( 38 children: [ 39 const Align( 40 alignment: AlignmentDirectional(0, 0), 41 child: TextFieldCustom( 42 labelText: "email", 43 hintText: "メールアドレス", 44 suffixIcon: null, 45 ), 46 ), 47 Align( 48 alignment: const AlignmentDirectional(0, 0), 49 child: TextFieldCustom( 50 labelText: "password", 51 hintText: "パスワード", 52 suffixIcon: SuffixIconWidget()), 53 ), 54 Align( 55 alignment: const AlignmentDirectional(0, 0), 56 child: TextFieldCustom( 57 labelText: "password-confirm", 58 hintText: "パスワード確認用", 59 suffixIcon: SuffixIconWidgetConfirm()), 60 ), 61 ], 62 ), 63 ), 64 ], 65 ), 66 )); 67 } 68} 69

Dart

1//textfield.dart 2import 'package:flutter/material.dart'; 3import 'package:flutter_riverpod/flutter_riverpod.dart'; 4import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 5 6final mask = StateProvider<bool>((ref) => true); 7final maskConfirm = StateProvider<bool>((ref) => true); 8 9class TextFieldCustom extends ConsumerWidget { 10 const TextFieldCustom({required this.labelText, required this.hintText, this.suffixIcon, Key? key}): super(key: key); 11 final String labelText; 12 final String hintText; 13 final Widget? suffixIcon; 14 15 16 17 Widget build(BuildContext context, WidgetRef ref) { 18 return Container( 19 margin: const EdgeInsets.only(bottom: 10), 20 child: TextFormField( 21 style: const TextStyle( 22 fontSize: 13, 23 ), 24 obscureText: ObscureTextFunction(suffixIcon, ref), //here 25 decoration: InputDecoration( 26 labelText: labelText, //** 27 hintText: hintText, //** 28 suffixIcon: suffixIcon, //** 29 labelStyle: const TextStyle( 30 fontSize: 15, 31 color: Color.fromARGB(255, 219, 219, 219), //labelの文字の色 32 ), 33 enabledBorder: OutlineInputBorder( 34 borderRadius: BorderRadius.circular(2), 35 borderSide: const BorderSide( 36 color: Color.fromARGB(255, 219, 219, 219), //outlineの文字の色 37 width: 1.0, 38 ), 39 ), 40 focusedBorder: OutlineInputBorder( 41 borderRadius: BorderRadius.circular(2), 42 borderSide: const BorderSide( 43 color: Color.fromARGB(255, 219, 219, 219), //outline-focusの色 44 width: 1.0, //outlineの太さ 45 )), 46 ), 47 )); 48 } 49} 50 51bool ObscureTextFunction(suffixIcon, ref) { //here 52 //suffixIconがnullではない場合は、パスワードなので、そこで分岐させる 53 print(ref.watch(mask)); 54 if (suffixIcon == null) { 55 return false; 56 } else { 57 final bool isVisible = ref.watch(mask); 58 return isVisible ? false : true; 59 } 60} 61 62class SuffixIconWidget extends ConsumerWidget { 63 64 Widget build(BuildContext context, WidgetRef ref) { 65 final bool isVisible = ref.read(mask); 66 67 return IconButton( 68 icon: Icon(ref.watch(mask) // false 69 ? FontAwesomeIcons.solidEye 70 : FontAwesomeIcons.solidEyeSlash), 71 onPressed: () { 72 ref.read(mask.notifier).update((state) => !isVisible); 73 }, 74 ); 75 } 76} 77 78class SuffixIconWidgetConfirm extends ConsumerWidget { 79 80 Widget build(BuildContext context, WidgetRef ref) { 81 final bool isVisible = ref.read(maskConfirm); 82 83 return IconButton( 84 icon: Icon(ref.watch(maskConfirm) // false 85 ? FontAwesomeIcons.solidEye 86 : FontAwesomeIcons.solidEyeSlash), 87 onPressed: () { 88 ref.read(maskConfirm.notifier).update((state) => !isVisible); 89 }, 90 ); 91 } 92}

調整後コード

Dart

1//main.dart 2//main.dart 3import 'package:cards/view/textfield.dart'; 4import 'package:flutter/material.dart'; 5import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 7void main() { 8 runApp( 9 ProviderScope( 10 child: MaterialApp( 11 title: 'Cards Demo', 12 home: RegisterWidget(), 13 ), 14 ), 15 ); 16} 17 18class RegisterWidget extends ConsumerWidget { 19 20 Widget build(BuildContext context, WidgetRef ref) { 21 return Scaffold( 22 body: Padding( 23 padding: const EdgeInsetsDirectional.fromSTEB(20, 50, 20, 0), 24 child: Column( 25 children: [ 26 Container( 27 width: double.infinity, 28 height: 50, 29 // color: Colors.grey, 30 alignment: Alignment.topLeft, 31 child: Image.asset('images/logo.png'), 32 ), 33 Container( 34 padding: const EdgeInsetsDirectional.fromSTEB(10, 0, 10, 0), 35 margin: const EdgeInsets.only(top: 30), 36 width: double.infinity, 37 // color: Colors.blue, 38 child: Column( 39 children: [ 40 Align( 41 alignment: const AlignmentDirectional(0, 0), 42 child: TextFieldCustom( 43 labelText: "email", 44 hintText: "メールアドレス", 45 ), 46 ), 47 Align( 48 alignment: const AlignmentDirectional(0, 0), 49 child: TextFieldCustom( 50 labelText: "password", 51 hintText: "パスワード", 52 provider: mask, 53 ), 54 ), 55 Align( 56 alignment: const AlignmentDirectional(0, 0), 57 child: TextFieldCustom( 58 labelText: "password-confirm", 59 hintText: "パスワード確認用", 60 provider: maskConfirm, 61 ), 62 ), 63 ], 64 ), 65 ), 66 ], 67 ), 68 )); 69 } 70} 71 72//textfield.dart 73//textfield.dart 74import 'package:flutter/material.dart'; 75import 'package:flutter_riverpod/flutter_riverpod.dart'; 76import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 77 78final mask = StateProvider<bool>((ref) => true); 79final maskConfirm = StateProvider<bool>((ref) => true); 80 81class TextFieldCustom extends ConsumerWidget { 82 TextFieldCustom( 83 {required this.labelText, 84 required this.hintText, 85 this.provider, 86 Key? key}) 87 : super(key: key); 88 final String labelText; 89 final String hintText; 90 final StateProvider<bool>? provider; 91 92 bool obscureTextFunction(WidgetRef ref) { 93 if (provider != null) { 94 final bool isVisible = ref.read(provider!); 95 return isVisible ? true : false; 96 } else { 97 return false; 98 } 99 } 100 101 Widget? suffixIconFuntion(WidgetRef ref) { 102 if (provider != null) { 103 final bool isVisible = ref.read(provider!); 104 return IconButton( 105 icon: Icon(ref.watch(provider!) // false 106 ? FontAwesomeIcons.solidEyeSlash 107 : FontAwesomeIcons.solidEye), 108 onPressed: () { 109 ref.read(provider!.notifier).update((state) => !isVisible); 110 }, 111 ); 112 } else { 113 return null; 114 } 115 } 116 117 118 Widget build(BuildContext context, WidgetRef ref) { 119 if (provider != null) { 120 ref.watch(provider!); 121 } 122 return Container( 123 margin: const EdgeInsets.only(bottom: 10), 124 child: TextFormField( 125 style: const TextStyle( 126 fontSize: 13, 127 ), 128 obscureText: obscureTextFunction(ref), 129 decoration: InputDecoration( 130 labelText: labelText, //** 131 hintText: hintText, //** 132 suffixIcon: suffixIconFuntion(ref), //** 133 labelStyle: const TextStyle( 134 fontSize: 15, 135 color: Color.fromARGB(255, 219, 219, 219), //labelの文字の色 136 ), 137 enabledBorder: OutlineInputBorder( 138 borderRadius: BorderRadius.circular(2), 139 borderSide: const BorderSide( 140 color: Color.fromARGB(255, 219, 219, 219), //outlineの文字の色 141 width: 1.0, 142 ), 143 ), 144 focusedBorder: OutlineInputBorder( 145 borderRadius: BorderRadius.circular(2), 146 borderSide: const BorderSide( 147 color: Color.fromARGB(255, 219, 219, 219), //outline-focusの色 148 width: 1.0, //outlineの太さ 149 )), 150 ), 151 )); 152 } 153} 154

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ObscureTextFunctionでwatchしているのがmaskだからです。
そのためmask関連の変更でしかウィジェットの再構築は実施されません。

StateProvider<bool>をTextFieldCustomに渡せるようにして、RegisterWidgetで2つのStateProvider<bool>を切り替えて渡し、ObscureTextFunctionまでその情報を引き継がせたらいいはずです。

部品化した場合はその中でGlobalな値は極力使わないような設計をすることです。そういう値は上位側から渡すような設計を心掛ける必要があります。

こんな感じじゃないかというのを以下に提示します。
(実行時にアイコン表示のコンテナは例外が出たので削除しています)

dart

1//main.dart 2import 'package:flutter/material.dart'; 3import 'package:flutter_riverpod/flutter_riverpod.dart'; 4import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 5 6void main() { 7 runApp( 8 const ProviderScope( 9 child: MaterialApp( 10 title: 'Cards Demo', 11 home: RegisterWidget(), 12 ), 13 ), 14 ); 15} 16 17class RegisterWidget extends ConsumerWidget { 18 const RegisterWidget({Key? key}) : super(key: key); 19 20 21 Widget build(BuildContext context, WidgetRef ref) { 22 return Scaffold( 23 body: Padding( 24 padding: const EdgeInsetsDirectional.fromSTEB(20, 50, 20, 0), 25 child: Column( 26 children: [ 27 Container( 28 padding: const EdgeInsetsDirectional.fromSTEB(10, 0, 10, 0), 29 margin: const EdgeInsets.only(top: 30), 30 width: double.infinity, 31 // color: Colors.blue, 32 child: Column( 33 children: [ 34 Align( 35 alignment: const AlignmentDirectional(0, 0), 36 child: TextFieldCustom( 37 labelText: "email", 38 hintText: "メールアドレス", 39 suffixIcon: null, 40 ), 41 ), 42 Align( 43 alignment: const AlignmentDirectional(0, 0), 44 child: TextFieldCustom( 45 labelText: "password", 46 hintText: "パスワード", 47 suffixIcon: const SuffixIconWidget(), 48 provider: mask), 49 ), 50 Align( 51 alignment: const AlignmentDirectional(0, 0), 52 child: TextFieldCustom( 53 labelText: "password-confirm", 54 hintText: "パスワード確認用", 55 suffixIcon: const SuffixIconWidgetConfirm(), 56 provider: maskConfirm), 57 ), 58 ], 59 ), 60 ), 61 ], 62 ), 63 )); 64 } 65} 66//textfield.dart 67 68final mask = StateProvider<bool>((ref) => true); 69final maskConfirm = StateProvider<bool>((ref) => true); 70 71class TextFieldCustom extends ConsumerWidget { 72 TextFieldCustom( 73 {required this.labelText, 74 required this.hintText, 75 this.suffixIcon, 76 this.provider, 77 Key? key}) 78 : super(key: key); 79 final String labelText; 80 final String hintText; 81 final Widget? suffixIcon; 82 StateProvider<bool>? provider; 83 84 bool obscureTextFunction(Widget? suffixIcon, WidgetRef ref) { 85 if (provider != null) { 86 //here 87 //suffixIconがnullではない場合は、パスワードなので、そこで分岐させる 88 debugPrint(ref.read(provider!).toString()); 89 if (suffixIcon == null) { 90 return false; 91 } else { 92 final bool isVisible = ref.read(provider!); 93 return isVisible ? false : true; 94 } 95 } else { 96 return true; 97 } 98 } 99 100 101 Widget build(BuildContext context, WidgetRef ref) { 102 if (provider != null) { 103 ref.watch(provider!); 104 } 105 return Container( 106 margin: const EdgeInsets.only(bottom: 10), 107 child: TextFormField( 108 style: const TextStyle( 109 fontSize: 13, 110 ), 111 obscureText: obscureTextFunction(suffixIcon, ref), //here 112 decoration: InputDecoration( 113 labelText: labelText, //** 114 hintText: hintText, //** 115 suffixIcon: suffixIcon, //** 116 labelStyle: const TextStyle( 117 fontSize: 15, 118 color: Color.fromARGB(255, 219, 219, 219), //labelの文字の色 119 ), 120 enabledBorder: OutlineInputBorder( 121 borderRadius: BorderRadius.circular(2), 122 borderSide: const BorderSide( 123 color: Color.fromARGB(255, 219, 219, 219), //outlineの文字の色 124 width: 1.0, 125 ), 126 ), 127 focusedBorder: OutlineInputBorder( 128 borderRadius: BorderRadius.circular(2), 129 borderSide: const BorderSide( 130 color: Color.fromARGB(255, 219, 219, 219), //outline-focusの色 131 width: 1.0, //outlineの太さ 132 )), 133 ), 134 )); 135 } 136} 137 138class SuffixIconWidget extends ConsumerWidget { 139 const SuffixIconWidget({Key? key}) : super(key: key); 140 141 142 Widget build(BuildContext context, WidgetRef ref) { 143 final bool isVisible = ref.read(mask); 144 145 return IconButton( 146 icon: Icon(ref.watch(mask) // false 147 ? FontAwesomeIcons.solidEye 148 : FontAwesomeIcons.solidEyeSlash), 149 onPressed: () { 150 ref.read(mask.notifier).update((state) => !isVisible); 151 }, 152 ); 153 } 154} 155 156class SuffixIconWidgetConfirm extends ConsumerWidget { 157 const SuffixIconWidgetConfirm({Key? key}) : super(key: key); 158 159 160 Widget build(BuildContext context, WidgetRef ref) { 161 final bool isVisible = ref.read(maskConfirm); 162 163 return IconButton( 164 icon: Icon(ref.watch(maskConfirm) // false 165 ? FontAwesomeIcons.solidEye 166 : FontAwesomeIcons.solidEyeSlash), 167 onPressed: () { 168 ref.read(maskConfirm.notifier).update((state) => !isVisible); 169 }, 170 ); 171 } 172}

投稿2022/08/15 00:56

ta.fu

総合スコア1667

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mako_0221

2022/08/15 05:48

ありがとうございます。概ね理解することができました。しかしながら、どうしても @override Widget build(BuildContext context, WidgetRef ref) { if (provider != null) { ref.watch(provider!); } における、ref.watch(provider!);が何をしたいのかがどうしても理解することができません。 ビルドするたびに継続的にproviderを監視しており、実際にここでprint(ref.watch(provider!));をしてみると、updateするたびに、providerの中身がtrue/falseと変化していることが確認できます。 内部的にこのwatch()の意味を少し調べてみたのですが、stateが変化したらウィジェットをrebuildするというように解釈できたのですが、正しいのでしょうか? https://zenn.dev/junq/articles/73a9db44393f4a つまり、ここでstateに何らの変化がないことが確認されれば、変化に応じたwidgetのrebuild自体が実行されないとのりかいでございます。
mako_0221

2022/08/15 06:21

上記が正しいことを前提に、suffixIconFuntionも関数化して見通しを良く致しました。調整後コードとして本文に掲げております。これで良いですよね、ご確認願えますかというものではなく、アドバイスを頂いた内容に基づいて組み替えるとこのようになったという後学のために残したものでございます。
ta.fu

2022/08/15 06:30

watchの場所を調整したのは、どのbuildのrefを使うのか明示したほうが良いかなと思ったのと、build直下に置きたかったからです。 ObscureTextFunctionの中にあってもいいんですが、呼び出される位置によってはうまく動作しない場合や、例外が発生するということもあるかなと思い、安全を見込んでのことです。 これ、他の質問で調べた時に、initStateでは呼び出さないようにしてくださいとか、on~などの呼び出し関数には記述しないでくださいなどの但し書きがriverpodのヘルプに書いてあったからです。 後、変更後のコード見ましたが、いいのではないでしょうか。
mako_0221

2022/08/15 07:22

なるほど、riverpodの該当のヘルプ箇所については、自分でも探してみます。 いずれにしても多くをご教示いただき、大変勉強になりました。深い御礼を申し上げます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問