ウィジェットの状態を保存しようとしています。そのため、ステートフルウィジェットをウィジェットツリーから一時的に削除し、後で再度追加すると、ウィジェットは、削除する前と同じ状態になります。これが私が持っている簡単な例です:
import 'package:flutter/material.Dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool showCounterWidget = true;
@override
Widget build(BuildContext context) {
return Material(
child: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
showCounterWidget ? CounterButton(): Text("Other widget"),
SizedBox(height: 16,),
FlatButton(
child: Text("Toggle Widget"),
onPressed: (){
setState(() {
showCounterWidget = !showCounterWidget;
});
},
)
],
),
),
);
}
}
class CounterButton extends StatefulWidget {
@override
_CounterButtonState createState() => _CounterButtonState();
}
class _CounterButtonState extends State<CounterButton> {
int counter = 0;
@override
Widget build(BuildContext context) {
return MaterialButton(
color: Colors.orangeAccent,
child: Text(counter.toString()),
onPressed: () {
setState(() {
counter++;
});
},
);
}
}
理想的には、状態をリセットしたくないので、カウンターは0にリセットされません。カウンターウィジェットの状態をどのように保存しますか?
ウィジェットが一時的にツリーから削除されたときにウィジェットがその状態を失うのは、ジョシュアが述べたように、ウィジェットが要素/状態を失うためです。
今あなたは尋ねるかもしれません:
Element/Stateをキャッシュして、次にウィジェットが挿入されたときに、新しく作成する代わりに以前のウィジェットを再利用できるようにできませんか?
これは有効なアイデアですが、違います。できません。 Flutterはそれをアンチパターンとして判断し、その状況では例外をスローします。
代わりに、ウィジェットツリー内のウィジェットを無効な状態に保つ必要があります。
このようなことを達成するには、次のようなウィジェットを使用できます。
これらのウィジェットを使用すると、ウィジェットツリー内にウィジェットを保持できます(その状態を維持するため)が、そのレンダリング/アニメーション/セマンティクスは無効になります。
そのため、代わりに:
Widget build(context) {
if (condition)
return Foo();
else
return Bar();
}
それらを切り替えるとFoo
/Bar
の状態が失われます
行う:
IndexedStack(
index: condition ? 0 : 1, // switch between Foo and Bar based on condition
children: [
Foo(),
Bar(),
],
)
このコードを使用すると、Foo
/Bar
はnotの間でやり取りするときに状態を失います。
ウィジェットは、スコープとライフタイム内に独自の一時データを格納することを目的としています。
あなたが提供したものに基づいて、ウィジェットを削除してウィジェットに追加し直すことにより、CounterButton
子ウィジェットを再作成しようとしています木。
この場合、CounterButton
の下のカウンター値は保存されませんでしたまたはは保存されていませんMyHomePage
画面の親ウィジェット、ビューモデルまたは参照への参照なし最上位レベル内または最上位の状態管理。
Flutterがウィジェットをレンダリングする方法のより技術的な概要
ウィジェットのコンストラクタを作成しようとした場合、key
とは何でしょうか?
class CounterButton extends StatefulWidget {
const CounterButton({Key key}) : super(key: key);
@override
_CounterButtonState createState() => _CounterButtonState();
}
キー(key
)は、ウィジェットツリー内のウィジェットのインスタンスを区別するためにFlutterフレームワークによって自動的に処理および使用される識別子です。 削除および追加ウィジェット(CounterButton
)ウィジェットツリーはそれに割り当てられたkey
をリセットします。したがって、ウィジェットツリーが保持するデータとその状態も削除されます。
注:パラメータとして
Widget
のみが含まれる場合、akey
のコンストラクタを作成する必要はありません。
ドキュメントから:
通常、別のウィジェットの唯一の子であるウィジェットは、明示的なキーを必要としません。
FlutterがCounterButtonに割り当てられたキーを変更するのはなぜですか?
Flutterが2つのオブジェクトを完全に区別する理由は、CounterButton
であるStatefulWidget
とText
であるStatelessWidget
を切り替えることです。
Dart Devtoolsを使用して、変更を検査し、Flutterアプリの動作を切り替えることができます。
#3a4d2
の最後にある_CounterButtonState
に注目してください。
これは、ウィジェットを切り替えた後のウィジェットツリー構造です。 CounterButton
からText
ウィジェットへ。
2つのウィジェットが完全に異なるため、CounterButton
が#31a53
で終わることが以前の識別子と異なることがわかります。 。
何ができますか?
ランタイム中に変更されたデータを_MyHomePageState
に保存し、コールバック関数を使用してCounterButton
にコンストラクターを作成し、呼び出しウィジェットの値を更新することをお勧めします。
counter_button.Dart
class CounterButton extends StatefulWidget {
final counterValue;
final VoidCallback onCountButtonPressed;
const CounterButton({Key key, this.counterValue, this.onCountButtonPressed})
: super(key: key);
@override
_CounterButtonState createState() => _CounterButtonState();
}
class _CounterButtonState extends State<CounterButton> {
@override
Widget build(BuildContext context) {
return MaterialButton(
color: Colors.orangeAccent,
child: Text(widget.counterValue.toString()),
onPressed: () => widget.onCountButtonPressed(),
);
}
}
_counterValue
で変数に_MyHomePageState
という名前を付けたとすると、次のように使用できます。
home_page.Dart
_showCounterWidget
? CounterButton(
counterValue: _counterValue,
onCountButtonPressed: () {
setState(() {
_counterValue++;
});
})
: Text("Other widget"),
さらに、このソリューションは、CounterButton
または他の同様のウィジェットをアプリの他の部分で再利用するのに役立ちます。
完全な例を dartpad.dev に追加しました。
AndrewとMattは、Flutterが内部でウィジェットをレンダリングする方法について素晴らしい講演をしました。
さらに読む