やりたいこと
Navigator.pop()で画面を戻って来たときに再描画したい。色々なやり方があるようだが、ある程度実装を進めたので今の設計を崩したくない。画面遷移部分だけの実装で、再描画を実現したい。
やったこと
- 公式ドキュメントのReturn data from a screenがほぼ全てだが、もとの画面に戻る際に値を受け取り、その値を見て再描画を実行する。
- 呼び出し元画面でNavigator.push()の実行をasyncメソッドで実装し、await実行することで、呼び出し先画面から戻った際に、そのメソッドに戻るようにする。
- あとはそのメソッド内で再描画をコールする。
ソースコード
今回は関連する部分のみ記述する。
まずは呼び出し元画面
FirstPage.dart
@override
Widget build(BuildContext context) {
return Container(
:
child: RaisedButton( // [*1]
onPressed: () {
pushWithReloadByReturn(context);
},
child: Text("GoSecondPage"),
);
}
void pushWithReloadByReturn(BuildContext context) async { // [*2]
final result = await Navigator.push( // [*3]
context,
new MaterialPageRoute<bool>( // [*4]
builder: (BuildContext context) => SecondPage(),
),
);
if (result) { // [*5]
// setState(() {});
// notifyListeners();
}
}
- 画面遷移するボタン。押下時に下で定義したasyncメソッドを呼び出している。
- asyncで画面遷移を実装したメソッド。次の画面からNavigator.pop()で戻った際に、このメソッドに返ってくる。
- awaitでNavigator.push()を呼ぶことで、画面が戻ってくるまでここで待ち続ける。
- 戻り値のクラスを<bool>に設定している。ここをStringや他のクラスにすることでいろいろな戻り値を受け取れる。今回はboolなので、当然[*3]のresultには、bool値が入る。
- あとはresultの値を見て、StatefulWidgetならsetState()、ChangeNotifierProviderを使用しているならnotifyListeners()といったように、再描画の処理を呼び出せば良い。
次に呼び出された画面
SecondPage.dart
@override
Widget build(BuildContext context) {
return Container(
:
child: RaisedButton(
onPressed: () {
Navigator.pop(context, true); // [*1]
},
child: Text("ReturnFirstPage"),
);
}
- こちらはシンプルで、Navigator.pop()の第2引数(呼び出し元ページに返却する値)にbool値を渡すだけ。今回の例では、FirstPage側の実装で、この値を見て再描画するかどうかを判定させているので、ここでtrueを送れば戻った直後に画面が再描画され、falseを送れば再描画はされないことになる。
やってみて
いろいろな記事を調べて試行錯誤しつつ、最終的にこの方法にたどり着いたが、きちんとやるならNavigatorObserverを使ってdidPopなどを実装するほうが汎用性が高そうだった。まとまった時間が取れたら、一度全体的なリファクタリングを行いたい。