🦅

Flutterの画面遷移(Navigator)をエフェクトを使ってかっこよく、コードはスッキリさせたい

2020/10/12に公開

発生した問題

  1. FlutterのNavigatorはデフォルトだと下から上へのスライドしかない(フェードや横のスライドを入れたい)
  2. Routeの設定をMaterialAppに記載していくと、MaterialAppの中身が肥大化する。

作成したコードのイメージ

main.dart
class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        Locale('ja', 'JP'),
      ],
      theme: ThemeData(
        primarySwatch: Colors.brown,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      // ここに定義することで、
      // Navigator.of(context).pushNamed("/aaa");
      // で遷移できるが、MaterialAppが長くなることに加えて、
      // 下から上への画面遷移になってしまうのが嫌だ。
      routes: <String, WidgetBuilder>{
        '/aaa': (_) => new AAA(),
        '/bbb': (_) => new BBB(),
        '/ccc': (_) => new CCC(),
      },
      home: MyHomePage(title: 'My App'),
    );
  }
}

環境

  • macOS Catalina 10.15.4
  • Android Studio 3.6.1
  • Flutter 1.17.0

結論

1. FlutterのNavigatorはデフォルトだと下から上へのスライドしかない(フェードや横のスライドを入れたい)

page_transitionを使うことで解決しました。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  page_transition: ^1.1.5
$ flutter pub get
main.dart
class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        Locale('ja', 'JP'),
      ],
      theme: ThemeData(
        primarySwatch: Colors.brown,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      // ここ
      // Navigator.of(context).pushNamed("/aaa");
      // で遷移できることに加えて、遷移のエフェクトを設定できる。
      // しかしながら、MaterialAppがさらに長くなってしまった。
      onGenerateRoute: (settings) {
       switch (settings.name) {
        // よく見る右から左へ画面遷移するパターン
        case '/aaa':
          return PageTransition(
            child: AAA(),
            type: PageTransitionType.rightToLeft,
            settings: settings,
          );
          break;
        // じんわりフェードで画面遷移するパターン
        case '/bbb':
          return PageTransition(
            child: BBB(),
            type: PageTransitionType.fade,
            settings: settings,
          );
          break;
        // Navigatorのデフォルト遷移パターン
        case '/ccc':
          return PageTransition(
            child: CCC(),
            type: PageTransitionType.downToUp,
            settings: settings,
          );
          break;
        default:
          return null;
    }
      }
      home: MyHomePage(title: 'My App'),
    );
  }
}

このほかにもエフェクトが使えるので、page_transitionを見てみてください。

2. Routeの設定をMaterialAppに記載していくと、MaterialAppの中身が肥大化する。

他のLaravelやNuxtのようにRouterファイルとして切り出しました。
まずは、router.dart というファイルでrouteとエフェクトを定義してから、

router.dart
import 'package:flutter/material.dart';
import 'package:page_transition/page_transition.dart';
// 適宜呼びたいページをimportしてください

mixin PageRouter implements StatelessWidget {

  static Route generate (RouteSettings settings) {
     switch (settings.name) {
        // よく見る右から左へ画面遷移するパターン
        case '/aaa':
          return PageTransition(
            child: AAA(),
            type: PageTransitionType.rightToLeft,
            settings: settings,
          );
          break;
        // じんわりフェードで画面遷移するパターン
        case '/bbb':
          return PageTransition(
            child: BBB(),
            type: PageTransitionType.fade,
            settings: settings,
          );
          break;
        // Navigatorのデフォルト遷移パターン
        case '/ccc':
          return PageTransition(
            child: CCC(),
            type: PageTransitionType.downToUp,
            settings: settings,
          );
          break;
        default:
          return null;
    }
  }
}

main.dart で作成したクラスメソッドを呼び出すだけでOK

main.dart
class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        Locale('ja', 'JP'),
      ],
      theme: ThemeData(
        primarySwatch: Colors.brown,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      // ここで呼び出すだけでOK!
      onGenerateRoute: PageRouter.generate
      home: MyHomePage(title: 'My App'),
    );
  }
}

今後、ページの追加があるときにはrouter.dart を編集すればOKです。
何か修正点やもっといい案があればぜひコメント欄でください!

Discussion