
ListWheelScrollView là gì và để làm gì?
Chào các chiến hữu! Hôm nay, chúng ta sẽ đào sâu vào một widget khá 'nghệ' trong Flutter, đó là ListWheelScrollView. Anh em cứ hình dung thế này: Có bao giờ các bạn thấy cái máy đánh bạc (slot machine) hay cái bộ chọn ngày tháng trên điện thoại chưa? Cái mà các mục nó cứ xoay xoay như một cái bánh xe, mục nào được chọn thì nổi bật lên giữa màn hình ấy. Chính xác! ListWheelScrollView sinh ra là để làm cái trò đó đấy!
Nói một cách hàn lâm hơn (nhưng vẫn dễ hiểu), ListWheelScrollView là một widget cho phép hiển thị một danh sách các mục (children) theo một góc nhìn 3D, tạo hiệu ứng như thể chúng đang nằm trên một cái bánh xe và bạn đang cuộn để chọn. Nó cực kỳ hữu ích khi bạn cần:
- Chọn một mục từ một tập hợp nhỏ đến trung bình: Ví dụ như chọn ngày, giờ, số lượng, hoặc một tùy chọn cụ thể nào đó.
- Tạo hiệu ứng UI độc đáo: Khi bạn muốn giao diện của mình trông 'khác bọt' và thu hút hơn so với các
ListViewtruyền thống.
Các thuộc tính quan trọng nhất mà anh em cần nắm là itemExtent (chiều cao của mỗi mục trên bánh xe), children (danh sách các widget con), và onSelectedItemChanged (sự kiện khi mục được chọn thay đổi).

Code Ví Dụ Minh Họa Rõ Ràng
Để các bạn dễ hình dung, chúng ta sẽ xây dựng một ví dụ đơn giản với ListWheelScrollView để chọn các mục từ một danh sách. Hãy cùng xem code nhé:
import 'package:flutter/material.dart';
class ListWheelScrollViewDemo extends StatefulWidget {
const ListWheelScrollViewDemo({super.key});
@override
State<ListWheelScrollViewDemo> createState() => _ListWheelScrollViewDemoState();
}
class _ListWheelScrollViewDemoState extends State<ListWheelScrollViewDemo> {
int _selectedItem = 0;
final List<String> _items = List<String>.generate(20, (index) => 'Mục số ${index + 1}');
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ListWheelScrollView Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Mục đã chọn: ${_items[_selectedItem]}',
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
SizedBox(
height: 200, // Chiều cao của khu vực cuộn
child: ListWheelScrollView(
itemExtent: 60, // CHIỀU CAO CỦA MỖI MỤC TRÊN BÁNH XE
perspective: 0.007, // Độ cong của bánh xe (thường từ 0.001 đến 0.01)
diameterRatio: 1.5, // Tỷ lệ đường kính của bánh xe
useMagnifier: true, // Dùng kính lúp để phóng to mục được chọn
magnification: 1.2, // Độ phóng đại của kính lúp
onSelectedItemChanged: (index) {
setState(() {
_selectedItem = index;
});
},
children: _items.map((item) {
return Card(
margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
color: _selectedItem == _items.indexOf(item) ? Colors.blueAccent.shade100 : Colors.grey.shade200,
child: Center(
child: Text(
item,
style: TextStyle(
fontSize: 20,
color: _selectedItem == _items.indexOf(item) ? Colors.white : Colors.black87,
fontWeight: _selectedItem == _items.indexOf(item) ? FontWeight.bold : FontWeight.normal,
),
),
),
);
}).toList(),
),
),
const SizedBox(height: 20),
// Để điều khiển cuộn programmatically, bạn sẽ cần FixedExtentScrollController.
// Ví dụ: FixedExtentScrollController _controller = FixedExtentScrollController(initialItem: 5);
// Sau đó gán controller vào ListWheelScrollView và dùng _controller.animateToItem(...)
Text(
'Để cuộn tự động tới một mục, dùng FixedExtentScrollController.',
style: TextStyle(fontStyle: FontStyle.italic, color: Colors.grey.shade600),
)
],
),
),
);
}
}
Trong ví dụ trên:
- Chúng ta có một danh sách
_itemsđơn giản. ListWheelScrollViewđược đặt trong mộtSizedBoxvới chiều cao cố định để nó có không gian hiển thị.itemExtent: 60định nghĩa mỗi mục sẽ cao 60 logical pixels trên trục cuộn. Đây là điểm mấu chốt!perspectivevàdiameterRatiogiúp điều chỉnh hiệu ứng 3D.onSelectedItemChangedcập nhật trạng thái_selectedItemmỗi khi người dùng cuộn và chọn một mục mới.- Các
Cardđược dùng làm widget con, và màu sắc của chúng thay đổi tùy theo mục được chọn.
Mẹo Vặt (Best Practices) từ Giảng viên Creyt
Để dùng ListWheelScrollView một cách hiệu quả và không bị 'ngã ngửa', các bạn nhớ mấy chiêu này:
itemExtentlà chìa khóa (Key Property): Đây là thuộc tính quan trọng nhất, nó định nghĩa chiều cao của vùng mà mỗi item chiếm trên bánh xe. Không phải là chiều cao thực tế của widget con đâu nhé! NếuitemExtentquá nhỏ so với widget con, các mục sẽ chồng lên nhau. Nếu quá lớn, chúng sẽ có khoảng trống thừa thãi. Cứ coi như nó là 'kích thước ô' mà mỗi item phải vừa vặn vào.- Đừng ham list quá dài:
ListWheelScrollViewkhông được tối ưu hóa cho các danh sách vô hạn hoặc cực kỳ dài nhưListView.builder. Nó render tất cả các widget con cùng lúc. Do đó, chỉ nên dùng cho các danh sách có số lượng mục vừa phải (dưới vài chục đến vài trăm là hợp lý). Nếu bạn có hàng ngàn mục, hãy nghĩ đến giải pháp khác hoặc chia nhỏ dữ liệu. - Dùng
FixedExtentScrollControllerđể điều khiển: Nếu bạn muốn cuộn đến một mục cụ thể theo lập trình (ví dụ: khi khởi tạo DatePicker, bạn muốn nó hiển thị ngày hiện tại), hãy dùngFixedExtentScrollController. Nó cung cấp các phương thức nhưanimateToItem()hoặcjumpToItem(). - Chơi với
perspectivevàdiameterRatio: Hai thuộc tính này giống như 'góc máy quay' và 'độ cong của ống kính' vậy.perspective(thường từ 0.001 đến 0.01) điều chỉnh độ sâu của hiệu ứng 3D, còndiameterRatio(thường từ 0.5 đến 2.0) ảnh hưởng đến kích thước ảo của 'bánh xe'. Cứ thử nghiệm để tìm ra hiệu ứng ưng ý nhất cho UI của mình. - Giữ Widget con đơn giản: Để đảm bảo hiệu suất mượt mà, các widget con bên trong
ListWheelScrollViewnên được giữ càng đơn giản càng tốt. Tránh các widget quá phức tạp hoặc tốn tài nguyên bên trong mỗi item.
Ứng dụng Thực Tế của ListWheelScrollView
ListWheelScrollView không chỉ là một widget 'làm màu' đâu nhé, nó có rất nhiều ứng dụng thực tế mà các bạn có thể đã gặp hàng ngày:
- Bộ chọn ngày/giờ (Date/Time Pickers): Đây là ứng dụng phổ biến nhất. Hầu hết các ứng dụng lịch, đặt hẹn đều dùng cơ chế tương tự để chọn ngày, tháng, năm hoặc giờ, phút.
- Bộ chọn số lượng/đơn vị: Trong các ứng dụng mua sắm, bạn có thể thấy nó được dùng để chọn số lượng sản phẩm. Hoặc trong các ứng dụng đo lường, để chọn đơn vị (kg, lít, mét...).
- Hiệu ứng 'Slot Machine' hay 'Lucky Wheel': Các ứng dụng game, quay số may mắn thường sử dụng hiệu ứng này để tạo sự kịch tính và thú vị cho người chơi.
- Bộ chọn tùy chỉnh (Custom Selectors): Bạn có thể dùng nó để chọn font chữ, màu sắc, hoặc bất kỳ tùy chọn nào khác mà bạn muốn mang lại trải nghiệm độc đáo cho người dùng.
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é!