teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

コードの貼り間違え...

2021/05/26 08:15

投稿

popobot
popobot

スコア6588

answer CHANGED
@@ -196,6 +196,7 @@
196
196
  class _NextScreenState2 extends State<NextScreen2> {
197
197
  // 起動時に初期化
198
198
  Model _initModel;
199
+ TextEditingController _controller;
199
200
 
200
201
  void initState() {
201
202
  super.initState();
@@ -205,6 +206,9 @@
205
206
  subTitle: widget._model.subTitle,
206
207
  key: widget._model.key,
207
208
  );
209
+
210
+ _controller = TextEditingController();
211
+ _controller.text = widget._model.title;
208
212
  }
209
213
 
210
214
  @override
@@ -226,17 +230,13 @@
226
230
  children: <Widget>[
227
231
  // タイトル
228
232
  TextField(
229
- controller: TextEditingController()
233
+ controller: _controller,
230
- ..text = widget._model.title,
231
234
  decoration: InputDecoration(
232
235
  hintText: 'タイトル',
233
236
  hintStyle: TextStyle(
234
237
  color: Colors.black26,
235
238
  ),
236
239
  ),
237
- onChanged: (text) {
238
- widget._model.title = text;
239
- },
240
240
  ),
241
241
 
242
242
  // 登録ボタン
@@ -251,8 +251,9 @@
251
251
  // TODO: 新規登録
252
252
  // 前の画面に戻る
253
253
  print('確定');
254
+ widget._model.title = _controller.text;
254
255
  if (Navigator.of(context).canPop()) {
255
- Navigator.of(context).pop(widget._model);
256
+ Navigator.of(context).pop();
256
257
  }
257
258
  },
258
259
  ),
@@ -271,7 +272,7 @@
271
272
  // 前の画面に戻る
272
273
  widget._model = _initModel;
273
274
  if (Navigator.of(context).canPop()) {
274
- Navigator.of(context).pop(null);
275
+ Navigator.of(context).pop();
275
276
  }
276
277
  },
277
278
  style: ElevatedButton.styleFrom(

2

注記を追加

2021/05/26 08:15

投稿

popobot
popobot

スコア6588

answer CHANGED
@@ -286,4 +286,7 @@
286
286
  ));
287
287
  }
288
288
  }
289
- ```
289
+ ```
290
+
291
+ この実装なら pop() で値を返す必要もないです
292
+ もし pop() で値を返すような実装にしたいなら、NectScreenに渡す際にモデルを生成し直せば、それでも実現できると思います

1

追記

2021/05/26 04:03

投稿

popobot
popobot

スコア6588

answer CHANGED
@@ -1,3 +1,289 @@
1
1
  コード全体がないので、確かなことをは言えませんが、 `_initModel` が書き換わってしまっているのだと思います。
2
2
 
3
- そこで、キャンセル時は `_initModel` を返さずに `null` を返すようにして、 TopScreen側では `result` が `null` じゃなかったら setState で状態を更新するようにしたらどうでしょうか
3
+ そこで、キャンセル時は `_initModel` を返さずに `null` を返すようにして、 TopScreen側では `result` が `null` じゃなかったら setState で状態を更新するようにしたらどうでしょうか
4
+
5
+ ----
6
+
7
+ コード全体を見ての追記:5/26 13:00
8
+
9
+ そうですね、TopScreenのmodelの参照をNectScreen側に渡しているので、TextFieldのonChangeの時点で値が変わってしまっています
10
+
11
+ NectScreenのTextEditingControllerの使い方がよくない気がします。TextEditingControllerが現在編集中の値を保持する役割をしてくれます。
12
+ TextEditingControllerの初期化をbuild内でしていますが、TextEditingControllerはプロパティとして保持して、確定時にModelに反映するような実装にするといい気がします
13
+
14
+ 修正してみたコードを以下に貼っておきます(もともとのコードと比較すると変更箇所がわかると思います)
15
+ ```
16
+ import 'package:flutter/material.dart';
17
+
18
+ void main() => runApp(MyApp());
19
+
20
+ class Todo {
21
+ final String title;
22
+ final String description;
23
+ Todo({@required this.title, @required this.description})
24
+ : assert(title != null),
25
+ assert(description != null);
26
+ }
27
+
28
+ class MyApp extends StatelessWidget {
29
+ @override
30
+ Widget build(BuildContext context) {
31
+ return MaterialApp(
32
+ debugShowCheckedModeBanner: false, // Debug文言の削除
33
+ title: 'Navigation',
34
+ home: ListScreen2(title: 'List'),
35
+ );
36
+ }
37
+ }
38
+
39
+ class Model {
40
+ String title;
41
+ String subTitle;
42
+ String key;
43
+
44
+ Model({
45
+ @required this.title,
46
+ @required this.subTitle,
47
+ @required this.key,
48
+ });
49
+ }
50
+
51
+ class ListScreen2 extends StatefulWidget {
52
+ ListScreen2({Key key, this.title}) : super(key: key);
53
+
54
+ final String title;
55
+
56
+ @override
57
+ _ListScreenState2 createState() => _ListScreenState2();
58
+ }
59
+
60
+ class _ListScreenState2 extends State<ListScreen2> {
61
+ List<Model> modelList;
62
+
63
+ @override
64
+ // 起動時に初期化
65
+ void initState() {
66
+ super.initState();
67
+
68
+ modelList = [];
69
+ List<String> titleList = ["Title A", "Title B", "Title C"];
70
+ List<String> subTitleList = ["SubTitle A", "SubTitle B", "SubTitle C"];
71
+
72
+ for (int i = 0; i < 3; i++) {
73
+ Model model = Model(
74
+ title: titleList[i],
75
+ subTitle: subTitleList[i],
76
+ key: i.toString(),
77
+ );
78
+ modelList.add(model);
79
+ }
80
+ }
81
+
82
+ // リスト項目となる削除可能なウィジェットを作成
83
+ Widget buildItem(Model model, int index) {
84
+ return Dismissible(
85
+ key: Key('${model.key}'), // 項目が特定できるよう固有の文字列をキーとする
86
+ background: Container(
87
+ padding: EdgeInsets.only(
88
+ right: 48,
89
+ ),
90
+ alignment: AlignmentDirectional.centerEnd,
91
+ color: Colors.red,
92
+ child: Icon(
93
+ Icons.delete,
94
+ color: Colors.white,
95
+ ),
96
+ ), // スワイプしているアイテムの背景色
97
+ direction: DismissDirection.endToStart,
98
+ confirmDismiss: (direction) async {
99
+ // 削除するか確認する
100
+ return await showDialog(
101
+ context: context,
102
+ builder: (BuildContext context) {
103
+ return AlertDialog(
104
+ title: Text('確認ダイアログ'),
105
+ content: Text('本当に削除しますか?'),
106
+ actions: <Widget>[
107
+ TextButton(
108
+ onPressed: () => Navigator.of(context).pop(false),
109
+ child: Text('CANCEL'),
110
+ ),
111
+ TextButton(
112
+ onPressed: () => Navigator.of(context).pop(true),
113
+ child:
114
+ Text('DELETE', style: TextStyle(color: Colors.red))),
115
+ ],
116
+ );
117
+ },
118
+ );
119
+ },
120
+ onDismissed: (direction) {
121
+ // 削除時の処理
122
+ setState(() {
123
+ modelList.remove(model);
124
+ });
125
+ },
126
+ // 各項目のレイアウト
127
+ child: Card(
128
+ child: ListTile(
129
+ tileColor: Theme.of(context).cardColor,
130
+ title: Text('${model.title}',
131
+ style: TextStyle(
132
+ fontWeight: FontWeight.bold, fontSize: 20, height: 1.2)),
133
+ subtitle: Text('${model.key} ${model.subTitle}'),
134
+ onTap: () async {
135
+ var result = await Navigator.push(
136
+ context,
137
+ MaterialPageRoute(
138
+ builder: (context) => NextScreen2(model: model)));
139
+ //print(result);
140
+ setState(() {
141
+ if (result != null) {
142
+ model = result;
143
+ print('set state ' + model.title);
144
+ }
145
+ });
146
+ },
147
+ )));
148
+ }
149
+
150
+ @override
151
+ Widget build(BuildContext context) {
152
+ return Scaffold(
153
+ appBar: AppBar(
154
+ title: Text(widget.title),
155
+ ),
156
+ body: Container(
157
+ child: ReorderableListView(
158
+ onReorder: (int oldIndex, int newIndex) {
159
+ if (newIndex > oldIndex) {
160
+ // 元々下にあった要素が上にずれるため一つ分後退させる
161
+ newIndex -= 1;
162
+ }
163
+
164
+ // 並び替え処理
165
+ Model model = modelList[oldIndex];
166
+ setState(() {
167
+ modelList.removeAt(oldIndex);
168
+ modelList.insert(newIndex, model);
169
+ });
170
+ },
171
+ padding: EdgeInsets.only(top: 4),
172
+ children: modelList
173
+ .asMap()
174
+ .map((i, item) => MapEntry(i, buildItem(item, i)))
175
+ .values
176
+ .toList())
177
+ //],
178
+ ));
179
+ }
180
+ }
181
+
182
+ ///////////
183
+
184
+ class NextScreen2 extends StatefulWidget {
185
+ Model _model;
186
+
187
+ NextScreen2({Key key, @required Model model})
188
+ : assert(model != null),
189
+ this._model = model,
190
+ super(key: key);
191
+
192
+ @override
193
+ _NextScreenState2 createState() => _NextScreenState2();
194
+ }
195
+
196
+ class _NextScreenState2 extends State<NextScreen2> {
197
+ // 起動時に初期化
198
+ Model _initModel;
199
+
200
+ void initState() {
201
+ super.initState();
202
+
203
+ _initModel = Model(
204
+ title: widget._model.title,
205
+ subTitle: widget._model.subTitle,
206
+ key: widget._model.key,
207
+ );
208
+ }
209
+
210
+ @override
211
+ Widget build(BuildContext context) {
212
+ return WillPopScope(
213
+ onWillPop: () {
214
+ print('onWillPop');
215
+ Navigator.of(context).pop(_initModel);
216
+ return Future.value(false);
217
+ },
218
+ child: Scaffold(
219
+ appBar: AppBar(
220
+ title: Text('Edit'),
221
+ ),
222
+ body: Container(
223
+ padding: EdgeInsets.all(20),
224
+ child: Column(
225
+ mainAxisAlignment: MainAxisAlignment.start,
226
+ children: <Widget>[
227
+ // タイトル
228
+ TextField(
229
+ controller: TextEditingController()
230
+ ..text = widget._model.title,
231
+ decoration: InputDecoration(
232
+ hintText: 'タイトル',
233
+ hintStyle: TextStyle(
234
+ color: Colors.black26,
235
+ ),
236
+ ),
237
+ onChanged: (text) {
238
+ widget._model.title = text;
239
+ },
240
+ ),
241
+
242
+ // 登録ボタン
243
+ Container(
244
+ //padding: EdgeInsets.all(_PADDING_SIZE),
245
+ padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
246
+ width: double.infinity, // 横幅いっぱいに広げる
247
+ height: 60,
248
+ child: ElevatedButton(
249
+ child: Text('確定'),
250
+ onPressed: () {
251
+ // TODO: 新規登録
252
+ // 前の画面に戻る
253
+ print('確定');
254
+ if (Navigator.of(context).canPop()) {
255
+ Navigator.of(context).pop(widget._model);
256
+ }
257
+ },
258
+ ),
259
+ ),
260
+
261
+ // キャンセルボタン
262
+ Container(
263
+ //padding: EdgeInsets.all(_PADDING_SIZE),
264
+ padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
265
+ width: double.infinity, // 横幅いっぱいに広げる
266
+ height: 60,
267
+ child: ElevatedButton(
268
+ child: Text('キャンセル'),
269
+ onPressed: () {
270
+ print('キャンセル');
271
+ // 前の画面に戻る
272
+ widget._model = _initModel;
273
+ if (Navigator.of(context).canPop()) {
274
+ Navigator.of(context).pop(null);
275
+ }
276
+ },
277
+ style: ElevatedButton.styleFrom(
278
+ primary: Colors.grey[200],
279
+ onPrimary: Colors.black45,
280
+ ),
281
+ ),
282
+ ),
283
+ ],
284
+ ),
285
+ ),
286
+ ));
287
+ }
288
+ }
289
+ ```