🐶

Flutterの画面遷移の実装Tips

2024/01/22に公開

画面遷移させる際のコードをスッキリさせたい

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