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

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

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

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

Dart

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

Q&A

1回答

572閲覧

【Flutter】TextFieldに横罫線を表示させる方法(Widgetなど)はあるのでしょうか?

bbiiq

総合スコア51

Flutter

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

Dart

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

0グッド

1クリップ

投稿2022/12/31 07:07

前提

いつもお世話になっています。
今回は横罫線について質問させていただきます。

実現したいこと

Flutterでメモ帳や大学ノートなどの横罫線を表示させたいです。
以下のサイトを参考にさせていただいているのですが、動作がおかしい(まったく罫線が表示されなかったり下の方の罫線が2重となっていたり)です。「flutter clean」実行後、再度ビルドすると一旦問題なく表示されるのですが、画面遷移などをするとまた動作がおかしくなってしまいます。
なので、以下のサイトではなく別の方法で横罫線が表示できないかと思い質問させていただきました。
どうすれば、メモ帳や大学ノートのような横罫線を表示することができるのでしょうか?
(なげやりな質問で申し訳ありません。ちょっとした情報でもあれば教えていただけると嬉しいです)
https://halzoblog.com/flutter-textfield-ruled-lines/#toc12

該当のソースコード(参考にさせていただいたサイトのソースです)

Dart

1import 'package:flutter/material.dart'; 2 3void main() => runApp(MyApp()); 4 5// Text Widgetの位置把握のためGlobalKey型のグローバル変数を定義 6GlobalKey globalKeyTextWidgetO = GlobalKey(); 7 8class MyApp extends StatelessWidget { 9 10 Widget build(BuildContext context) { 11 return MaterialApp( 12 title: "Test", 13 theme: ThemeData.light(), 14 home: SampleScreenO(), 15 ); 16 } 17} 18 19class SampleScreenO extends StatefulWidget { 20 21 _SampleScreenOState createState() => _SampleScreenOState(); 22} 23 24class _SampleScreenOState extends State<SampleScreenO> { 25 26 // Text Widgetの2次元情報を入れるためのRenderBox型の変数を定義 27 // null safety対応でlateをつける 28 late RenderBox textWidgetO; 29 30 // Text WidgetのY座標開始位置と高さ(縦幅)を入れる変数を定義 31 // 初回ビルド時のnullエラーを防ぐため、初期値を設定しておく 32 double textWidgetDyO = 0; 33 double textWidgetHeightO = 0; 34 35 // 画面全体のパディング、フォントサイズ、1行の高さを決める係数を設定 36 static const double standardPaddingO = 8.0; 37 static const double fontSizeO = 18.0; 38 static const double lineSpaceO = 1.2; 39 40 // TextField上にデフォルトで設定される上部パディングの変更値。ここでは0に設定 41 static const double contentPaddingO = 0.0; 42 43 // TextFieldに設置するFocusNodeのインスタンス作成 44 FocusNode focusNodeTextFieldO = FocusNode(); 45 46 // 初期値設定等を行うinitStateメソッドを導入 47 48 void initState() { 49 50 // Text Widgetが描画された後でないとGlobalKeyの値が取得できずエラーになるため、 51 // buildメソッド完了後にGlobalKeyの値取得が行われるよう、 52 // addPostFrameCallbackメソッドを使用 53 // null safety対応で?(null以外のみアクセス)をつける 54 WidgetsBinding.instance?.addPostFrameCallback((cb) { 55 56 // GlobalKeyを通じてText Widgetの2次元情報を取得 57 // null safety対応で?と最後にas RenderBoxをつける 58 textWidgetO = globalKeyTextWidgetO.currentContext?.findRenderObject() as RenderBox; 59 60 // 2次元情報からText Widgetの縦方向の上端位置(Y座標)と高さ(縦幅)を取得 61 textWidgetDyO = textWidgetO.localToGlobal(Offset.zero).dy; 62 textWidgetHeightO = textWidgetO.size.height; 63 64 // 確認のため、取得した位置と高さをDebugウィンドウに表示 65 print("TextWidgetの上端位置 $textWidgetDyO、TextWidgetの高さ $textWidgetHeightO"); 66 67 // Text Widgetの位置と高さを取得後、setStateメソッドで全体を再描画する 68 setState(() {}); 69 }); 70 71 super.initState(); 72 } 73 74 75 Widget build(BuildContext context) { 76 77 // 画面全体の縦幅と横幅を取得するためのMediaQuery.ofメソッド 78 // ※画面の縦幅・横幅は変化しないためfinalで宣言 79 final screenHeightO = MediaQuery.of(context).size.height; 80 final screenWidthO = MediaQuery.of(context).size.width; 81 82 // 画面の下端位置(キーボード出現時はキーボードの上端位置)を取得するためのMediaQuery.ofメソッド 83 var textFieldBottomO = MediaQuery.of(context).viewInsets.bottom; 84 85 // TextFieldの縦幅を柔軟に設定する計算式 86 // 画面全体に設定している下部パディングも引く必要あり 87 var textFieldVerticalRangeO= 88 screenHeightO 89 - (textWidgetDyO + textWidgetHeightO) 90 - textFieldBottomO 91 - standardPaddingO; 92 93 return Scaffold( 94 appBar: AppBar( 95 title: Text("TestApp"), 96 ), 97 body: Padding( 98 padding: const EdgeInsets.all(standardPaddingO), 99 child: Column( 100 children: [ 101 Text( 102 "↓TextField", 103 104 // GlobalKeyをText Widgetのkeyプロパティに設定 105 key: globalKeyTextWidgetO, 106 107 ), 108 Container( 109 110 // 分かりやすくするために枠線を付けるが、内側に自動で1.0のパディングが発生する点に注意 111 decoration: BoxDecoration( 112 border: Border.all(color: Colors.grey), 113 ), 114 115 // 初回描画時点では、GlobalKey未取得のため、計算式から高さを求めると 116 // 画面サイズオーバーのエラーになる。これを回避するため、GlobalKeyの取得が 117 // 未了の段階(=初回描画時点)では高さを0.0にしておく 118 height: (textWidgetDyO == 0) 119 ? 0.0 120 : textFieldVerticalRangeO, 121 122 child: Scrollbar( 123 124 // スクロール時に罫線も動かすために、SingleChildScrollViewが必要 125 child: SingleChildScrollView( 126 127 // Stackを使って、罫線を引くCustomPaintの上に、TextFieldを重ね表示する 128 child: Stack( 129 children: <Widget>[ 130 131 // 描画するCustomPaintクラスの呼び出し 132 CustomPaint( 133 134 // painter属性では、外部に作成したクラスに処理を飛ばす 135 // 画面の座標・幅情報を引数として渡す 136 painter: DisplayRuledLinesO( 137 textFieldVerticalRangeO: textFieldVerticalRangeO, 138 screenWidthO: screenWidthO, 139 focusNodeTextFieldO: focusNodeTextFieldO, 140 textWidgetDyO: textWidgetDyO, 141 textWidgetHeightO: textWidgetHeightO, 142 ), 143 ), 144 145 TextField( 146 147 // FocusNodeのインスタンスを設置 148 focusNode: focusNodeTextFieldO, 149 150 // フォントサイズを設定 151 style: TextStyle(fontSize: fontSizeO), 152 153 strutStyle: const StrutStyle( 154 155 // 1行の高さをフォントサイズに対する倍数値で設定 156 height: lineSpaceO, 157 158 // 改行とともにズレるのを防ぐため、行間強制を設定 159 forceStrutHeight: true, 160 161 ), 162 163 autofocus: true, 164 165 decoration: InputDecoration( 166 167 // TextFieldの上端から書き始められるように設定 168 isDense: true, 169 170 border: InputBorder.none, 171 172 // デフォルトの上部パディング(12.0)を変更するために設定 173 contentPadding: 174 const EdgeInsets.only(top: contentPaddingO), 175 ), 176 177 keyboardType: TextInputType.multiline, 178 179 // 行数は上限なしで設定 180 maxLines: null, 181 ), 182 ], 183 ), 184 ), 185 ), 186 ), 187 ], 188 ), 189 ), 190 ); 191 } 192} 193 194 195// 横罫線の描画処理をするCustomPainterの拡張クラス 196class DisplayRuledLinesO extends CustomPainter { 197 198 final double textFieldVerticalRangeO; 199 final double screenWidthO; 200 final FocusNode focusNodeTextFieldO; 201 final double textWidgetDyO; 202 final double textWidgetHeightO; 203 204 // 引数から初期値を受け取るコンストラクタ 205 DisplayRuledLinesO({ 206 required this.textFieldVerticalRangeO, 207 required this.screenWidthO, 208 required this.focusNodeTextFieldO, 209 required this.textWidgetDyO, 210 required this.textWidgetHeightO, 211 }); 212 213 214 215 216 void paint(Canvas canvas, Size size) { 217 218 // 罫線を描画する範囲(高さ)を計算 219 // 見えているTextFieldの高さ(キーボードの出現有無で可変)に、スクロールで上に動いた分を加算 220 // これにより、スクロールとともに追加で罫線を描画し、罫線が無くなることを防ぐ 221 final drawingVerticalRangeO = 222 223 // TextFieldの縦幅 224 textFieldVerticalRangeO 225 + ( 226 // GlobalKeyのY座標位置を加算 227 // Containerに枠線設定をすることで発生する上部パディング1.0を加える 228 // ※枠線を無くしたときは、この「+1.0」は削除が必要 229 // さらに、TextField上部のデフォルトのパディングを変更した値が、0より大きいときは、その加算も必要 230 (textWidgetDyO + textWidgetHeightO + 1.0 + _SampleScreenOState.contentPaddingO) 231 232 // FocusNodeのY座標位置(スクロールによる移動をトレースしたTextField上端位置)を減算 233 - focusNodeTextFieldO.offset.dy 234 ); 235 236 // 確認のため、Debugウィンドウに表示 237 print("TextFieldの縦幅: $textFieldVerticalRangeO"); 238 print("GlobalKeyの位置: ${textWidgetDyO + textWidgetHeightO + 1.0 + _SampleScreenOState.contentPaddingO}"); 239 print("FocusNodeの位置: ${focusNodeTextFieldO.offset.dy}"); 240 241 // フォントサイズ×行間係数(strutStyleで規定)で1行の高さを算出し(ズレの蓄積を防ぐため四捨五入して整数化する) 242 // ※切上げではなく四捨五入に修正(2021.12.26修正) 243 // 上で求めた描画範囲(高さ)を割って、描画本数を計算する(切捨て) 244 final ruledLineSpaceO = (_SampleScreenOState.fontSizeO * _SampleScreenOState.lineSpaceO).round(); 245 print("ruledLineSpaceO: $ruledLineSpaceO"); 246 final int ruledLineNumberO = (drawingVerticalRangeO / ruledLineSpaceO).floor(); 247 print("ruledLineNumberO: $ruledLineNumberO"); 248 249 // 罫線の横幅を計算 250 // MediaQueryで把握済のTextFieldの幅から、左右に設定したパディングを引いて計算 251 final ruledLineWidthO = screenWidthO - _SampleScreenOState.standardPaddingO * 2; 252 253 // 罫線の描画 254 final paint = Paint() 255 ..color = Colors.grey 256 ..strokeWidth = 0.7; 257 for (var i = 1; i <= ruledLineNumberO; i++) { 258 canvas.drawLine( 259 260 // Offset(x, y)からOffset(x', y')へ線を引き、それを「ruledLineNumberO」回繰り返す 261 // TextField上部のデフォルトのパディングを変更した値が、0より大きいときは、Y座標に加算する必要あり 262 Offset(0, ruledLineSpaceO * i + _SampleScreenOState.contentPaddingO), 263 Offset(ruledLineWidthO, ruledLineSpaceO * i + _SampleScreenOState.contentPaddingO), 264 paint, 265 ); 266 } 267 } 268 269 // 一度描画した横罫線の再描画は不要なため、false 270 271 bool shouldRepaint(CustomPainter oldDelegate) => false; 272 273}

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

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

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

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

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

guest

回答1

0

https://flutter.ctrnost.com/basic/interactive/form/textfield/
これ見ながらやってみてください。
maxLines: nullをmaxLines:1にしてみると良いかもしれないです。

投稿2023/02/13 17:13

ituking

総合スコア80

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問