ttlog

日々の開発で得た知見の技術メモ。モバイルアプリネタが多いです。

Flutterで画面遷移

Flutterにおける画面遷移の基本です。 以下の2通りの遷移方法があるようです。

  • 直接画面を生成して遷移
  • ルーティングを定義して名前で遷移

ここで紹介する方法は、iOS的にはNavigationスタイル(Push/Pop)の画面遷移になります。

概要

Navigatorクラスを使用します。

このクラスはRouteオブジェクトをスタックとして管理するものです。 Flutterでは、一般的に画面だとかページだとか言われるものをRouteと呼ぶそうです。

Navigator.pushもしくはNavigator.pushNamed関数により遷移し、Navigator.pop関数で前画面に戻ります。

画面の定義

まずは遷移用の画面を定義します。

遷移前の画面(FirstScreen)にRaisedButtonを2つ置き、タップ時にそれぞれのパターンで遷移をします。

遷移後の画面(SecondScreen)にはRaisedButtonを1つ置き、タップ時に前画面(FirstScreen)に戻ります。

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('First Screen'),
        ),
        body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                    child: Text('Navigate directly'),
                    onPressed: () {
                      // 直接画面を生成して遷移
                    }
                ),
                RaisedButton(
                    child: Text('Navigate with named route'),
                    onPressed: () {
                      // ルーティングを定義して名前で遷移
                    }
                ),
              ],
            )
        )
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Second Screen'),
        ),
        body: Center(
            child: RaisedButton(
                child: Text('Go back!'),
                onPressed: () {
                  // 前画面に戻る
                }
            )
        )
    );
  }
}

直接画面を生成して遷移

Navigator.push関数を使用し、Routeを指定して遷移します。

RouteにはMaterialPageRouteを指定しておけば、自動でプラットフォーム固有のトランジションアニメーションを実現してくれます。 (むしろ他のRouteをどうやって作るのかはまだ分かってません)

RaisedButton(
    child: Text('Navigate directly'),
    onPressed: () {
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => SecondScreen())
      );
    }
),

ルーティングを定義して名前で遷移

MaterialAppの初期化時にルーティングを定義し、 Navigator.pushNamed関数を使用して遷移します。

  • ルーティング定義
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
    
      ...
      
      // 初回起動画面を指定
      initialRoute: '/',

      // ルーティングの定義
      routes: {
        '/': (context) => FirstScreen(),
        '/second': (context) => SecondScreen(),
      },
    
      ...
    
    );
  }
}
  • 遷移処理
RaisedButton(
    child: Text('Navigate with named route'),
    onPressed: () {
      // ルーティングで定義した名前を指定して遷移
      Navigator.pushNamed(context, "/second");
    }
),

前画面に戻る

Navigator.pop関数を使用します。

RaisedButton(
    child: Text('Go back!'),
    onPressed: () {
      Navigator.pop(context);
    }
)

ちなみに、画面のWidgetScaffoldを使用している場合はAppBarの左側に自動で戻るボタンが表示されるので、そちらから戻ることも出来ます。

ソース全体

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,
      ),
      initialRoute: '/',
      routes: {
        '/': (context) => FirstScreen(),
        '/second': (context) => SecondScreen(),
      },
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('First Screen'),
        ),
        body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                    child: Text('Navigate directly'),
                    onPressed: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(builder: (context) => SecondScreen())
                      );
                    }
                ),
                RaisedButton(
                    child: Text('Navigate with named route'),
                    onPressed: () {
                      Navigator.pushNamed(context, "/second");
                    }
                ),
              ],
            )
        )
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Second Screen'),
        ),
        body: Center(
            child: RaisedButton(
                child: Text('Go back!'),
                onPressed: () {
                  Navigator.pop(context);
                }
            )
        )
    );
  }
}

参考