Flutter

Flutter, Bài 6: Giới thiệu về các loại Layout

Như đã giới thiệu trong bài trước, việc định vị các đối tượng Widgets trong Flutter bằng các thuộc tính margin, padding hay constraints trực tiếp trên đối tượng đó là điều không thể.  Ngoài ra, cũng như trên Android, bạn cần “bọc” tất cả các Widgets trong một Widgets lớn có tính tổ chức, tương tự như RelativeLayout hay LinearLayout vậy. Để giúp các bạn dễ dàng tiếp cận với Flutter, tôi sẽ giới thiệu một số Layout Widgets quan trọng nhất mà bạn sẽ sử dụng nhiều. Sau khi đọc xong bài này, bạn nên sử dụng chúng ngay để hiểu rõ hành vi của chúng, vì bài viết chỉ cung cấp các thông tin quan trọng nhất.

Flutter

0. Điểm chung của các loại Layout:

Tất cả các Layout sẽ chứa các Widget con trong tham số child ở constructor đối với các Layout chỉ chứa một Widget duy nhất như Container, hay children đối với các Layout có thể chứa nhiều Widgets con như Column và Row. Đối với param children, thì value của nó sẽ là một List<Widget>.

1. Container:

Có thể coi container là một Layout đơn giản, chỉ chứa đúng một Widget con. Cho tới thời điểm này, nó là widget duy nhất có các thuộc tính margin, padding, constraints, width và height rõ ràng và được chỉ định trong constructor. Mặc dù “hành vi” của nó được giải thích khá đầy đủ và dài dòng tại https://docs.flutter.io/flutter/widgets/Container-class.html, nhưng đối với các bạn đã có kinh nghiệm làm việc với code Android qua Android SDK, iOS qua iOS SDK hay React Native, thì hành vi của nó có thể được giải thích ngắn gọn như sau:

  • Nếu nó không có các thuộc tính định vị với bên ngoài như margin và constraint, hay bên trong chính nó là padding, và cũng không được định width và height thì khi render, kích thước của nó sẽ là nhỏ nhất có thể.
  • Nếu nó có thuộc tính constraint theo parent Widget chứa nó, thì nó sẽ dãn ra cho khớp constraints được định.

Bạn có thể sử dụng nó nhiều lần nếu “không bỏ được” hướng tư duy dựng Widgets theo kiểu Android, iOS hay React Native với các thuộc tính margin, padding, constraints, width và height. Tuy nhiên, tên này sẽ “ăn” khá nhiều thời gian tính toán so với các loại Layout khác nên việc sử dụng “số đông” Container có thể gây cảm giác giật, lag trên các thiết bị có cấu hình yếu. Do đó, tôi khuyên bạn nên hạn chế dùng tên này, mà thay vào đó là dùng tên Padding bên dưới để tạo “khoảng cách” giữa Widget trong với parent của nó.

2. Padding:

Padding có thể được hiểu là một dạng thu gọn của Container bên trên với chỉ có một thuộc tính padding, dù chúng không hề có quan hệ gần gũi nào ngoài “thủy tổ” là Widget. Bạn có đang nghĩ là sẽ nhập “số” trực tiếp vào phần padding không? Nếu có thì nên bỏ ngay và luôn, vì param padding là một instance của EdgeInsectsGeometry. Tôi sẽ nói về class này sau, còn hiện tại, bạn sẽ tạm nghĩ nó là một class để định nghĩa paddingLeft, paddingTop, paddingRight và paddingBottom là được. Về chi tiết của Padding, Flutter team có diễn giải ở https://docs.flutter.io/flutter/widgets/Padding-class.html.

3. Center:

Đây sẽ là một Widget có thể gây cho bạn sự lầm lẫn khá tai hại bởi hành vi của nó có khác khi nó đi một mình hay đi chung với ai đó. Nếu nó đi một mình, và parent của nó không đi kèm – hay nói theo kiểu vui của tôi là không có cặp kè – với Widget ngang hàng (với tên parent đó) nào khác và bản thân của nó cũng không “nắm tay” ai, thì kích thước của nó sẽ tăng lên chiếm trọn không gian. Còn nếu nó phải đi kèm với ai đó, thì nó sẽ e dè mà điều chỉnh kích thước chỉ vừa đủ bọc con/child của nó mà thôi. Để đọc các câu chữ “chính chủ” do đội phát triển Flutter viết về Center, bạn tham khảo tại https://docs.flutter.io/flutter/widgets/Center-class.html.

4. Transform:

Widget này có nhiệm vụ thay đổi (transform) của widget con theo chỉ định của bạn. Ngoài việc thay đổi vị trí theo tọa độ (translate), nó còn có khả năng khác gồm xoay (rotate) và co dãn (scale). Đối với những bạn đã quen với việc trực tiếp transform (UI)Views trên Android, iOS hay React Native, bạn sẽ cần bọc Widget tương ứng trong Transform widget này khi code Flutter. Thông tin đầy đủ của class này được lưu trữ tại https://docs.flutter.io/flutter/widgets/Transform-class.html.

5. Expanded:

Widget này có nhiệm vụ sẽ chiếm trọn vùng màn hình, tương tự như layout_weight của Android LinearLayout. Để hiểu rõ hơn, bạn mở project mặc định của Flutter lên, cho chạy để thấy phần giao diện trước.

Sau đó, bạn cóp pát phần này vào _MyHomePageState#build(BuildContext)#body. Kết quả là giao diện tự dưng bị rối lên, vì tên Center tự dưng “e ấp, thẹn thùng” co kích thước chiều dọc lại, không còn “mạnh tay” chiếm trọn phần màn hình trống nữa.

Qua ví dụ trên, bạn sẽ thấy Center không phải lúc nào cũng mở rộng hết mức như trong “quảng cáo” của Flutter doc chính chủ. Để phần “You have pushed the button this many times” và “$_counter” vào giữa (theo chiều dọc), bạn cần tên Expanded này chứa Center như bên dưới:

Và như vậy, hai phần Text mặc định đã được căn giữa cả chiều dọc lẫn ngang ở phần màn hình trống bên dưới “Hello world”. Hiện tại, phần Expanded chỉ có mỗi prop child là quan trọng nhất, nhưng bạn có thể thỉnh thoảng kiểm tra https://docs.flutter.io/flutter/widgets/Expanded-class.html  để cập nhật những methods và props mới của nó.

6. Row và Column:

Đây sẽ là những widget quen thuộc đối với các bạn chuyên code Android, vì chúng tương đồng với LinearLayout. Trong khi Row tương ứng với LinearLayout với orientation là horizontal, tức chiều ngang, thì Column resemble vertical LinearLayout, tức chiều dọc. Còn đối với các bạn chưa thao tác với Android Sdk, thì chúng sẽ sắp xếp tuần tự các widget con theo một hướng ngang từ start sang end đối với Row, hay từ top xuống bottom với Column. Về các constructor và methods của chúng, bạn có thể đọc tại https://docs.flutter.io/flutter/widgets/Row-class.html và https://docs.flutter.io/flutter/widgets/Column-class.html.

7. Stack:

Widget này bố trí các widget con tự do theo vị trí bạn định sẵn theo trục tọa độ cao (z). Widget nào được thêm cuối sẽ nằm ở tầng cao nhất và ngược lại, tương tự như FrameLayout hay CoordinatorLayout ở Android hay thuộc tính zIndex của React Native. Trong trường hợp bạn muốn tham khảo thêm về nó, hãy tìm đến https://docs.flutter.io/flutter/widgets/Stack-class.html.

8. ListView:

Có lẽ đây là một widget không thể không có trong các ứng dụng phổ biến, đặc biệt là các ứng dụng có gửi request và nhận reponse chứa loạt dữ liệu về để hiển thị. Chẳng hạn, danh mục sản phẩm, hay nội dung chat. Đối với các bạn chuyên code iOS, thì ListView tương ứng với UITableView. Tôi sẽ dành hẳn một bài hướng dẫn riêng về tên này trong thời gian tới. Còn hiện tại, bạn có thể tìm hiểu chi tiết class này qua https://docs.flutter.io/flutter/widgets/ListView-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.