Trong bài học ngày hôm nay, chúng ta sẽ tìm hiểu về một khái niệm mang tính "linh hồn" và cốt lõi nhất khi lập trình giao diện bằng Flutter, đó chính là Widget.
1. Widget là gì?
Trong thế giới của Flutter, có một quy tắc tối thượng: "Mọi thứ đều là Widget".
Về mặt kỹ thuật, một Widget là một phần khai báo bất biến (immutable) và là khối xây dựng cơ bản (building block) để tạo nên Giao diện người dùng (UI) của ứng dụng Flutter. Bạn có thể hình dung Widget giống như những viên gạch Lego. Flutter khuyến khích kỹ thuật "lắp ráp" (composition), kết hợp nhiều Widget nhỏ, đơn chức năng lại với nhau để tạo thành một màn hình UI hoàn chỉnh và phức tạp.
Các Widget sử dụng phương thức build() để khai báo giao diện của chúng. Về cơ bản, hàm này thực hiện việc chuyển đổi trạng thái của ứng dụng thành giao diện hiển thị trên màn hình theo công thức: UI = f(state). Nhờ tốc độ khởi tạo và hủy đối tượng cực nhanh của Dart, Flutter có thể gọi hàm build() này bao nhiêu lần tùy thích để cập nhật màn hình mà không làm giảm hiệu năng.
2. Hai phân loại Widget cốt lõi
Flutter phân loại các Widget vào hai nhóm chính dựa trên khả năng thay đổi trạng thái:
- StatelessWidget (Widget không trạng thái): Là những Widget không có trạng thái biến đổi (mutable state), có nghĩa là tất cả các thuộc tính của chúng không bao giờ thay đổi theo thời gian. Chẳng hạn như một đoạn văn bản (Text) tĩnh hay một biểu tượng (Icon) cố định.
- StatefulWidget (Widget có trạng thái): Là những Widget sở hữu các đặc tính có thể thay đổi dựa trên các thao tác tương tác của người dùng hoặc các yếu tố dữ liệu khác. Đi kèm với Widget này là một đối tượng
State. Khi bạn gọi hàmsetState(), Flutter sẽ tự động gọi lại hàmbuild()của State đó để cập nhật UI.
3. Cấu trúc Cây Widget (Widget Tree)
Khi các Widget lồng vào nhau, chúng sẽ tạo thành một hệ thống phân cấp gọi là Cây Widget (Widget Tree). Thông thường, gốc của cây (màn hình ngoài cùng) sẽ là một MaterialApp (dành cho giao diện chuẩn Google) hoặc CupertinoApp (dành cho giao diện chuẩn Apple).
Trong Flutter, việc xây dựng giao diện người dùng dựa trên nguyên tắc lắp ráp (composition), trong đó bạn kết hợp các Widget nhỏ, mang đơn chức năng lại với nhau để tạo thành một hệ thống phân cấp phức tạp (Cây Widget).
Dưới đây là các thành phần chính và cách để xây dựng một Widget:
4. Các thành phần chính của một Widget
Dù là Widget tự định nghĩa hay có sẵn, một Widget thường cấu thành từ các yếu tố sau:
- Lớp (Class) kế thừa: Để tạo một Widget, lớp của bạn bắt buộc phải kế thừa từ
StatelessWidget(dành cho giao diện không có trạng thái thay đổi) hoặcStatefulWidget(dành cho giao diện có dữ liệu thay đổi theo thời gian). - Phương thức
build(): Đây là nơi bạn khai báo giao diện của Widget. Hàm này nhận vào một tham sốBuildContextvà luôn phải trả về một cây Widget (Widget tree) mới. Về bản chất, hàmbuild()thực hiện việc chuyển đổi trạng thái thành giao diện người dùng theo công thức UI = f(state). - Đối tượng
State(Chỉ dành cho StatefulWidget): Nếu bạn dùngStatefulWidget, thành phần giao diện sẽ đi kèm với một lớpStateriêng biệt chứa dữ liệu có thể thay đổi (mutable state). Lớp này cung cấp hàmsetState()dùng để thông báo cho Flutter biết dữ liệu đã thay đổi, từ đó gọi lại hàmbuild()để cập nhật lại màn hình.
5. Cách xây dựng một Widget
Để xây dựng một Widget, bạn thực hiện theo các bước sau:
- Bước 1: Khai báo một class mới và cho kế thừa từ
StatelessWidgethoặcStatefulWidget. - Bước 2: Định nghĩa hàm khởi tạo (constructor) để nhận các tham số truyền vào từ bên ngoài (nếu có).
- Bước 3: Ghi đè (override) phương thức
build(BuildContext context). - Bước 4: Lắp ráp các Widget cơ bản có sẵn của Flutter (như
Container,Text,Row,Column,Center,...) và trả về (return) chúng bên trong hàmbuild()để tạo hình cấu trúc giao diện mong muốn,.
Ví dụ minh họa cách xây dựng một StatelessWidget cơ bản:
import 'package:flutter/material.dart';
// Bước 1: Kế thừa StatelessWidget
class MyApp extends StatelessWidget {
// Bước 2: Hàm khởi tạo
const MyApp({Key? key}) : super(key: key);
// Bước 3: Ghi đè phương thức build()
@override
Widget build(BuildContext context) {
// Bước 4: Trả về cấu trúc lồng ghép các Widget có sẵn
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text(
'Hello World',
style: TextStyle(fontSize: 24),
),
),
),
);
}
}
Nhờ tốc độ khởi tạo và hủy đối tượng rất nhanh của Dart, Flutter có thể gọi hàm build() này liên tục để cập nhật lại giao diện màn hình mà không làm giảm hiệu năng của ứng dụng. Bạn chỉ việc mô tả giao diện sẽ trông như thế nào ở một trạng thái cụ thể, và Flutter sẽ tự động lo phần hiển thị.
6. Ví dụ minh họa
Dưới đây là một đoạn code ví dụ kinh điển kết hợp nhiều Widget cơ bản để tạo ra một màn hình ứng dụng đơn giản:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('My Home Page'),
),
body: Center(
child: Builder(
builder: (BuildContext context) {
return Column(
children: [
const Text('Hello World'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
print('Click!');
},
child: const Text('A button'),
),
],
);
},
),
),
),
);
}
}
Giải thích ví dụ:
- Trong ví dụ trên, class
MyAppcủa chúng ta là một StatelessWidget vì giao diện khung này không cần thay đổi dữ liệu động, và chúng ta ghi đè phương thứcbuild()để khai báo UI,. - Cây Widget được xây dựng thông qua việc lồng ghép (nesting):
MaterialApp: Khung ứng dụng cao nhất.Scaffold: Cung cấp cấu trúc nền tảng cho trang (có app bar, body).AppBar: Thanh tiêu đề trên cùng với mộtText.Center: Một Widget bố cục giúp căn giữa các thành phần con.Column: Một Widget bố cục để xếp các Widget con (children) theo chiều dọc từ trên xuống dưới. Bên trong nó chứa một dòng chữText, một khoảng trốngSizedBoxvà một nút bấmElevatedButton.
sơ đồ cây Widget (Widget Tree) chi tiết để bạn dễ dàng hình dung cấu trúc code:
Sơ đồ cây Widget chi tiết
MaterialApp (Gốc của ứng dụng)
Scaffold (Khung sườn màn hình)
AppBar (Thanh tiêu đề)
leading: Icon (Menu)title: Text ("Máy tính lãi suất")actions: [Icon (More)]
Padding (Tạo khoảng cách lề)
Column (Sắp xếp theo chiều dọc)
Row (Hàng 1: Số tiền)
Expanded (Label) $\rightarrow$ Text ("Số tiền")
Expanded (Input) $\rightarrow$ TextField
SizedBox (Khoảng cách)
Row (Hàng 2: Lãi suất)
Expanded (Label) $\rightarrow$ Text ("Lãi hàng năm")
Expanded (Input) $\rightarrow$ TextField
SizedBox (Khoảng cách)
Row (Hàng 3: Kết quả)
Text ("Số năm để tiền tăng gấp đôi")
Text (Biến
_yearsToDouble)
Spacer (Đẩy nút bấm xuống dưới cùng)
Align (Căn lề phải)
ElevatedButton (Nút tính toán)
Text ("Tính toán")
Chỉ với việc xếp chồng các Widget nhỏ này vào nhau, bạn đã tạo ra được một màn hình ứng dụng đầy đủ chức năng!
Tóm lại: Khi học Flutter, việc của các bạn phần lớn là học cách làm chủ các loại Widget có sẵn và cách tự định nghĩa các Widget mới để "vẽ" lên giao diện mong muốn. Chúc các bạn code vui vẻ!
![[Bài 1] Giới thiệu về Widget trong Flutter [Tự học Flutter] [Bài 1] Giới thiệu về Widget trong Flutter [Tự học Flutter]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfuXzMuHawJJardwiNILeAzMjlLIewP-V5B_C3gAyQFWZWzz1_2UzJCNCXiTJzd79xBwu10tQIOyPMDo9ejivKY-Jr9_P4KSmcfbTM97ks2syE5iXys2szrKHAqCqDET5rjcJm58mpRNLEAg9rM6z0O250nPIkpbNoWDRj4_PBRPRkcATGS55UzmOcgT65/w640-h358-rw/unnamed.png)
.png)



.png)



