🐶
Flutterの画面遷移の実装Tips
画面遷移させる際のコードをスッキリさせたい
Navigatorで遷移させる際に、Navigator.of(context).push()) を使う場合
HogePage()に遷移したいだけで、結構コードを書かされる。
// onPressed() のなかで遷移
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return HogePage();
},
),
);
これを、↓を呼ぶだけにしたい。
// onPressed() のなかで遷移
HogePage.push(context);
// 動作確認
// 呼び出し側(FirstPage -> HogePageへの遷移)
class FirstPage extends StatelessWidget {
const FirstPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Spacer(),
Text("First Page"),
Spacer(),
IconButton(icon: Icon(Icons.navigate_next), onPressed: () {
HogePage.push(context, "");
},),
Spacer(),
],
),
),
);
}
}
実装
やること
- HogePageを例に、static push() を定義
// HogePageに、static push()の遷移処理を定義したサンプル
class HogePage extends StatefulWidget {
// HogePageに遷移させる処理
// 利用側の実装: HogePage.push(context);
static push(BuildContext context) {
Navigator.of(context).push<dynamic>(
MaterialPageRoute<dynamic>(
builder: (_) => HogePage(),
));
}
const HogePage({super.key});
State<HogePage> createState() => _HogePageState();
}
class _HogePageState extends State<HogePage> {
void initState() {
super.initState();
print("initState! HogePage!");
}
void dispose() {
super.dispose();
print("dispose! HogePage!");
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Column(children: [
Spacer(),
Text("Hoge Page!!"),
Spacer(),
BackButton(onPressed: () {
Navigator.pop(context); // 戻る
}
),
Spacer(),
],))
);
}
}
// 画面させる際のコード
HogePage.push(context);
以降、おまけ
実装2(遷移時に値を渡したい場合)
class HogePage extends StatefulWidget {
static push(BuildContext context, String inputString) async {
Navigator.of(context).push<dynamic>(
MaterialPageRoute<dynamic>(
builder: (_) => HogePage(inputString: inputString),
));
}
// 持たせたい値を定義
final String inputString;
// コンストラクタの引数にも設定
const HogePage({super.key, this.inputString});
State<HogePage> createState() => _HogePageState();
}
class _HogePageState extends State<HogePage> {
void initState() {
super.initState();
print("initState! HogePage!");
}
void dispose() {
super.dispose();
print("dispose! HogePage!");
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Column(children: [
Spacer(),
Text("Hoge Page!!"),
Text(widget.inputString), // widget. で、引数にアクセス
Spacer(),
BackButton(onPressed: () {
Navigator.pop(context); // 戻る
}
),
Spacer(),
],))
);
}
}
// 遷移させる際のコード
HogePage.push(context, "this is input string");
その他のメリット
-
型でキー入力時に、補完が効くのがよい。
-
initState()の代わりになる(※StatelessWidgetの場合)
→ HogePageが、StatelessWidgetだった場合に、initState()的なライフサイクル処理を埋め込む余地ができる。
static push() の中に、処理を書けば、画面遷移時に1度しか実行されないため。
ここ、初期化時のデータ取得処理(awaitな処理)を埋め込むこともできる。
また、取得したデータは、「コンストラクタの引数」に設定したり、Provider経由で渡したりで対応。
awaitの処理が長い場合、UI表示はどうなるか?
static push() の中で、重い(長い)Future処理をawaitする場合、
画面遷移せず最初の画面のまま5秒経過し、そのあと、次画面に遷移する。
await Future.delayed(Duration(seconds: 5));
(↑を、埋め込んで確認済み)
対応としては、
・await を敢えてつけずに処理を実行(すぐに画面遷移させることが可能)
・await をつける場合、Futureの処理が完了してから画面遷移処理が走るので、その間、画面が止まっている状態となる。グローバルなローディングUI表示で対応が必要そう。
GlobalDialog.show(); // くるくる表示
await Future.delayed(Duration(seconds: 5));
GlobalDialog.hidden(); // くるくるを消す
Discussion