Flutter: Codelab
Mục lục bài viết
- Các lớp Row và Column
- Kích thước và căn chỉnh trục
- Widget Flexible
- Widget Expanded
- Widget SizedBox
- Widget Spacer
- Widget Text
- Widget Icon
- Widget Image
- Tổng kết
Bài viết này sẽ hướng dẫn bạn cách xây dựng giao diện người dùng Flutter mà không cần tải xuống và cài đặt Flutter hoặc Dart!
Quan trọng: Codelab này bao gồm các khái niệm cơ bản về bố cục Flutter bằng cách sử dụng một trình soạn thảo code thử nghiệm có tên là DartPad. DartPad chưa được thử nghiệm đầy đủ trên tất cả các trình duyệt. Nếu bạn gặp bất kỳ khó khăn nào khi sử dụng DartPad trên một trình duyệt cụ thể, vui lòng tạo sự cố DartPad và chỉ định trình duyệt bạn đang sử dụng trong tiêu đề sự cố.
Flutter khác với các framework khác vì giao diện người dùng của nó được xây dựng trong mã, không phải (ví dụ) trong tệp XML hoặc tương tự. Các widget là các khối xây dựng cơ bản của giao diện người dùng Flutter. Sau khi bạn đã tiến bộ qua codelab này, bạn sẽ biết rằng hầu hết mọi thứ trong Flutter đều là một widget. Widget là một đối tượng bất biến mô tả một phần cụ thể của giao diện người dùng. Bạn cũng sẽ biết rằng các widget Flutter có thể kết hợp, nghĩa là bạn có thể kết hợp các widget hiện có để tạo ra các widget phức tạp hơn. Ở phần cuối của codelab này, bạn sẽ áp dụng những gì đã học vào việc xây dựng giao diện người dùng Flutter hiển thị danh thiếp.
Thời gian ước tính để hoàn thành codelab này: 45-60 phút.
Các lớp Row và Column
Row và Column là các lớp chứa và bố trí các widget. Các widget bên trong của một Row hoặc Column được gọi là con, Row và Column được gọi là cha. Row đặt các widget của nó theo chiều ngang và Column đặt các widget của nó theo chiều dọc.
Ví dụ: Tạo một cột
Ví dụ sau đây hiển thị sự khác biệt giữa a Row và Column.
1. Nhấp vào nút Run.
2. Trong đoạn code bạn hãy thay đổi Row thành một Column và chạy lại.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( children: [ BlueBox(), BlueBox(), BlueBox(), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width:50, height:50, decoration:BoxDecoration( color:Colors.blue, border:Border.all(), ), ); } }
Kích thước và căn chỉnh trục (axis)
Các BlueBoxwidget đã được sắp xếp lại với nhau (ở bên trái hoặc ở trên cùng của Đầu ra giao diện người dùng). Bạn có thể thay đổi cách sắp xếp các BlueBoxwidget bằng cách sử dụng thuộc tính kích thước trục và căn chỉnh.
Thuộc tính mainAxisSize
Row và Column chiếm các trục chính khác nhau. Trục chính của Row là trục ngang và trục chính của Column là trục thẳng. Thuộc tính mainAxisSize sẽ xác định bao nhiêu không gian cho một Row và Column có thể chiếm trên trục chính của họ. mainAxisSize có hai giá trị có thể:
MainAxisSize.max
Row và Column chiếm tất cả không gian trên các trục chính của chúng. Nếu chiều rộng tổng hợp của các con của chúng nhỏ hơn tổng không gian trên các trục chính của chúng, thì các con của chúng được bố trí thêm không gian.
MainAxisSize.min
Row và Column chỉ chiếm đủ không gian trên các trục chính của chúng cho con của chúng. Con của chúng được bố trí không có không gian thừa và ở giữa các trục chính của chúng.
Mẹo:
MainAxisSize.maxlà giá trị mặc định của thuộc tínhmainAxisSize. Nếu bạn không chỉ định giá trị khác, giá trị mặc định sẽ được sử dụng, như được hiển thị trong ví dụ trên.
Ví dụ: Sửa đổi kích thước trục
Ví dụ sau đặt mainAxisSize thành giá trị mặc định của nó MainAxisSize.max.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.max, children: [ BlueBox(), BlueBox(), BlueBox(), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } }
Mẹo: Lưu ý cách phần màu xám đậm của Row biến mất khi bạn thay đổi
MainAxisSize.maxthànhMainAxisSize.min. Bản thân Row đang chiếm ít chiều rộng hơn của trục chính và được căn giữa vì điều này.
Thuộc tính mainAxisAlignment
Khi mainAxisSize được đặt thành MainAxisSize.max thì Row và Column có thể khiến con của chúng có thêm không gian. Thuộc tính mainAxisAlignment xác định cách thức Row và Column có thể định vị con của chúng trong không gian bổ sung đó. mainAxisAlignment có sáu giá trị có thể có:
MainAxisAlignment.start
Vị trí con gần đầu trục chính (trái cho Row, trên cùng cho Column).
MainAxisAlignment.end
Vị trí con gần cuối trục chính (phải cho Row, dưới cùng cho Column).
MainAxisAlignment.center
Vị trí con ở giữa trục chính.
MainAxisAlignment.spaceBetween
Chia đều không gian thừa giữa các con.
MainAxisAlignment.spaceEvenly
Chia đều không gian thừa giữa con và trước và sau con.
MainAxisAlignment.spaceAround
Tương tự như MainAxisAlignment.spaceEvenly, nhưng giảm một nửa không gian trước con đầu tiên và sau con cuối cùng xuống một nửa chiều rộng giữa các con.
Ví dụ: Sửa đổi căn chỉnh trục chính
Ví dụ sau đặt mainAxisAlignment thành giá trị mặc định của nó MainAxisAlignment.start.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.start, children: [ BlueBox(), BlueBox(), BlueBox(), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } }
Mẹo: Trước khi chuyển sang phần tiếp theo, hãy thay đổi
MainAxisAlignment.endsang giá trị khác.
Thuộc tính crossAxisAlignment
Thuộc tính crossAxisAlignment xác định cách Row và Column có thể xác định vị trí con của chúng trên trục chéo của chúng. Trục chéo của Row là thẳng và của Column là nằm ngang. crossAxisAlignment có năm giá trị có thể:
CrossAxisAlignment.start
Vị trí con gần điểm bắt đầu của trục chéo (trên cùng cho Row, trái cho Column).
CrossAxisAlignment.end
Vị trí con gần cuối trục chéo (dưới cùng cho Row, phải cho Column).
CrossAxisAlignment.center
Vị trí con ở giữa trục chéo (giữa cho Row, trung tâm cho Column).
CrossAxisAlignment.stretch
Kéo dài con qua trục chéo (từ trên xuống dưới cho Row, từ trái sang phải cho Column).
CrossAxisAlignment.baseline
Căn chỉnh con theo đường cơ bản của chúng (chỉ dành cho lớp Text và yêu cầu thuộc tính textBaseline được đặt thành TextBaseline.alphabetic. Hãy xem phần Text widget để biết ví dụ).
Ví dụ: Sửa đổi căn chỉnh trục chéo
Ví dụ sau đặt crossAxisAlignment thành giá trị mặc định của nó CrossAxisAlignment.center.
Để chứng minh sự căn chỉnh trục chéo, mainAxisAlignment được đặt thành MainAxisAlignment.spaceAround và Row hiện chứa một widget con BiggerBlueBox cao hơn các widget BlueBox.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [ BlueBox(), BiggerBlueBox(), BlueBox(), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } } class BiggerBlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 100, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } }
Mẹo: Trước khi chuyển sang phần tiếp theo, hãy thay đổi
CrossAxisAlignment.startthành giá trị khác.
Widget Flexible
Như bạn đã thấy, các thuộc tính mainAxisAlignment và crossAxisAlignment xác định cách Row và Column định vị trí các widget dọc theo cả hai trục. Row và Column đầu tiên bố trí các widget có kích thước cố định. Các widget có kích thước cố định được coi là không linh hoạt vì chúng không thể tự thay đổi kích thước sau khi đã được bố trí.
Widget Flexible sẽ gộp widget, vì vậy widget đó có thể thay đổi kích thước. Khi widget Flexible gộp một widget thì widget đó sẽ trở thành Flexible con của widget đó và được coi là linh hoạt . Sau khi các widget không linh hoạt được bố trí, các widget được thay đổi kích thước theo các thuộc tính flex và fit của chúng:
flex
So sánh chính nó với các thuộc tính flex khác trước khi xác định phần nào trong tổng dung lượng còn lại mà mỗi widget Flexible nhận được.
fit
Xác định xem một widget Flexible có lấp đầy tất cả không gian bổ sung của nó hay không.
Ví dụ: Thay đổi thuộc tính phù hợp
Ví dụ sau minh họa thuộc tính fit, có thể có một trong hai giá trị:
FlexFit.loose
Kích thước ưa thích của tiện ích được sử dụng (mặc định).
FlexFit.tight
Buộc tiện ích phải lấp đầy tất cả không gian thừa của nó.
Trong ví dụ này, hãy thay đổi thuộc tính fit để làm cho các widget Flexible lấp đầy không gian thừa.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( children: [ BlueBox(), Flexible( fit: FlexFit.loose, flex: 1, child: BlueBox(), ), Flexible( fit: FlexFit.loose, flex: 1, child: BlueBox(), ), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } }
Ví dụ: Kiểm tra giá trị flex
Trong ví dụ sau, Row chứa một widget BlueBox và hai widget Flexible bao ngoài hai widget BlueBox. Các widget Flexible chứa các thuộc tính flex có giá trị flex đặt thành 1 (giá trị mặc định).
Khi các thuộc tính flex được so sánh với nhau, tỷ lệ giữa các giá trị flex của chúng xác định phần nào trong tổng không gian còn lại mà mỗi widget Flexible nhận được.
remainingSpace * (flex / totalOfAllFlexValues)
Trong ví dụ này, tổng các giá trị flex (2), xác định rằng cả hai widget Flexible đều nhận được một nửa tổng không gian còn lại. Widget BlueBox (hoặc widget kích thước cố định) vẫn cùng kích thước.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( children: [ BlueBox(), Flexible( fit: FlexFit.tight, flex: 1, child: BlueBox(), ), Flexible( fit: FlexFit.tight, flex: 1, child: BlueBox(), ), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } }
Mẹo: Trước khi chuyển sang ví dụ tiếp theo, hãy thử thay đổi các thuộc tính
flexthành các giá trị khác, chẳng hạn như 2 và 1.
Widget Expanded
Tương tự như Flexible, widget Expanded có thể bao ngoài một widget khác và buộc nó phải lấp đầy không gian mở rộng.
Mẹo: Sự khác biệt giữa Flexible và Expanded là gì? Sử dụng
Flexibleđể thay đổi kích thước các widget trongRowhoặcColumn. Bằng cách đó, bạn có thể điều chỉnh khoảng cách của tiện ích con trong khi vẫn giữ kích thước của nó tương quan với tiện ích mẹ. Sử dụngExpandedđể thay đổi các ràng buộc của một widget con, để nó lấp đầy bất kỳ không gian trống nào.
Để tìm hiểu thêm về các ràng buộc và cách chúng ảnh hưởng đến cách Flutter xác định kích thước và vị trí của các tiện ích con của nó, hãy xem bài viết Tìm hiểu các ràng buộc.
Ví dụ: Điền thêm không gian
Ví dụ sau minh họa cách widget Expanded buộc widget con của nó là BlueBox lấp đầy không gian thừa.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( children: [ BlueBox(), Expanded( child: BlueBox(), ), BlueBox(), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } }
Widget SizedBox
Widget SizedBox có thể được sử dụng theo một trong hai cách khi tạo kích thước chính xác. Khi SizedBox kết thúc một widget, nó sẽ thay đổi kích thước của widget bằng cách sử dụng các thuộc tính height và width. Khi nó không bao ngoài widget nào thì nó sẽ sử dụng các thuộc tính height và width để tạo không gian trống.
Ví dụ: Thay đổi kích thước widget
Ví dụ sau bao ngoài widget BlueBox thứ 2 bởi một widget SizedBox và đặt chiều rộng và chiều cao của BlueBox đó thành 100 pixel.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.max, children: [ BlueBox(), SizedBox( width: 100, height: 100, child: BlueBox(), ), BlueBox(), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } }
Ví dụ: Tạo không gian
Ví dụ sau chứa ba widget BlueBox và hai widget SizedBox nằm xen kẽ giữa các widget BlueBox, trong đó widget SizedBox thứ nhất chứa thuộc tính width mang giá trị 50 pixel, widget SizedBox còn lại chứa thuộc tính width mang giá trị 25 pixel.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( children: [ BlueBox(), SizedBox(width: 50), BlueBox(), SizedBox(width: 25), BlueBox(), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } }
Widget Spacer
Tương tự như SizedBox, widget Spacer cũng có thể tạo khoảng cách giữa các widget.
Mẹo: Sự khác biệt giữa SizedBox và Spacer là gì? Sử dụng
Spacerkhi bạn muốn tạo không gian bằng thuộc tínhflex. Sử dụngSizedBoxkhi bạn muốn tạo không gian bằng cách sử dụng một số pixel logic cụ thể.
Ví dụ: Tạo thêm không gian
Ví dụ sau xen kẽ các widget BlueBox là các widget Spacer có flex giá trị là 1.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( children: [ BlueBox(), Spacer(flex: 1), BlueBox(), Spacer(flex: 1), BlueBox(), ], ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue, border: Border.all(), ), ); } }
Widget Text
Widget Text hiển thị widget và có thể được cấu hình cho các phông chữ, kích thước và màu sắc khác nhau.
Ví dụ: Căn chỉnh văn bản
Ví dụ sau hiển thị “V1S!” ba lần, nhưng ở các cỡ chữ khác nhau và có màu sắc khác nhau. Row xác định các thuộc tính crossAxisAlignment và textBaseline.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: [ Text( 'V1S!', style: TextStyle( fontSize: 30, fontFamily: 'Futura', color: Colors.blue, ), ), Text( 'V1S!', style: TextStyle( fontSize: 50, fontFamily: 'Futura', color: Colors.green, ), ), Text( 'V1S!', style: TextStyle( fontSize: 40, fontFamily: 'Futura', color: Colors.red, ), ), ], ); } }
Widget Icon
Widget Icon hiển thị một biểu tượng đồ họa thể hiện một phần của giao diện người dùng. Flutter có sẵn các gói biểu tượng cho các ứng dụng Material và Cupertino.
Ví dụ: Tạo biểu tượng
Ví dụ sau đây hiển thị widget Icons.widget từ thư viện Icon Material bằng màu đỏ và xanh lam.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( crossAxisAlignment:CrossAxisAlignment.center, textBaseline:TextBaseline.alphabetic, children: [ Icon( Icons.widgets, size:50, color:Colors.blue, ), Icon( Icons.widgets, size:50, color:Colors.red, ), Icon( Icons.widgets, size:50, color:Colors.amber, ), ], ); } }
Widget Image
Widget Image dùng để hiển thị một hình ảnh. Bạn có thể tham chiếu hình ảnh bằng URL hoặc bạn có thể bao gồm hình ảnh bên trong gói ứng dụng của mình. Vì DartPad không thể đóng gói hình ảnh, nên ví dụ sau sử dụng hình ảnh từ mạng.
Ví dụ: Hiển thị hình ảnh
Ví dụ sau hiển thị một hình ảnh được lưu trữ từ xa trên photo của google. Phương thức Image.network có một tham số chuỗi chứa URL của hình ảnh.
Trong ví dụ này, Image.network chứa một URL ngắn.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.network('https://photos.app.goo.gl/Js91nDs2fPcRwGRy5'), ], ); } }
Ví dụ áp dụng
Bạn sắp kết thúc bài viết codelab này. Nếu bạn muốn kiểm tra kiến thức của mình về các kỹ thuật bạn đã học, bạn hãy áp dụng những kỹ năng đó vào việc xây dựng giao diện người dùng Flutter hiển thị danh thiếp như hình dưới đây nhé:
Bạn sẽ chia bố cục của Flutter thành các phần, đó là cách bạn tạo giao diện người dùng Flutter trong thế giới thực.
Trong Phần 1, bạn sẽ triển khai một Column chứa tên và tiêu đề. Sau đó, bạn sẽ bọc Column trong một Row cái có chứa biểu tượng, được đặt ở bên trái của tên và tiêu đề.
Trong Phần 2, bạn sẽ bao ngoài mã Row trong một Column, vì vậy thành ra là mã chứa một Column trong một Row trong một Column. Sau đó, bạn sẽ điều chỉnh bố cục Column của ngoài cùng , để nó trông đẹp mắt hơn. Cuối cùng, bạn sẽ thêm thông tin liên hệ vào danh sách con Column ở ngoài cùng, vì vậy thông tin liên hệ được hiển thị bên dưới tên, tiêu đề và biểu tượng như sau:
Trong Phần 3, bạn sẽ hoàn thành việc xây dựng hiển thị danh thiếp bằng cách thêm bốn biểu tượng khác, được đặt bên dưới thông tin liên hệ.
Phần 1
Bài tập: Tạo tên và tiêu đề
Triển khai một Column chứa hai widget văn bản:
-
Widget Text đầu tiên có tên
Flutter McFluttervà thuộc tínhstyleđược đặt thànhTheme.of(context).textTheme.headline. -
Widget Text thứ hai chứa tiêu đề
Experienced Developer.
Đối với Column, đặt mainAxisSize thành MainAxisSize.min và crossAxisAlignment thành CrossAxisAlignment.start.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Flutter McFlutter', style: Theme.of(context).textTheme.headline), Text('Experienced Developer'), ], ); } }
Bài tập: Gộp cột trong một hàng
Gộp Column mà bạn đã triển khai trong một Row chứa các widget sau:
-
Một widget
Iconđược đặt thànhIcons.account_circlevà có kích thước là 50 pixel. -
Một widget
Paddingtạo ra một không gian 8 pixel xung quanh widgetIcon.Để làm điều này, bạn có thể chỉ định
const EdgeInsets.all(8.0)cho thuộc tínhpadding.Rowsẽ trông như thế này:Row( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Icon(Icons.account_circle, size: 50), ), Column( ... ), // <--- The Column you first implemented ], );
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Icon(Icons.account_circle, size: 50)), Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Flutter McFlutter', style: Theme.of(context).textTheme.headline), Text('Experienced App Developer'), ], ), ], ); } }
Phần 2
Bài tập: Chỉnh sửa bố cục
Bao ngoài Row trong một Column có thuộc tính mainAxisSize được đặt thành MainAxisSize.min và thuộc tính crossAxisAlignmenttính được đặt thành CrossAxisAlignment.stretch. Column chứa các widget sau:
-
Một widget
SizedBoxcó chiều cao là 8. -
Khoảng trống
Rownơi bạn sẽ thêm thông tin liên hệ ở bước sau. -
Widget SizedBox thứ hai có chiều cao là 16.
-
Khoảng trống thứ hai
Rownơi bạn sẽ thêm bốn biểu tượng (Phần 3).
Các widget của Column cần được định dạng như sau để thông tin liên lạc và các biểu tượng được hiển thị dưới tên và chức danh:
],
), // <--- Closing parenthesis for the Row
SizedBox(),
Row(), // First empty Row
SizedBox(),
Row(), // Second empty Row
],
); // <--- Closing parenthesis for the Column that wraps the Row
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Icon(Icons.account_circle, size: 50), ), Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( 'Flutter McFlutter', style: Theme.of(context).textTheme.headline, ), Text( 'Experienced App Developer', ) ], ), ], ), SizedBox(height: 8), Row(), SizedBox(height: 16), Row(), ], ); } }
Bài tập: Nhập thông tin liên hệ
Nhập hai widget Text bên trong ô trống đầu tiên Row:
-
Widget Text đầu tiên chứa địa chỉ
123 Main Street. -
Widget Text thứ hai chứa số điện thoại
(415) 555-0198.
Đối với ô trống đầu tiên Row, hãy đặt thuộc tính mainAxisAlignment thành MainAxisAlignment.spaceBetween.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.account_circle, size: 50),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Flutter McFlutter',
style: Theme.of(context).textTheme.headline,
),
Text(
'Experienced App Developer',
)
],
),
],
),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'123 Main Street',
),
Text(
'(415) 555-0198',
),
],
),
SizedBox(height: 16),
Row(),
],
);
}
}
Phần 3
Bài tập: Thêm bốn biểu tượng
Đưa các widget Icon sau vào ô trống thứ hai Row:
Icons.accessibilityIcons.timerIcons.phone_androidIcons.phone_iphone
Đối với ô trống thứ hai Row, hãy đặt thuộc tính mainAxisAlignmentthành MainAxisAlignment.spaceAround.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Column( mainAxisSize:MainAxisSize.min, crossAxisAlignment:CrossAxisAlignment.stretch, children: [ Row( children: [ Padding( padding: const EdgeInsets.all(8.0), child:Icon(Icons.account_circle, size:50), ), Column( crossAxisAlignment:CrossAxisAlignment.start, mainAxisSize:MainAxisSize.min, children: [ Text( 'Flutter McFlutter', style:Theme.of(context).textTheme.headline, ), Text( 'Experienced App Developer', ) ], ), ], ), SizedBox(height:8), Row( mainAxisAlignment:MainAxisAlignment.spaceBetween, children: [ Text( '123 Main Street', ), Text( '(415) 555-0198', ), ], ), SizedBox(height:16), Row( mainAxisAlignment:MainAxisAlignment.spaceAround, children: [ Icon(Icons.accessibility), Icon(Icons.timer), Icon(Icons.phone_android), Icon(Icons.phone_iphone), ], ), ], ); } }
Tổng kết
Xin chúc mừng, bạn đã hoàn thành codelab! Nếu bạn muốn biết thêm về Flutter, đây là một vài gợi ý về các tài nguyên đáng để khám phá:
- Tìm hiểu thêm về bố cục trong Flutter bằng cách truy cập bài viết Xây dựng bố cục (layout).
- Kiểm tra các ứng dụng mẫu.
- Truy cập kênh YouTube của Flutter , nơi bạn có thể xem nhiều video khác nhau, từ video tập trung vào các tiện ích riêng lẻ đến video của các nhà phát triển đang xây dựng ứng dụng.