Flutter

Flutter, Bài 9: Button và ListView

ListView, có thể nói, là một Widget cực kì phổ biến, không chỉ trên ứng dụng cho di động, mà còn là trên các ứng dụng Web và cho máy tính nữa. Tuyệt đại đa số các ứng dụng ít nhiều đều cần hiển thị danh sách các mục, các phần tử trong một mảng nào đó ra màn hình để người dùng thao tác. Bài này sẽ hướng dẫn các bạn về ListView trong Flutter vốn tương ứng với ListView hay RecyclerView của Android và UITableView của iOS. Nhưng trước hết, hãy tìm hiểu về một số loại Button.

Flutter

1. Button

Button trong Flutter không extends TextView như trên Android Sdk. Flutter Button có phần giống một layout hơn. Trong bài trước, tôi đã trình bày một Button khá phổ biến trong Material Design là FloatingActionButton. Còn hôm nay, tôi sẽ giới thiệu thêm ba loại Button nữa gồm RaisedButton, FlatButton và IconButton.

1.1. RaisedButton

RaisedButton tương tự như android.widget.Button mặc định trên Android API 21+ với Theme Material trong Android Sdk, hay android.support.v7.widget.AppCompatButton mặc định với Theme AppCompat trong Android Support Lib, hay MDCButton trong bộ thư viện Material Elements của Google dành cho iOS. Nếu bạn vẫn chưa hình dung được, thì xin nhìn hình dưới đây:

Nó có hai constructors như sau:

Đó là constructor thường gặp nhất. Ở đây, ngoài onPressed là một VoidCallback mà bạn đã khá quen thuộc, và các props với tên gọi đã nói lên bản chất của chúng vốn sẽ không lấy của bạn nhiều thời gian để vọc vạch về sau, bạn chỉ cần chú ý vào phần Widget child. Thông thường, nó sẽ là một Text để chứa phần chữ nghĩa bên trong để người dùng đọc. Tuy nhiên, nếu thích, bạn cũng có thể nhồi nhét một Column hay Row vào để hiển thị nhiều thứ hơn. Lưu ý: Phần child này sẽ trực tiếp chịu ảnh hưởng của theme của RaisedButton, nên thông thường bạn có thể không cần style cho phần Text nhiều. Chẳng hạn, nếu phần Color color là một màu không sáng, thì phần Text trong child sẽ tự có màu trắng, như trong hình trên. Còn nếu Color color là một màu sáng, chẳng hạn như vàng chanh tươi, thì tự động màu Text sẽ chuyển thành đen, mà không cần bạn định nghĩa lại với Color textColor. Tất nhiên, bạn thoải mái ép buộc dùng màu chữ textColor tùy ý thích, và Flutter team đã cung cấp cho bạn khả năng tùy chỉnh đó thôi.

Constructor thứ hai tương tự như Android Button with compound Drawable, tương tự như trên nhưng có thêm một prop Widget icon.

Để có thêm chi tiết về RaisedButton, xin mời bạn “bay” đến https://docs.flutter.io/flutter/material/RaisedButton-class.html.

1.2. FlatButton

Thay vì giải thích dài dòng văn tự với hiệu quả kém, xin mời bạn “chiêm ngưỡng” FlatButton in real action:

Constructor của nó hoàn toàn tương tự như RaisedButton bên trên nên tôi chỉ để đây và không nói gì thêm ngoài việc mời mộc các bạn tự do thay đổi để tìm hiểu thêm trong các ví dụ thực tiễn.

Để có thêm chi tiết về FlatButton, xin mời bạn “bay” đến https://docs.flutter.io/flutter/material/FlatButton-class.html.

1.3. IconButton

Nếu đã quen với ImageView trong Android với việc gán một src hoặc srcCompat cho ImageView đó rồi setOnClickListener, thì rất tiếc là bạn không thể đem thói quen đó vào hoạt động lập trình với Flutter được. Bắt buộc, bạn phải dufnng IconButton. Nhưng mọi thứ cũng không quá phức tạp cho lắm, vì constructor của IconButton hoàn toàn dễ hiểu:

Ngoài VoidCallback onPressed, bạn sẽ cần chú ý tới Widget icon là phần Icon hiển thị biểu tượng của Button, và String tooltip là chú thích. Chẳng hạn:

Để có thêm chi tiết về IconButton, xin mời bạn “bay” đến https://docs.flutter.io/flutter/material/IconButton-class.html.

2. ListView

Flutter ListView gồm có 3 constructors, nhưng tôi chỉ giới thiệu ListView và ListView.builder trong bài này. Đối với ListView.custom, tôi sẽ giới thiệu trong bài khác khi có thể.

2.1. ListView

Constructor này có khá nhiều tham số:

Tuy nhiên, bạn chỉ cần trước mắt nhớ một số props sau:

  • ScrollController controller: Controller để quan sát quá trình cuộn của ListView. Chi tiết về ScrollController, bạn có thể đọc thêm ở https://docs.flutter.io/flutter/widgets/ScrollController-class.html.
  • EdgeInsetsGeometry padding: Tương tự như padding của các Widget khác.
  • List<Widget> children: const []: Các dòng của ListView.

Dưới đây là ví dụ đơn giản nhất về ListView, và kết quả của nó được thể hiện qua ảnh bên dưới:

2.2. ListView.builder:

Đây là constructor phổ biến nhất, linh động nhất về số lượng các dòng, hay từ thông dụng hơn là các items, và cả về bản thân các dòng nữa. Constructor này gợi nhắc tới việc sử dụng các hàm tableView(_) trên iOS hay dựng ListAdapter hay RecyclerView.Adapter trên Android. Trước hết, bạn cần nhìn nội dung của nó:

Và cũng như các mục ở các bài viết về Flutter của tôi, bạn chỉ cần lưu ý một số props ở thời điểm này mà thôi. Lần này, so với những props trong ListView, bạn cần nhớ các props sau:

  • IndexedWidgetBuilder itemBuilder: Định nghĩa từng row, tương tự như tableView:cellForRowAtIndex trên iOS UITableView, hay ListAdapter#getView(int, View, ViewGroup) trên Android ListView, hay RecyclerView#onBindViewHolder(RecyclerView.ViewHolder, int) trên Android RecyclerView.
  • int itemCount: Tổng số item, tương tự như tableView:numberOfRowsInSection trên iOS hay ListAdapter#getCount trên Android ListView hay RecyclerView.Adapter#getItemCount trên Android RecyclerView.

Bạn sẽ dễ hiểu hơn qua ví dụ sau:

Và kết quả của nó sẽ tương tự như trong ảnh:

Ở đây, bạn sẽ cần lưu ý về int itemCount. Dù nó là một optional param nghĩa là có cũng được, không có cũng không sao, nhưng nếu bạn không điền con số không quá chiều dài của dữ liệu nguồn, tức List.length, thì giá trị mặc định có thể lên tới con số 10 ngàn kia, và ListView xem như cuộn vô hạn và vượt ra khỏi chiều dài của mảng. Khi người dùng cuộn xuống phía dưới, họ sẽ thấy một loạt các dòng trống, có thể cảm thấy “hoang mang”. Ngoài ra, bạn cũng không nhất thiết phải sử dụng ListTile, mà có thể dùng Widget nào bạn thích.

2.3. Nhận Event Click trên mỗi dòng:

Tương tự như cách bạn hay dùng trên Android RecyclerView, bạn sẽ gán Event onTap hay onPressed trên mỗi dòng tùy Widget bạn chọn để render từng dòng. Đối với ListTile, đó là event onTap, và giá trị của nó là một hàm callback không tham số. Ví dụ:

Và cũng tương tự cho các event khác như long press hay swipe.

2.4. ListView với nhiều kiểu dòng khác nhau:

Tương tự như iOS UITableView hay Android ListView và RecyclerView, bạn có thể có nhiều kiểu dòng khác nhau trên ListView, chẳng hạn như Header và Item. Chẳng hạn:

2.5. Tổng hợp:

Như vậy, tôi đã hướng dẫn bạn cách sử dụng ListView để hiển thị nhiều mục trên ứng dụng Flutter. Nếu bạn muốn tìm hiểu thêm về ListView, hãy ghé thăm https://docs.flutter.io/flutter/widgets/ListView-class.html.

3. GridView:

Flutter cũng cung cấp cho chúng ta GridView tương tự như trong Android, và cách dựng GridView cũng tương tự như ListView. Constructors cũng tương tự. Chẳng hạn, GridView.builder tương đồng với ListView.builder, hay GridView tương đồng với ListView. Nhưng GridView và GridView.builder còn yêu cầu thêm một tham số bắt buộc nữa là SliverGridDelegate delegate. Nó chẳng qua đóng vai trò định nghĩa số cột mà thôi. Bạn không dùng trực tiếp class SliverGridDelegate, mà dùng hoặc SliverGridDelegateWithFixedCrossAxisCount(int columnCount, …) với prop int là số cột “cứng” – nghĩa là dù chiều ngang màn hình có dài ngắn ra sao thì vẫn giữ số cột là columnCount, hoặc SliverGridDelegateWithMaxCrossAxisExtent(double maxCrossAxisExtent, …) với prop double là số cột tối đa được dãn ra tùy theo chiều ngang màn hình. Ngoài ra, bạn có GridView.count tương ứng với GridView và SliverGridDelegateWithFixedCrossAxisCount, giúp bạn đốt giai đoạn bằng cách truyền trực tiếp số cột vào GridView.count constructor, hay GridView.extent tương ứng với GridView và SliverGridDelegateWithMaxCrossAxisExtent, giúp bạn giúp bạn đốt giai đoạn bằng cách truyền trực tiếp số cột vào GridView.extent constructor. Về chi tiết, bạn tham khảo thêm ở https://docs.flutter.io/flutter/widgets/GridView-class.html.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.