PageStorageBucket: Túi Thần Ký Ức Cuộn Trang - Giữ Vững Phong Độ!
Flutter

PageStorageBucket: Túi Thần Ký Ức Cuộn Trang - Giữ Vững Phong Độ!

Author

Admin System

@root

Ngày xuất bản

20 Mar, 2026

Lượt xem

2 Lượt

"PageStorageBucket"

PageStorageBucket: Cái Túi Thần Kỳ Lưu Giữ Ký Ức Cuộn Trang

Chào các chiến thần Gen Z! Hôm nay, anh Creyt sẽ cùng các em 'phẫu thuật' một khái niệm nghe hơi… 'sách vở' nhưng lại cực kỳ 'thực chiến' trong Flutter: PageStorageBucketPageStorageKey. Nghe tên có vẻ phức tạp, nhưng tin anh đi, nó chính là 'người hùng thầm lặng' giúp trải nghiệm app của các em 'mượt như lụa' đó!

1. PageStorageBucket là gì và để làm gì? (Hay: Tại sao cái list của mình cứ 'mất trí' hoài vậy?)

Các em có bao giờ lướt TikTok, cuộn đến mỏi tay, thấy một cái video hay ho rồi bấm vào xem profile của đứa đăng không? Sau đó, bấm nút back quay lại feed, phù, cái feed vẫn y nguyên ở vị trí em vừa cuộn tới, chứ không phải 'nhảy' về đầu trang đúng không? Đó chính là 'phép thuật' của việc lưu giữ trạng thái cuộn (scroll position) đó.

Trong Flutter, các widget có khả năng cuộn như ListView, GridView, CustomScrollView... khi chúng ta rời khỏi màn hình (ví dụ: navigate sang màn hình khác) rồi quay lại, theo 'mặc định' thì chúng sẽ... 'mất trí nhớ'. Tức là, chúng sẽ reset về vị trí cuộn ban đầu (thường là đầu trang). Tưởng tượng đang cuộn một danh sách sản phẩm dài dằng dặc, thấy cái ưng ý, bấm vào xem chi tiết, rồi quay lại thì nó lại 'nhảy' lên đầu. Bực mình không? Bực mình chứ!

Đây chính là lúc PageStorageBucket 'lên sàn'. Các em cứ hình dung nó như một cái 'tủ hồ sơ' thông minh, hoặc chuẩn hơn là một cái 'túi thần kỳ' có khả năng 'ghi nhớ' vị trí cuộn của từng widget scrollable. Khi một widget scrollable được gắn vào một PageStorageBucket, nó sẽ tự động lưu lại vị trí cuộn của mình vào cái túi đó trước khi bị 'biến mất' khỏi màn hình. Và khi nó 'quay trở lại', cái túi sẽ 'nhắc nhở' nó về vị trí cũ. Tuyệt vời chưa!

Còn PageStorageKey là gì? Đơn giản thôi. Nếu PageStorageBucket là cái tủ hồ sơ, thì mỗi cái PageStorageKey chính là cái 'nhãn' hay 'mã số' duy nhất mà các em dán lên từng 'hồ sơ' (tức là từng widget scrollable). Nhờ có cái nhãn này, cái tủ mới biết 'ký ức cuộn' này là của 'ai', để sau này trả lại đúng chỗ. Không có PageStorageKey, cái tủ sẽ không biết phải lưu hay lấy ký ức cho widget nào đâu nha!

2. Code Ví Dụ Minh Họa: 'Hồi Ức' Cho ListView

Để các em dễ hình dung, anh Creyt sẽ dựng một ví dụ đơn giản: Một app có 2 màn hình. Màn hình đầu tiên là một ListView dài, màn hình thứ hai là một màn hình chi tiết. Chúng ta sẽ xem khi có và không có PageStorageKey, trải nghiệm sẽ khác nhau như thế nào.

Bước 1: Chuẩn bị app cơ bản

Illustration

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    // MaterialApp tự động cung cấp một PageStorageBucket mặc định rồi đó các em.
    // Nên thường chúng ta không cần bọc thêm PageStorageBucket bên ngoài nữa.
    return MaterialApp(
      title: 'Flutter PageStorageBucket Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomeScreen(),
    );
  }
}

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  // Tạo một PageStorageKey duy nhất cho ListView này.
  // Đây là 'cái nhãn' để PageStorageBucket nhận diện và lưu trữ vị trí cuộn.
  static const PageStorageKey _scrollKey = PageStorageKey('myScrollableList');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Màn hình chính - List dài dằng dặc'),
      ),
      body: ListView.builder(
        // Đây là chỗ mấu chốt: gắn PageStorageKey vào ListView!
        // Hãy thử comment dòng này và chạy lại để xem sự khác biệt nhé!
        key: _scrollKey, 
        itemCount: 100, // Một list dài 100 items cho đã tay cuộn.
        itemBuilder: (context, index) {
          return Card(
            margin: const EdgeInsets.all(8.0),
            child: ListTile(
              title: Text('Item số $index'),
              subtitle: Text('Đây là chi tiết của item $index'),
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => DetailScreen(itemIndex: index),
                  ),
                );
              },
            ),
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  final int itemIndex;
  const DetailScreen({super.key, required this.itemIndex});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Màn hình chi tiết'),
      ),
      body: Center(
        child: Text(
          'Bạn đang xem chi tiết Item số $itemIndex',
          style: const TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

Giải thích ví dụ:

  1. MyApp: Là widget gốc, MaterialApp tự động tạo ra một PageStorageBucket ở cấp độ cao nhất. Điều này có nghĩa là mọi widget con bên dưới nó đều có thể truy cập và sử dụng PageStorageBucket này. Thường thì các em không cần tự tạo thêm PageStorageBucket đâu.
  2. HomeScreen: Chứa ListView.builder. Đây là nơi chúng ta cần lưu giữ vị trí cuộn.
  3. _scrollKey = PageStorageKey('myScrollableList'): Đây là 'chìa khóa' quan trọng nhất. Anh Creyt đã tạo một PageStorageKey với một giá trị chuỗi duy nhất ('myScrollableList'). Giá trị chuỗi này có thể là bất cứ thứ gì miễn là nó duy nhất trong phạm vi các widget scrollable mà em muốn lưu trạng thái cuộn.
  4. key: _scrollKey: Chúng ta gán _scrollKey này vào thuộc tính key của ListView.builder. Chính nhờ dòng này mà ListView của chúng ta 'có trí nhớ'. Khi em cuộn xuống, bấm vào một item, chuyển sang DetailScreen, rồi pop (quay lại) HomeScreen, ListView sẽ tự động cuộn về đúng vị trí mà em đã rời đi.

Thử nghiệm:

  • Chạy lần 1 (có key: _scrollKey): Cuộn xuống giữa list, bấm vào một item, quay lại. Thấy list vẫn ở vị trí cũ. Tuyệt vời!
  • Chạy lần 2 (comment dòng key: _scrollKey): Cuộn xuống giữa list, bấm vào một item, quay lại. Thấy list 'nhảy' về đầu trang. Bực mình không? Đó là sự khác biệt đó!

3. Mẹo Vặt & Best Practices Từ Anh Creyt (Để không bị 'lú' giữa đường)

  • PageStorageKey là 'linh hồn': Luôn nhớ gán một PageStorageKey cho các widget scrollable mà em muốn lưu trữ vị trí cuộn. Không có nó là 'mất trí' ngay!
  • Đảm bảo Key là duy nhất: Mỗi PageStorageKey nên là duy nhất trong phạm vi mà nó hoạt động. Nếu có hai ListView cùng một PageStorageKey trong cùng một PageStorageBucket, chúng sẽ 'đánh nhau' để giành quyền lưu trữ, và kết quả là không ai nhớ đúng cả.
  • Không phải 'thần dược' cho mọi loại state: PageStorageBucket được thiết kế đặc biệt để lưu vị trí cuộn. Đừng cố gắng dùng nó để lưu các loại state phức tạp khác của widget (như dữ liệu đã nhập vào form, trạng thái bật/tắt của switch...). Đối với các loại state đó, em cần dùng các giải pháp quản lý state khác như Provider, Bloc, Riverpod...
  • Vị trí của PageStorageBucket: Như đã nói, MaterialApp mặc định đã cung cấp một PageStorageBucket rồi. Nhưng nếu em có một cấu trúc widget phức tạp hơn và muốn các Bucket riêng biệt cho các phần khác nhau của ứng dụng, em hoàn toàn có thể bọc một phần widget tree bằng PageStorageBucket mới. Tuy nhiên, trong hầu hết các trường hợp, Bucket mặc định là đủ.

4. Ứng Dụng Thực Tế (Ở Đâu Rồi?)

Nói đâu xa, các em đang dùng PageStorageBucket (hoặc các cơ chế tương tự trong các framework khác) hàng ngày mà không hay biết đó:

  • Mạng xã hội: Instagram, Facebook, TikTok... Khi cuộn feed, xem profile, rồi quay lại, feed vẫn ở đúng chỗ.
  • Ứng dụng đọc tin tức: Các app như VnExpress, Zing News... cuộn danh sách bài viết, bấm vào đọc một bài, rồi quay lại, danh sách vẫn giữ nguyên vị trí.
  • Thương mại điện tử: Shopee, Lazada, Tiki... cuộn danh sách sản phẩm, xem chi tiết, rồi quay lại, danh sách vẫn 'yên vị'.

5. Thử Nghiệm Đã Từng và Hướng Dẫn Nên Dùng Cho Case Nào

Anh Creyt đã từng 'đau đầu' với việc các ListView cứ 'mất trí nhớ' khi làm các app có nhiều tab, mỗi tab là một danh sách. Ban đầu không biết PageStorageBucket, cứ nghĩ phải tự lưu scrollOffset vào Provider hay Bloc, rất lằng nhằng và tốn công. Đến khi phát hiện ra PageStorageKey, mọi thứ như 'mở cờ trong bụng'!

Nên dùng khi nào?

  • Khi em có các widget scrollable (như ListView, GridView, CustomScrollView, PageView...) mà người dùng mong muốn trạng thái cuộn được giữ lại khi họ điều hướng tạm thời ra khỏi màn hình đó và quay lại.
  • Đặc biệt hữu ích trong các ứng dụng có cấu trúc BottomNavigationBar hoặc TabBarView nơi các tab chứa các danh sách cuộn.

Không nên dùng khi nào?

  • Khi nội dung của danh sách thay đổi quá thường xuyên hoặc quá nhanh đến mức việc giữ lại vị trí cuộn không còn ý nghĩa (ví dụ: một danh sách chat real-time mà tin nhắn mới luôn đẩy lên đầu).
  • Đối với các danh sách quá ngắn, việc reset về đầu trang không gây khó chịu cho người dùng.

Vậy đó, PageStorageBucketPageStorageKey không phải là thứ gì đó 'cao siêu' khó hiểu. Nó chỉ là một 'công cụ' nhỏ nhưng cực kỳ hiệu quả để làm cho app Flutter của các em 'có tâm hồn' hơn, 'nhân văn' hơn, và mang lại trải nghiệm người dùng 'đỉnh của chóp'. Thực hành ngay đi nhé các chiến thần!

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!