🚪

画面遷移が遅いとWidgetが重なる

2024/11/12に公開

💡Tips

美しいグラデーションを使用したログインページのUIを作成して画面遷移をさせてみたのだが、次のページに遷移したときに動きが遅いせいか入力フォームとボタンが重なって見えて違和感を感じた。

なんとかならないか?
いつもは感じなかったのだが、入力フォームのデザインを変えたからだろうか?
入力フォームを角丸にするためにわざわざContainerで囲んでいたのだが、これは正しいのかと思うことがあった?

正規表現のバリデーションを表示したときだとUI崩れることがあった。他の方法でデザイン角丸の入力フォームにした方が良いなと思った。
でやってみたら、対策が必要になった💦

解決策

画面遷移のスピードを早くするために、MaterialPageRouteの遷移時間をカスタマイズして対応した。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

// カスタム遷移用のルート
class FastPageRoute<T> extends MaterialPageRoute<T> {
  FastPageRoute({
    required WidgetBuilder builder,
  }) : super(
    builder: builder,
    // 遷移時間を100ミリ秒に短縮(デフォルトは300ミリ秒)
    maintainState: true,
  );

  
  Duration get transitionDuration => const Duration(milliseconds: 100);

  
  Duration get reverseTransitionDuration => const Duration(milliseconds: 100);
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: Colors.black,
        scaffoldBackgroundColor: Colors.transparent,
        appBarTheme: const AppBarTheme(
          backgroundColor: Colors.black,
          foregroundColor: Colors.white,
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.black,
            foregroundColor: Colors.white,
            minimumSize: const Size.fromHeight(56),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(10),
            ),
          ),
        ),
      ),
      home: Container(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.centerLeft,
            end: Alignment.centerRight,
            colors: [
              Color.fromRGBO(253, 219, 146, 1),
              Color.fromRGBO(209, 253, 254, 1),
            ],
          ),
        ),
        child: const LoginPage(),
      ),
    );
  }
}

class LoginPage extends StatelessWidget {
  const LoginPage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Gradient App'),
      ),
      body: ListView(
        padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
        children: [
          const SizedBox(height: 16),
          const Text(
            "メールアドレス",
            style: TextStyle(
              fontWeight: FontWeight.bold,
              fontSize: 16,
            ),
          ),
          const SizedBox(height: 8),
          TextFormField(
            keyboardType: TextInputType.emailAddress,
            decoration: InputDecoration(
              hintText: 'メールアドレス',
              filled: true,
              fillColor: Colors.white,
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(10),
                borderSide: BorderSide.none,
              ),
            ),
          ),
          const SizedBox(height: 24),
          const Text(
            "パスワード",
            style: TextStyle(
              fontWeight: FontWeight.bold,
              fontSize: 16,
            ),
          ),
          const SizedBox(height: 8),
          TextFormField(
            obscureText: true,
            decoration: InputDecoration(
              hintText: 'パスワード',
              filled: true,
              fillColor: Colors.white,
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(10),
                borderSide: BorderSide.none,
              ),
            ),
          ),
          const SizedBox(height: 32),
          ElevatedButton(
            onPressed: () {
              Navigator.of(context).push(
                FastPageRoute(
                  builder: (context) => Container(
                    decoration: const BoxDecoration(
                      gradient: LinearGradient(
                        begin: Alignment.centerLeft,
                        end: Alignment.centerRight,
                        colors: [
                          Color.fromRGBO(253, 219, 146, 1),
                          Color.fromRGBO(209, 253, 254, 1),
                        ],
                      ),
                    ),
                    child: const NextPage(),
                  ),
                ),
              );
            },
            child: const Text(
              'Login',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class NextPage extends StatelessWidget {
  const NextPage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Next Page'),
      ),
      body: const Center(
        child: Text(
          "Next Page",
          style: TextStyle(
            fontSize: 24,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
    );
  }
}

動作

https://www.youtube.com/shorts/K9O3d5oTetg

主な変更点:

  1. カスタム遷移ルートの作成

    • FastPageRoute クラスを作成
    • 遷移時間を100ミリ秒に短縮(デフォルトの3分の1)
    • 戻る時の遷移時間も同様に短縮
  2. グラデーション背景の継承

    • 次の画面でもグラデーション背景を維持
    • 遷移時の見た目の一貫性を保持

これにより、画面遷移がより素早く、スムーズになります。必要に応じて遷移時間(milliseconds: 100)を調整することができます:

  • より速くしたい場合は値を小さく(例:50ms)
  • よりアニメーションを感じさせたい場合は値を大きく(例:150ms)

また、アニメーションを完全に無効化したい場合は、以下のような方法も可能です:

Navigator.of(context).pushReplacement(
  PageRouteBuilder(
    pageBuilder: (context, animation1, animation2) => const NextPage(),
    transitionDuration: Duration.zero,
    reverseTransitionDuration: Duration.zero,
  ),
);

1秒は早すぎるので3秒が丁度良さそう。

// カスタム遷移用のルート
class FastPageRoute<T> extends MaterialPageRoute<T> {
  FastPageRoute({
    required super.builder,
  }) : super(
    maintainState: true,
  );

  
  Duration get transitionDuration => const Duration(milliseconds: 300);

  
  Duration get reverseTransitionDuration => const Duration(milliseconds: 300);
}

Discussion