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

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

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

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

Dart

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

Q&A

解決済

2回答

933閲覧

クラスの正しい使い方につきまして

mako_0221

総合スコア87

Flutter

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

Dart

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

0グッド

1クリップ

投稿2022/08/12 15:15

クラスやオブジェクト指向についてDart言語+FlutterSDKを通じて学んでいるのですが、下記のところまで記載して考え方や実際に適用する方法がわからなくなってしまいました。関連するところについて//**でマーキングさせて頂きました。

TextFieldEmail()ウィジェットとTextFieldPasswordウィジェットを並べているのですが、コードの通り、殆どが共通しております。実際にはこれらのクラスを分けずに共通する部分を束ねることがクラスの一つの役割かと思っているのですが、このケースでは「どのように束ねるべきか」がわかりません。

コンストラクタ時点で分岐させて分けて一つのクラスで管理するのか、親クラスのような物を作って、それぞれの子クラスに継承させて複数クラスで管理するのかイメージがわきません。

稚拙は承知で申し訳ございませんが、本件であれば、「一般的にはこのようにします」のようなアドバイスをいただけると大変有難いです。
よろしくお願い申し上げます。

Dart

1//main.dart 2import 'package:cards/view/textfield.dart'; 3import 'package:flutter/material.dart'; 4import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 6 7void main() { 8 runApp( 9 ProviderScope( 10 child: MaterialApp( 11 title: 'Demo', 12 home: RegisterWidget(), 13 ), 14 ), 15 ); 16} 17 18class RegisterWidget extends ConsumerWidget { 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 child: Column( 32 children: [ 33 Align( 34 alignment: const AlignmentDirectional(0, 0), 35 child: TextFieldEmail(),//** 36 ), 37 Align( 38 alignment: const AlignmentDirectional(0, 0), 39 child: TextFieldPassword(),//** 40 ), 41 ], 42 ), 43 ), 44 ], 45 ), 46 )); 47 } 48}

Dart

1import 'package:flutter/material.dart'; 2import 'package:flutter_riverpod/flutter_riverpod.dart'; 3import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 4 5class TextFieldEmail extends ConsumerWidget { 6 7 8 Widget build(BuildContext context, WidgetRef ref) { 9 return Container( 10 margin: const EdgeInsets.only(bottom: 10), 11 child: TextFormField( 12 style: const TextStyle( 13 fontSize: 13, 14 ), 15 obscureText: true, 16 decoration: InputDecoration( 17 labelText: "email",//** 18 hintText: "メールアドレス",//** 19 suffixIcon: null,//** 20 labelStyle: const TextStyle( 21 fontSize: 15, 22 color: Color.fromARGB(255, 219, 219, 219), //labelの文字の色 23 ), 24 enabledBorder: OutlineInputBorder( 25 borderRadius: BorderRadius.circular(2), 26 borderSide: const BorderSide( 27 color: Color.fromARGB(255, 219, 219, 219), //outlineの文字の色 28 width: 1.0, 29 ), 30 ), 31 focusedBorder: OutlineInputBorder( 32 borderRadius: BorderRadius.circular(2), 33 borderSide: const BorderSide( 34 color: Color.fromARGB(255, 219, 219, 219), //outline-focusの色 35 width: 1.0, //outlineの太さ 36 )), 37 ), 38 )); 39 } 40} 41 42// final mask = StateProvider((ref) => false); 43 44class TextFieldPassword extends ConsumerWidget { 45bool mask = false; 46 47 48 Widget build(BuildContext context, WidgetRef ref) { 49 return Container( 50 margin: const EdgeInsets.only(bottom: 10), 51 child: TextFormField( 52 style: const TextStyle( 53 fontSize: 13, 54 ), 55 obscureText: true, 56 decoration: InputDecoration( 57 labelText: "password",//** 58 hintText: "パスワード",//** 59 suffixIcon: IconButton(//** 60 icon: Icon(mask 61 ? FontAwesomeIcons.solidEye 62 : FontAwesomeIcons.solidEyeSlash), 63 onPressed: () { 64 mask = !mask; 65 }, 66 ), 67 labelStyle: const TextStyle( 68 fontSize: 15, 69 color: Color.fromARGB(255, 219, 219, 219), //labelの文字の色 70 ), 71 enabledBorder: OutlineInputBorder( 72 borderRadius: BorderRadius.circular(2), 73 borderSide: const BorderSide( 74 color: Color.fromARGB(255, 219, 219, 219), //outlineの文字の色 75 width: 1.0, 76 ), 77 ), 78 focusedBorder: OutlineInputBorder( 79 borderRadius: BorderRadius.circular(2), 80 borderSide: const BorderSide( 81 color: Color.fromARGB(255, 219, 219, 219), //outline-focusの色 82 width: 1.0, //outlineの太さ 83 )), 84 ), 85 )); 86 } 87}

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

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

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

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

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

guest

回答2

0

これで一応定義が一箇所にまとまって、
=>コード量が(コードの重複が)減って読みやすい
=>修正時の労力が最小化される
というような目的が達成されていると思います。
上記の目的のために重複箇所をまとめるような考え方が一般的、と言えますでしょうか。

ベースクラスを定義してそれを継承した複数のクラスを定義する方法でもできるような気はしますが、このケースでそれをするのはあまり一般的とは言えないような気がします。
その方法は、階層構造を持つ似たような複数のクラスを定義する必要がある(そういうクラス設計をしようと思っている)場合には有用だと思いますが、このケースでは上記のやり方で済むので、クラスを複数定義する必要が無いように思います。
が、結局開発者やアプリ所有者の方針次第かと思います。

「正しい使い方」とは何なのか、ということですが、結局目的があって、その目的を達成していれば正しい使い方、そうでなければ正しくない使い方、ということになるでしょうか。
目的を達成する方法が複数ある場合、「より労力が少なくて済む」「よりわかりやすい」方が正しい、と言えるでしょうか。
それも大差無い場合はもう後は好みの問題のような気がします。

import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; void main() { runApp( ProviderScope( child: MaterialApp( title: 'Demo', home: RegisterWidget(), ), ), ); } final maskProvider = StateProvider((ref) => false); class RegisterWidget extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { bool mask = ref.watch(maskProvider); return Scaffold( body: Padding( padding: const EdgeInsetsDirectional.fromSTEB(20, 50, 20, 0), child: Column( children: [ Container( padding: const EdgeInsetsDirectional.fromSTEB(10, 0, 10, 0), margin: const EdgeInsets.only(top: 30), width: double.infinity, child: Column( children: [ Align( alignment: const AlignmentDirectional(0, 0), child: CustomTextField( labelText: 'email', hintText: 'メールアドレス', ), //** ), Align( alignment: const AlignmentDirectional(0, 0), child: CustomTextField( labelText: 'password', hintText: 'パスワード', iconButton: IconButton( icon: Icon(mask ? Icons.arrow_back : Icons.arrow_forward), onPressed: () { ref.read(maskProvider.notifier).state = !ref.read(maskProvider.notifier).state; }, ), ), ), //** ], ), ), ], ), )); } } class CustomTextField extends StatelessWidget { const CustomTextField({ Key? key, required this.labelText, required this.hintText, this.iconButton, }) : super(key: key); final String labelText; final String hintText; final Widget? iconButton; @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(bottom: 10), child: TextFormField( style: const TextStyle( fontSize: 13, ), obscureText: true, decoration: InputDecoration( labelText: labelText, //** hintText: hintText, //** suffixIcon: iconButton, //** labelStyle: const TextStyle( fontSize: 15, color: Color.fromARGB(255, 219, 219, 219), //labelの文字の色 ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(2), borderSide: const BorderSide( color: Color.fromARGB(255, 219, 219, 219), //outlineの文字の色 width: 1.0, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(2), borderSide: const BorderSide( color: Color.fromARGB(255, 219, 219, 219), //outline-focusの色 width: 1.0, //outlineの太さ ), ), ), ), ); } }

投稿2022/08/13 02:07

moriman

総合スコア615

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

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

mako_0221

2022/08/13 16:43

コメントありがとうございます。 なるほど、iconButtonの取り扱いをどうしたものかと思っていたのですが、そこの部分については、CustomTextFieldのパラメータにしながらも(not required)、直接consumer widget側に記載していくんですね。素人なりの発想で、statlesswidget側に書いて条件で分岐させるようなことしか思いつかなかったのですが、より理解が得られやすく自然な気がします。 「より労力が少なくて済む」「よりわかりやすい」方が正しいという言葉が腹落ちしている状況ですが、今だから皆様の提示してくださったコードを拝見して、私が取ろうとしていた選択肢がいずれも満たしていないことに気付かされた、大変勉強になりました!
moriman

2022/08/14 02:21

一つだけ、「私のコードが正解」ということではなく、一つの例として見ていただければ幸いでございます。 頂いたコメントを呼んで考えてみたのですが、パラメーターでiconButtonを渡すのではなく、CustomTextField内でiconButtonを条件分岐で切り替えるやり方もできると思います(確認はしていません)し、このケースではどちらの方が明らかに優れた方法、というほどの違いもないと思います。 まあ結局はアプリの仕様と開発者の考え方次第かと思います。
guest

0

ベストアンサー

このケースだけ見るのであれば、部品化するかな。

共通部分だけでWidgetクラスを作り、異なる部分についてはコンストラクタの引数で渡すようにする。

異なる部分はInputDecorationに渡すlabelText, hintText, suffixIconなので、これを渡せるようにする。

先頭部分だけだけど、以下の様な感じ。

dart

1class TextFieldP extends ConsumerWidget { 2 const TextFieldP(this.labeltext, this.hintText, this.suffixIcon, {Key? key}) 3 : super(key: key); 4 5 final String labeltext; 6 final String hintText; 7 final Widget? suffixIcon; 8 9 10 Widget build(BuildContext context, WidgetRef ref) { 11 return Container( 12 margin: const EdgeInsets.only(bottom: 10), 13 child: TextFormField( 14 style: const TextStyle( 15 fontSize: 13, 16 ), 17 obscureText: true, 18 decoration: InputDecoration( 19 labelText: labeltext, 20 hintText: hintText, 21 suffixIcon: suffixIcon, //**

使う方はこんな感じ。
TextFieldP("email", "メールアドレス", null)

dart

1TextFieldP("password", "パスワード", IconButton(//** 2 icon: Icon(mask 3 ? FontAwesomeIcons.solidEye 4 : FontAwesomeIcons.solidEyeSlash), 5 onPressed: () { 6 mask = !mask; 7 }, 8 ))

投稿2022/08/13 01:06

ta.fu

総合スコア1664

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

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

mako_0221

2022/08/13 16:34

ご回答ありがとうございます。 特に3つ目のパラメータのsuffixIconについて、いずれかだけに存在する場合はどうするのだろうと考えあぐねていたのですが、単純にemailの場合にはnullとして差し支えないのですね。実際に動かしてみたら問題なかったです。 おそらく、このくらいのケースであれば、この方法が後の管理でも都合が良いと考えます。一方で、パターン変化に富んできた場合でパラメータが多くなってきたしまった場合には、部品化すべきなのか他のアプローチを取るべきなのか考えなければならないと感じました。 いずれにしてもご助言に深謝をいたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問