PageMetrics Flutter: GPS cho trang của bạn, Gen Z ơi!
Flutter

PageMetrics Flutter: GPS cho trang của bạn, Gen Z ơi!

Author

Admin System

@root

Ngày xuất bản

20 Mar, 2026

Lượt xem

1 Lượt

"PageMetrics"

Chào các Gen Z mê code, anh Creyt đây! Hôm nay chúng ta sẽ cùng “mổ xẻ” một khái niệm tuy nhỏ mà có võ, giúp các em “nắm thóp” được mọi chuyển động của các trang trong app Flutter của mình: đó là PageMetrics. Nghe thì có vẻ hàn lâm, nhưng thật ra nó lại là một “GPS” siêu xịn sò cho mấy cái PageView của tụi mình đấy!

1. PageMetrics là gì mà “thần thánh” vậy?

Tưởng tượng thế này: các em đang lướt TikTok, lướt Instagram Story hoặc xem một cuốn catalogue sản phẩm online. Mấy cái đó đều có dạng “trang” mà mình vuốt qua vuốt lại đúng không? PageView trong Flutter chính là cái hộp thần kỳ để chứa mấy cái trang đó.

Thế thì, PageMetrics chính là bộ cảm biến siêu thông minh được gắn vào cái hộp PageView ấy. Nó không chỉ báo cho em biết “đang ở trang số mấy” mà còn chi tiết hơn nhiều: “trang đó đang hiển thị bao nhiêu phần trăm?”, “đã vuốt được bao nhiêu pixel rồi?”, “trang kế tiếp đã lấp ló được bao nhiêu?”. Nói chung, nó là bảng điều khiển toàn diện cho mọi chuyển động của các trang trong PageView của em.

Nó sinh ra là để làm gì ư? Đơn giản thôi: để em có thể tạo ra những hiệu ứng UI “mượt như nhung”, những thanh chỉ số trang (page indicator) thông minh, hay thậm chí là những màn hình onboarding “đỉnh của chóp” mà nội dung thay đổi theo từng milimet chuyển động của ngón tay người dùng. Nó biến một PageView tĩnh thành một vũ đài sống động!

Về mặt kỹ thuật, PageMetrics là một subclass của ScrollMetrics. ScrollMetrics thì rộng hơn, nó mô tả trạng thái của bất kỳ thành phần nào có thể cuộn (scroll) được. Còn PageMetrics thì chuyên biệt hóa cho PageView, nơi mà khái niệm "trang" là cốt lõi.

Các thuộc tính quan trọng nhất của PageMetrics mà anh em mình cần nhớ như in:

  • page (double): Đây là số trang hiện tại. Nhưng đừng nghĩ nó chỉ là số nguyên nhé! Khi em vuốt giữa trang 1 và trang 2, nó có thể là 0.5, 0.7, 1.2, 1.9... Chính cái giá trị double này mới là "vàng" để tạo hiệu ứng động đó.
  • pixels (double): Tổng số pixel đã cuộn từ đầu PageView. Giống như tổng quãng đường đã đi vậy.
  • viewportFraction (double): Phần trăm chiều rộng (hoặc chiều cao nếu cuộn dọc) của viewport mà một trang chiếm. Mặc định là 1.0 (toàn bộ viewport là một trang). Nếu em muốn tạo hiệu ứng mà trang bên cạnh lấp ló một chút, em sẽ chỉnh cái này.
  • viewportDimension (double): Kích thước (chiều rộng hoặc chiều cao) của vùng hiển thị (viewport) của PageView.

2. Code Ví Dụ: PageMetrics “lên sóng”

Để thấy rõ PageMetrics hoạt động thế nào, chúng ta sẽ làm một ví dụ đơn giản: một PageView với 3 trang, và một cái Text hiển thị số trang hiện tại (dạng double) khi chúng ta vuốt.

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'PageMetrics Demo by Creyt',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const PageMetricsScreen(),
    );
  }
}

class PageMetricsScreen extends StatefulWidget {
  const PageMetricsScreen({super.key});

  @override
  State<PageMetricsScreen> createState() => _PageMetricsScreenState();
}

class _PageMetricsScreenState extends State<PageMetricsScreen> {
  double _currentPage = 0.0; // Biến để lưu trữ số trang hiện tại

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('PageMetrics Demo'),
      ),
      body: Column(
        children: [
          // Hiển thị số trang hiện tại
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text(
              'Trang hiện tại: ${_currentPage.toStringAsFixed(2)}',
              style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
          ),
          Expanded(
            child: NotificationListener<ScrollNotification>(
              onNotification: (ScrollNotification notification) {
                // Kiểm tra nếu đây là Notification từ PageView và có PageMetrics
                if (notification.metrics is PageMetrics) {
                  final pageMetrics = notification.metrics as PageMetrics;
                  // Cập nhật trạng thái khi trang thay đổi
                  if (_currentPage != pageMetrics.page) {
                    setState(() {
                      _currentPage = pageMetrics.page!; // page có thể null nếu chưa khởi tạo
                    });
                  }
                }
                // Quan trọng: Trả về false để notification tiếp tục được lan truyền
                // hoặc true để dừng lại ở đây (tùy trường hợp)
                return false;
              },
              child: PageView(
                children: <Widget>[
                  _buildPage(Colors.red, 'Trang 1'),
                  _buildPage(Colors.green, 'Trang 2'),
                  _buildPage(Colors.blue, 'Trang 3'),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildPage(Color color, String text) {
    return Container(
      color: color,
      child: Center(
        child: Text(
          text,
          style: const TextStyle(color: Colors.white, fontSize: 48),
        ),
      ),
    );
  }
}

Trong ví dụ trên:

  • Chúng ta dùng NotificationListener<ScrollNotification> để "nghe lén" mọi sự kiện cuộn xảy ra trong PageView của chúng ta.
  • Khi có một ScrollNotification bắn ra, chúng ta kiểm tra xem notification.metrics có phải là PageMetrics hay không. Nếu đúng, chúng ta ép kiểu và lấy ra đối tượng PageMetrics đó.
  • Từ pageMetrics, chúng ta truy cập thuộc tính page để biết số trang hiện tại (kể cả phần thập phân khi đang vuốt).
  • Cuối cùng, dùng setState để cập nhật UI, hiển thị số trang lên màn hình.
Illustration

3. Mẹo (Best Practices) từ “lão làng” Creyt

Để dùng PageMetrics một cách hiệu quả và không bị “lag” app, anh Creyt có vài tips nhỏ cho các em đây:

  • Đừng setState quá đà: ScrollNotification bắn ra liên tục khi em vuốt. Nếu mỗi lần nó bắn ra mà em lại setState thì app có thể bị giật. Hãy chỉ setState khi giá trị page thực sự thay đổi một cách đáng kể (ví dụ, khi nó vượt qua một ngưỡng nào đó, hoặc khi phần nguyên của page thay đổi). Trong ví dụ trên, anh đã thêm điều kiện if (_currentPage != pageMetrics.page) để tránh setState không cần thiết.
  • Sử dụng Debounce hoặc Throttle: Đối với các hiệu ứng phức tạp hơn, nơi mà mỗi lần ScrollNotification bắn ra đều tốn tài nguyên, hãy cân nhắc dùng kỹ thuật debounce hoặc throttle. Tức là, thay vì xử lý ngay lập tức, em đợi một chút hoặc chỉ xử lý sau mỗi khoảng thời gian nhất định.
  • Hiểu rõ PageController vs PageMetrics:
    • PageController dùng để điều khiển PageView (chuyển trang, nhảy trang, lấy thông tin trang hiện tại).
    • PageMetrics dùng để đọc thông tin chi tiết về trạng thái cuộn của PageView khi nó đang hoạt động, đặc biệt là khi người dùng đang thao tác. Thường thì em sẽ dùng PageMetrics qua NotificationListener để phản ứng với hành động của người dùng, còn PageController để điều khiển hoặc lấy thông tin tại một thời điểm cụ thể.
  • Trả về false cho onNotification: Trong hầu hết các trường hợp, em nên trả về false từ onNotification để các NotificationListener khác (nếu có) hoặc các widget cha vẫn có thể nhận được notification. Trả về true sẽ "nuốt" notification và ngăn nó lan truyền.

4. Ứng dụng thực tế: PageMetrics “bật mode” siêu sao

PageMetrics không chỉ là lý thuyết suông, nó là nền tảng cho rất nhiều tính năng "xịn xò" mà em thấy hàng ngày:

  • Page Indicators (chấm tròn chỉ trang): Đây là ứng dụng kinh điển nhất. Khi em vuốt qua các trang onboarding, các chấm tròn bên dưới sẽ sáng lên hoặc di chuyển mượt mà theo độ lệch của trang. Chính PageMetrics.page (với phần thập phân) giúp các chấm tròn này chuyển động "ăn khớp" với ngón tay của người dùng.
  • Parallax Scrolling Effects: Khi em vuốt một trang, các lớp nội dung khác nhau di chuyển với tốc độ khác nhau, tạo cảm giác chiều sâu. PageMetrics cung cấp thông tin độ lệch chính xác để tính toán tốc độ di chuyển của từng lớp.
  • Onboarding Screens động: Nội dung text, hình ảnh có thể thay đổi độ mờ (opacity), vị trí, hoặc kích thước một cách mượt mà khi người dùng vuốt giữa các trang.
  • Gallery/Carousel ảnh thông minh: Khi đến trang cuối, có thể tự động tải thêm ảnh mới hoặc gợi ý hành động tiếp theo.

Các app như Instagram Stories, Facebook Stories, các ứng dụng đọc báo có carousel ảnh, hay các màn hình giới thiệu sản phẩm của Shopee/Lazada đều ít nhiều dùng đến cơ chế tương tự PageMetrics để tạo ra trải nghiệm mượt mà đó.

5. Thử nghiệm và hướng dẫn nên dùng cho case nào

Anh Creyt đã từng "vật lộn" với PageMetrics (hay các khái niệm tương tự trong các framework khác) rất nhiều lần để tạo ra những hiệu ứng UI độc đáo.

Khi nào nên dùng PageMetrics qua NotificationListener?

  • Khi em muốn phản ứng với hành động vuốt của người dùng theo thời gian thực: Ví dụ, em muốn một thanh tiến trình (progress bar) di chuyển liên tục khi người dùng vuốt giữa các trang, không chỉ khi trang đã dừng hẳn.
  • Khi em cần thông tin độ lệch chính xác (double page value): Để tạo các hiệu ứng chuyển động mượt mà, liên tục mà PageController.page chỉ cung cấp khi trang đã dừng lại hoặc đang chuyển động một cách rõ ràng.
  • Khi em muốn tạo hiệu ứng dựa trên sự "hiện diện" của trang: Ví dụ, một hình ảnh sẽ scale to dần khi nó bắt đầu xuất hiện trong viewport và scale nhỏ lại khi nó khuất dần.

Khi nào nên dùng PageController?

  • Khi em muốn điều khiển PageView: Nhảy đến một trang cụ thể (jumpToPage), chuyển động mượt mà đến một trang (animateToPage).
  • Khi em chỉ cần biết số trang hiện tại đã được chọn (số nguyên) sau khi quá trình cuộn đã dừng lại: pageController.page sẽ cung cấp giá trị này.
  • Khi em muốn lắng nghe sự kiện khi trang đã chuyển đổi hoàn toàn: Dùng addListener trên PageController và kiểm tra pageController.page.

Kinh nghiệm của anh Creyt: Anh từng xây dựng một component carousel ảnh với hiệu ứng parallax và "zoom-in" nhẹ nhàng cho ảnh chính, trong khi ảnh phụ ở hai bên hơi mờ và nhỏ hơn. Toàn bộ hiệu ứng đó được tính toán dựa trên giá trị page (double) từ PageMetrics để điều chỉnh opacity, scaletransform của từng ảnh. Nó đòi hỏi một chút toán học về interpolation (nội suy) nhưng kết quả thì "đáng đồng tiền bát gạo" lắm, nhìn app "pro" hẳn ra.

Tóm lại, PageMetrics là chìa khóa để mở ra thế giới của những UI động, mượt mà trong Flutter PageView. Nắm vững nó, các em sẽ có thêm một "siêu năng lực" để biến những ý tưởng UI phức tạp thành hiện thực! Cứ thử nghiệm đi, đừng ngại sai, đó là cách tốt nhất để học hỏi đấy các Gen Z của anh!

Thuộc Series: Flutter

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!