Flutter: Codelab

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Mục lục bài viế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à conRow và Column được gọi là chaRow đặ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.max là giá trị mặc định của thuộc tính mainAxisSize. 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.max thành MainAxisSize.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.end sang 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.start thà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 flex thà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 trong Row hoặc Column. 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ụng Expanded để 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 Spacer khi bạn muốn tạo không gian bằng thuộc tính flex. Sử dụng SizedBox khi 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é:

Danh thiếp đã hoàn thà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 đề.

Danh thiếp đã hoàn thành

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:

Danh thiếp đã hoàn thành

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ệ.

Danh thiếp đã hoàn thành

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 McFlutter và thuộc tính style được đặt thành Theme.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ành Icons.account_circle và có kích thước là 50 pixel.

  • Một widget Padding tạo ra một không gian 8 pixel xung quanh widget Icon.

    Để làm điều này, bạn có thể chỉ định const EdgeInsets.all(8.0) cho thuộc tính padding.

    Row sẽ 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.stretchColumn chứa các widget sau:

  • Một widget SizedBox có chiều cao là 8.

  • Khoảng trống Row nơ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 Row nơ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.accessibility
  • Icons.timer
  • Icons.phone_android
  • Icons.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á:

Nguồn: flutter.dev
» Tiếp: Giao diện người dùng: Giới thiệu các widget
« Trước: Viết ứng dụng Flutter đầu tiên của bạn, phần 1
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!