
Ê mấy đứa, hôm nay anh Creyt lại có món ngon cho tụi bây đây! Trong thế giới Flutter đầy rẫy những widget, đôi khi chúng ta gặp phải mấy cái tên nghe 'hàn lâm' muốn xỉu, nhưng thực ra lại là mấy 'vũ khí bí mật' giúp app mình mượt mà như bơ, không giật không lag. Và hôm nay, 'vũ khí' mà anh muốn giới thiệu chính là SliverPrototypeExtentList.
Nghe cái tên đã thấy 'pro' rồi đúng không? Nhưng đừng lo, anh sẽ bóc tách nó ra từng miếng nhỏ cho tụi bây dễ nuốt. Tưởng tượng thế này: tụi bây đang lướt TikTok, Instagram, hay YouTube Shorts, cái feed nó cứ cuộn, cuộn mãi mà không thấy giật tí nào. Đó không phải tự nhiên đâu, đằng sau đó là cả một 'binh đoàn' các kỹ thuật tối ưu, và SliverPrototypeExtentList là một trong những 'chiến binh' thầm lặng đó.
1. SliverPrototypeExtentList Là Gì Mà Nghe Có Vẻ Ghê Gớm Vậy Anh?
Trước hết, mình phải hiểu 'Sliver' là gì đã. Tưởng tượng màn hình điện thoại của tụi bây là một tờ giấy dài, và cái tờ giấy đó có thể cuộn lên cuộn xuống. Một 'Sliver' chính là một 'mảnh ghép' nhỏ, một 'lát cắt' của cái tờ giấy đó. Nó không phải là cả một tờ giấy to đùng (như ListView bình thường), mà là một phần nhỏ, linh hoạt, có thể tự cuộn hoặc kết hợp với các 'Sliver' khác để tạo thành một khu vực cuộn lớn hơn. Điều này giúp Flutter chỉ cần vẽ những gì đang hiển thị trên màn hình, tiết kiệm tài nguyên cực kỳ.
Còn 'PrototypeExtentList' thì sao? Đây mới là phần hay ho này. Khi tụi bây có một danh sách cực dài (ví dụ: hàng ngàn bài post, hàng ngàn comment), mà tất cả các bài post đó nhìn chung có cùng một chiều cao (hoặc chiều rộng, nếu cuộn ngang) thì sao? Bình thường, Flutter sẽ phải 'đo' từng item một khi nó chuẩn bị xuất hiện trên màn hình để biết nó cao bao nhiêu, từ đó mới tính toán được tổng chiều dài của cái list và vị trí cuộn. Cái việc 'đo đạc' này, tuy nhỏ, nhưng nếu làm với hàng trăm, hàng ngàn item, nó sẽ gây ra cái mà dân dev gọi là 'jank' – tức là cảm giác giật giật, khựng khựng khi cuộn.
SliverPrototypeExtentList ra đời để giải quyết bài toán này. Nó hoạt động như một 'thằng nhóc tiền trạm' thông minh. Thay vì đo tất cả, nó chỉ cần tụi bây cung cấp MỘT item MẪU (prototype item) – coi như là 'đại diện' cho tất cả các item khác. Nó sẽ bí mật render (vẽ) cái item mẫu này ngoài màn hình để 'đo đạc' chiều cao của nó. Sau khi có được chiều cao của 'thằng tiền trạm' này, nó sẽ 'tự động hiểu' rằng TẤT CẢ các item còn lại trong danh sách cũng sẽ có chiều cao tương tự.
Vậy là từ giờ, Flutter không cần phải đo đạc từng cái item nữa! Nó chỉ cần biết chiều cao của 'thằng mẫu' là xong, rồi cứ thế mà nhân lên với số lượng item để tính toán vị trí cuộn và tổng chiều dài list. Kết quả: App của tụi bây cuộn mượt mà như bơ, không một chút giật lag nào, dù danh sách có dài đến mấy đi chăng nữa. Ngon lành cành đào chưa?
2. Code Ví Dụ Minh Hoạ Rõ Ràng, Chuẩn Kiến Thức
Để tụi bây dễ hình dung, anh Creyt sẽ phác thảo một ví dụ đơn giản nhé. Tưởng tượng tụi bây có một danh sách các 'bài đăng' (post) trên mạng xã hội, và các bài đăng này có cấu trúc khá giống nhau, nên chiều cao của chúng cũng xêm xêm nhau.
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: 'SliverPrototypeExtentList Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
// Giả lập một danh sách các bài đăng rất dài
final List<String> _posts = const [
'Flutter là số 1!',
'Học SliverPrototypeExtentList để app mượt như lụa.',
'Creyt giảng bài dễ hiểu bá cháy con bọ chét!',
'Làm dev Gen Z phải biết tối ưu hiệu năng chứ!',
'Cuộn cuộn, lướt lướt, không giật lag là auto yêu.',
'Widget này hay ho phết, dùng cho list dài là chuẩn.',
'Đừng quên like và subscribe kênh anh Creyt nhé!',
'Hôm nay có ai học được gì mới không?',
'Mỗi dòng là một post, chiều cao tương đối.',
'Thử kéo xuống cuối xem có mượt không nhé!',
'Flutter community is awesome!',
'Dart is a beautiful language.',
'Build amazing UIs with Flutter.',
'Performance matters in mobile apps.',
'Slivers provide great flexibility.',
'Prototype item is the key here.',
'Understand the why behind the how.',
'Keep learning, keep building.',
'Gen Z devs are the future!',
'This list goes on and on...',
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('SliverPrototypeExtentList Demo'),
),
body: CustomScrollView(
slivers: <Widget>[
// Đây là prototype item. Nó sẽ được render ngoài màn hình
// để tính toán chiều cao. Quan trọng là nó phải đại diện
// cho kích thước của các item thật.
SliverPrototypeExtentList(
// Key cho prototype item, giúp Flutter nhận diện.
// Có thể bỏ qua nếu không cần thiết, nhưng dùng thì tốt hơn.
prototypeItem: _buildPostItem('Đây là một bài đăng mẫu để đo chiều cao.'),
// Delegate cung cấp các item thật sự cho danh sách.
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
// Các item thật sự trong danh sách
// Chúng ta giả định tất cả đều có cấu trúc và chiều cao tương tự
// như prototypeItem.
return _buildPostItem(_posts[index % _posts.length]);
},
childCount: 1000, // Một danh sách rất dài để thấy hiệu quả
),
),
],
),
);
}
// Hàm tạo một widget bài đăng đơn giản
Widget _buildPostItem(String text) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3), // changes position of shadow
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Người dùng Gen Z',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
const SizedBox(height: 4),
Text(
text,
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Icon(Icons.thumb_up_alt_outlined, size: 18, color: Colors.grey),
Icon(Icons.comment_outlined, size: 18, color: Colors.grey),
Icon(Icons.share_outlined, size: 18, color: Colors.grey),
],
),
],
),
);
}
}
Trong ví dụ trên, cái _buildPostItem chính là cái 'khuôn' để tạo ra các bài đăng. Anh dùng nó để tạo cả prototypeItem lẫn các item thật trong SliverChildBuilderDelegate. Điều này đảm bảo rằng 'thằng tiền trạm' và 'binh đoàn' của nó có cùng kích thước, và đó là chìa khóa để SliverPrototypeExtentList hoạt động hiệu quả.

3. Một Vài Mẹo (Best Practices) Từ Anh Creyt Để Nhớ Và Dùng Thực Tế
Để không biến 'vũ khí bí mật' thành 'vũ khí tự hủy', tụi bây nhớ mấy mẹo này nhé:
- Chỉ dùng khi 'Đồng phục':
SliverPrototypeExtentListchỉ phát huy tối đa sức mạnh khi TẤT CẢ các item trong danh sách của tụi bây có chiều cao (hoặc chiều rộng) gần như nhau. Nếu item cao thấp khác nhau quá nhiều, nó sẽ tính toán sai, và kết quả là… vẫn giật lag như thường, hoặc thậm chí còn tệ hơn vì nó 'đo nhầm'. Lúc đó thà dùngSliverListbình thường còn hơn. - 'Thằng tiền trạm' phải chuẩn: Cái
prototypeItemmà tụi bây cung cấp phải là một bản sao chính xác (về cấu trúc và kích thước) của các item thật. Đừng có đưa một cái prototype đơn giản quá, trong khi item thật lại phức tạp hơn nhiều. Nó sẽ đo sai bét nhè. - Tránh 'kích thước động' trong Prototype: Trong
prototypeItem, hạn chế tối đa các widget có thể thay đổi kích thước của nó một cách linh hoạt (ví dụ:Expanded,Flexiblemà không cóflexcụ thể, hoặc text mà không có giới hạnmaxLinesrõ ràng nếu nó có thể co giãn). Mục tiêu là để nó có một kích thước cố định, dễ đo đạc. - Hiểu về 'Jank': Hãy nhớ, mục đích chính là giảm 'jank' – tức là những khoảnh khắc mà UI bị khựng lại do CPU và GPU phải làm việc quá sức để tính toán và vẽ.
SliverPrototypeExtentListgiúp giảm tải cho chúng bằng cách 'đo trước' một lần duy nhất. - Không phải lúc nào cũng cần: Nếu list của tụi bây ngắn, hoặc các item có kích thước đã được xác định rõ ràng từ đầu (ví dụ: tất cả đều cao 50px), thì dùng
SliverFixedExtentListhoặc thậm chíListViewbình thường vớiitemExtentcòn dễ hơn và hiệu quả tương tự.
4. Ứng Dụng Thực Tế: Ai Đã Dùng 'Vũ Khí' Này?
Mấy cái app mà tụi bây hay dùng hàng ngày, khả năng cao là họ đã dùng những kỹ thuật tương tự (hoặc chính nó) để tối ưu hiệu năng đó:
- Feed mạng xã hội (Facebook, Instagram, TikTok): Mặc dù các bài đăng có thể khác nhau về nội dung (ảnh, video, chữ), nhưng thường thì các 'khung' bài đăng (chứa avatar, tên, nút like/comment) có kích thước tương đối đồng nhất. Nếu họ có một loại bài đăng đặc trưng với chiều cao cố định,
SliverPrototypeExtentListlà một ứng cử viên sáng giá. - Danh sách sản phẩm (Shopee, Lazada, Tiki): Khi tụi bây lướt qua hàng ngàn sản phẩm, mỗi sản phẩm hiển thị trong một 'card' có kích thước giống nhau. Việc đo đạc từng card sẽ là thảm họa hiệu năng.
- Danh sách chat (Zalo, Messenger): Nếu các bong bóng chat có chiều cao khá đồng đều (ví dụ: tin nhắn ngắn, không có ảnh/video quá lớn), thì việc dùng kỹ thuật này sẽ giúp cuộn danh sách tin nhắn mượt mà hơn rất nhiều.
5. Thử Nghiệm Và Hướng Dẫn Nên Dùng Cho Case Nào
Anh Creyt đã từng thử nghiệm nhiều cách tối ưu list trong Flutter, và đây là kinh nghiệm xương máu:
-
Nên dùng khi:
- Tụi bây có một danh sách RẤT DÀI (hàng trăm, hàng ngàn item).
- Tất cả các item trong danh sách có cấu trúc và kích thước (chiều cao/rộng) gần như giống hệt nhau.
- Tụi bây không biết chính xác kích thước của item từ trước, mà phải để Flutter tự tính toán (ví dụ: chiều cao của một bài đăng phụ thuộc vào độ dài của đoạn text).
- Tụi bây đang gặp vấn đề 'jank' (giật lag) khi cuộn danh sách và đã thử các cách khác mà chưa hiệu quả.
-
Không nên dùng khi:
- Danh sách của tụi bây ngắn (vài chục item đổ lại). Việc tối ưu này có thể không cần thiết và đôi khi còn làm phức tạp code hơn.
- Các item trong danh sách có kích thước KHÁC NHAU RẤT NHIỀU. Lúc này,
SliverPrototypeExtentListsẽ gây sai lệch trong tính toán và không mang lại hiệu quả. Hãy dùngSliverListbình thường hoặcListView.buildervà để Flutter tự quản lý kích thước từng item. - Tụi bây ĐÃ BIẾT CHÍNH XÁC kích thước của từng item (ví dụ: mỗi item cao đúng 60px). Trong trường hợp này,
SliverFixedExtentListvới thuộc tínhitemExtentsẽ là lựa chọn tối ưu hơn, vì nó còn đơn giản hơn nữa và hiệu quả y hệt.
Nhớ nhé, không có 'viên đạn bạc' nào trong lập trình cả. Mỗi 'vũ khí' đều có điểm mạnh, điểm yếu và trường hợp sử dụng riêng. Hiểu rõ nó là gì, dùng để làm gì, và khi nào nên dùng, đó mới là phong thái của một dev Gen Z 'chất'!
Chúc tụi bây code mượt, app chạy nhanh, và đừng quên thực hành để biến kiến thức thành kỹ năng nhé! Anh Creyt đi pha trà đây!
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é!