Custom page transition – Flutter

1817

Bài viết được sự cho phép của tác giả Khiêm Lê

Custom page transition

Để custom page transition trong flutter, các bạn sẽ thực hiện animation khi một route mới được thêm vào Stack Navigator. Ví dụ như thông qua push, pushNamed… Để làm được điều này, chúng ta sẽ sử dụng thuộc tính onGenerateRoute của MaterialApp.

  Biết chọn gì đây? Flutter, React Native hay Xamarin?
  Chat app đơn giản với Flutter

Thuộc tính onGenerateRoute của MaterialApp là một function, function này sẽ được thực hiện khi chúng ta push một route vào Stack Navigator.

Thuộc tính onGenerateRoute nhận vào một tham số là RouteSettings, chúng ta sẽ cần thuộc tính name từ tham số này. Chúng ta cần trả về một PageRouteBuilder và sử dụng 3 thuộc tính của nó là pageBuilder, transitionDuration và transitionsBuilder.

Thuộc tính pageBuilder là thuộc tính xác định route nào sẽ được trả về (tức route mà bạn đang push vào stack navigator). Nhận vào một hàm có tham số lần lượt là Context, Animation, Animation, chúng ta chỉ cần quan tâm đến tham số Context, đơn giản bạn chỉ cần trả về route được push dựa vào thuộc tính name của tham số settings được truyền vào thuộc tính onGenerateRoute sử dụng Context.

Vậy để có thể nhận biết được route nào được push vào, các bạn phải sử dụng routes map (tương tự như thuộc tính routes trong MaterialApp). Thêm nữa, để có thể truy cập được routes từ trong các thuộc tính, chúng ta cần phải để biến routes map là biến toàn cục.

Thuộc tính tiếp theo là transitionsBuilder, thuộc tính này cho phép chúng ta custom transition cho page. Thuộc tính này nhận vào một hàm có tham số lần lược là Context, Animation, Animation và child. Ở lần này, chúng ta sẽ sử dụng hai tham số là animation thứ nhất và child.

Tham số animation thứ nhất là để chúng ta thực hiện animation khi navigate route. Do đó, chúng ta sẽ sử dụng các thuộc tính này trong thuộc tính animation value của các widget như SlideTransition, ScaleTransition… và thuộc tính child chính là tham số child truyền vào.

Các Transition Widget đều có thể lồng nhau, do đó, các bạn có lồng nhiều Transition Widget vào nhau để tạo ra các hiện ứng transition đẹp mắt của riêng bạn.

Thuộc tính cuối cùng là transitionDuration, mình có thể custom duration của animation khi navigate route. Thuộc tính này đơn giản nhận vào một Duration, các bạn có thể để thời gian bao lâu tùy thích, milliseconds, seconds…

Ví dụ như mình muốn có hiệu ứng vừa slide, vừa scale từ dưới lên và fade luôn, thì mình sẽ có đoạn code sau:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'screens/screens.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  final _routes = {
    HomeScreen.id: (context) => HomeScreen(),
    SecondScreen.id: (context) => SecondScreen(),
  };

  
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: HomeScreen.id,
      onGenerateRoute: (settings) {
        return PageRouteBuilder(
          pageBuilder: (context, animation, secondAnimation) {
            return _routes[settings.name](context);
          },
          transitionDuration: Duration(
            milliseconds: 300,
          ),
          transitionsBuilder: (context, animation, secondAnimation, child) {
            return SlideTransition(
              position: animation.drive(
                Tween(
                  begin: Offset(0, 1),
                  end: Offset(0, 0),
                ).chain(CurveTween(curve: Curves.ease)),
              ),
              child: ScaleTransition(
                scale: animation,
                child: FadeTransition(opacity: animation, child: child),
              ),
            );
          },
        );
      },
    );
  }
}

iOS page transition

Nếu các bạn muốn ứng dụng của bạn có hiệu ứng transition như trên iOS, bạn có thể sử dụng Widget CupertinoPageRoute thay cho Widget PageRouteBuilder.

Không giống như Widget PageRouteBuilder, Widget CupertinoPageRoute không đòi hỏi bạn phải custom quá nhiều. Bạn chỉ cần trả về Widget CupertinoPageRoute, bên trong Widget này có thuộc tính builder nhận vào một funciton có tham số là Context, bạn chỉ cần trả về một route dựa trên route name.

Mình sẽ code như sau để có hiệu ứng transition như trên iOS:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'screens/screens.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  final _routes = {
    HomeScreen.id: (context) => HomeScreen(),
    SecondScreen.id: (context) => SecondScreen(),
  };

  
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: HomeScreen.id,
      onGenerateRoute: (settings) {
        return CupertinoPageRoute(
          builder: (context) {
            return _routes[settings.name](context);
          },
        );
      },
    );
  }
}

Đối với Widget CupertinoPageRoute, các bạn không thể custom được gì nhiều, chỉ dùng là có transition effect như iOS thôi, tù y hệt như trên iOS luôn.

Tổng kết

Một lưu ý nhỏ là nếu các bạn sử dụng thuộc tính routes trong MaterialApp, hàm trong thuộc tính onGenerateRoute sẽ không được gọi do nó đã tìm được route trong thuộc tính routes rồi. Do đó, các bạn không nên truyền routes vào cho thuộc tính routes của MaterialApp nếu muốn custom transition hoạt động.

Qua bài này, mình đã hướng dẫn các bạn cách custom page transition flutter đơn giản bằng việc sử dụng các Widget Transition có sẵn. Nếu bạn thấy video và bài viết này hay, đừng quên chia sẻ cho bạn bè được biết. Cảm ơn các bạn đã theo dõi bài viết!

Bài viết gốc được đăng tải tại khiemle.dev

Có thể bạn quan tâm:

Xem thêm các việc làm Developer hấp dẫn tại TopDev