Flutter Widget Fundamentals
1. Widget
Trong Flutter, Widget là thành phần cơ bản dùng để xây dựng giao diện người dùng (UI).
Có thể hiểu đơn giản:
Mọi thành phần xuất hiện trên giao diện Flutter đều được xây dựng từ Widget.
Ví dụ:
Text→ hiển thị văn bản.Image→ hiển thị hình ảnh.Icon→ hiển thị biểu tượng.Container→ tạo một vùng chứa.Row→ sắp xếp Widget theo chiều ngang.Column→ sắp xếp Widget theo chiều dọc.ElevatedButton→ tạo nút bấm.Scaffold→ cung cấp cấu trúc cơ bản cho một màn hình.
Ví dụ:
Text('Hello Flutter')
Ở đây Text là một Widget.
Một giao diện phức tạp cũng được tạo bằng cách kết hợp nhiều Widget nhỏ lại với nhau.
2. Widget Tree
Flutter tổ chức giao diện dưới dạng Widget Tree (cây Widget).
Mỗi Widget có thể chứa một hoặc nhiều Widget con.
Ví dụ:
MaterialApp
└── Scaffold
├── AppBar
│ └── Text
│
└── Center
└── Column
├── Text
└── ElevatedButton
Có thể hiểu:
MaterialApplà Widget gốc.Scaffoldnằm bên trongMaterialApp.AppBarvà phầnbodynằm bên trongScaffold.Textnằm bên trongAppBar.Columnnằm bên trongCenter.TextvàElevatedButtonnằm bên trongColumn.
Ví dụ code
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(
child: Column(
children: [
const Text('Hello'),
ElevatedButton(
onPressed: () {},
child: const Text('Click'),
),
],
),
),
),
)
Điểm quan trọng:
Flutter không xây dựng UI bằng cách thao tác trực tiếp từng thành phần giao diện. Thay vào đó, ta mô tả cấu trúc UI bằng một cây Widget.
3. Widget là bất biến (Immutable)
Một đặc điểm quan trọng của Widget trong Flutter là:
Widget thường là immutable object – đối tượng không thể thay đổi sau khi được tạo.
Ví dụ:
Text(
'Hello',
style: TextStyle(
fontSize: 20,
),
)
Sau khi Widget Text được tạo, ta không thay đổi trực tiếp giá trị của nó.
Nếu dữ liệu thay đổi, Flutter sẽ tạo ra cấu hình Widget mới và xây dựng lại phần giao diện cần thiết.
Ví dụ:
Text('$count')
Khi count thay đổi từ:
0 → 1
Flutter sẽ tạo cấu hình Widget mới:
Text('1')
thay vì sửa trực tiếp Widget cũ.
4. StatelessWidget
StatelessWidget là Widget không quản lý State có thể thay đổi bên trong chính nó.
Nó phù hợp với những UI phụ thuộc vào dữ liệu không thay đổi trong vòng đời Widget.
Ví dụ:
class WelcomeText extends StatelessWidget {
const WelcomeText({super.key});
@override
Widget build(BuildContext context) {
return const Text(
'Welcome to Flutter',
);
}
}
Widget này chỉ hiển thị một dòng chữ và không có dữ liệu nội bộ cần thay đổi.
Đặc điểm
StatelessWidget
│
├── Không quản lý State nội bộ
├── Không sử dụng setState()
├── Có phương thức build()
└── Phù hợp với UI tĩnh
Ví dụ thường gặp:
- Logo.
- Tiêu đề.
- Icon.
- Text.
- Card hiển thị dữ liệu.
- Các thành phần UI chỉ nhận dữ liệu từ Widget cha.
5. StatefulWidget
StatefulWidget là Widget có State có thể thay đổi trong quá trình ứng dụng chạy.
Ví dụ:
- Bộ đếm số.
- Checkbox.
- Switch.
- Form nhập dữ liệu.
- Animation.
- Loading.
- Hiển thị/ẩn password.
Một StatefulWidget thường gồm hai phần:
StatefulWidget
│
└── State
├── lưu dữ liệu thay đổi
└── build UI
Ví dụ:
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('$count'),
ElevatedButton(
onPressed: () {
setState(() {
count++;
});
},
child: const Text('Increase'),
),
],
);
}
}
Khi người dùng nhấn nút:
setState(() {
count++;
});
Giá trị count thay đổi.
Flutter nhận biết State đã thay đổi và gọi lại build() để cập nhật UI.
6. State
State là dữ liệu có thể thay đổi và ảnh hưởng đến giao diện của Widget.
Ví dụ:
int count = 0;
count là State nếu giá trị của nó thay đổi và sự thay đổi đó cần được phản ánh lên UI.
Ví dụ:
count = 0
↓
User click
↓
count = 1
↓
UI được cập nhật
↓
Text hiển thị "1"
Một số ví dụ về State:
- Số lượng sản phẩm trong giỏ hàng.
- Trạng thái đăng nhập.
- Trạng thái loading.
- Giá trị TextField.
- Trạng thái bật/tắt Switch.
- Tab hiện tại.
- Danh sách dữ liệu đang hiển thị.
7. setState()
setState() được sử dụng trong StatefulWidget để thông báo cho Flutter rằng State đã thay đổi và UI cần được xây dựng lại.
Ví dụ:
setState(() {
count++;
});
Nếu thay đổi State nhưng không gọi setState():
count++;
thì Flutter không được thông báo để cập nhật UI ngay theo cách của StatefulWidget.
Nguyên tắc
Không nên viết:
setState(() {
// Thực hiện các tác vụ quá nặng
});
setState() nên chủ yếu dùng để cập nhật State liên quan đến UI.
8. Build Method
build() là phương thức quan trọng trong Widget.
Nó có nhiệm vụ:
Mô tả giao diện mà Widget cần hiển thị dựa trên State và dữ liệu hiện tại.
Ví dụ:
@override
Widget build(BuildContext context) {
return const Text(
'Hello Flutter',
);
}
build() phải trả về một Widget.
Ví dụ:
Widget build(BuildContext context) {
return Container();
}
hoặc:
Widget build(BuildContext context) {
return Column(
children: [
const Text('Hello'),
const Text('Flutter'),
],
);
}
Khi nào build() được gọi?
build() có thể được gọi lại khi:
- Widget được tạo.
- State thay đổi.
- Widget cha rebuild.
- Một số dữ liệu phụ thuộc thay đổi.
Vì vậy:
Không nên đặt các tác vụ nặng hoặc tác vụ có side effect vào build().
Ví dụ không nên gọi API trực tiếp trong build().
9. BuildContext
BuildContext đại diện cho vị trí của một Widget trong Widget Tree.
Ví dụ:
Widget build(BuildContext context) {
return Text('Hello');
}
context thường được sử dụng để truy cập các thông tin nằm trong Widget Tree.
Ví dụ:
Theme.of(context)
Lấy Theme hiện tại.
Hoặc:
MediaQuery.of(context)
Lấy thông tin về kích thước màn hình.
Hoặc:
Navigator.of(context)
Truy cập Navigator để chuyển màn hình.
Có thể hiểu đơn giản:
BuildContext giúp Widget biết nó đang nằm ở đâu trong Widget Tree và có thể truy cập các thông tin được cung cấp ở phía trên nó.
10. Parent Widget và Child Widget
Widget có thể có:
- Parent Widget → Widget cha.
- Child Widget → Widget con.
Ví dụ:
Center(
child: Text('Hello'),
)
Ở đây:
Center
└── Text
Center là Parent.
Text là Child.
Một Widget có thể có nhiều Widget con:
Column(
children: [
Text('A'),
Text('B'),
Text('C'),
],
)
Cây:
Column
├── Text A
├── Text B
└── Text C
11. child và children
Flutter thường sử dụng hai thuộc tính:
child
Dùng khi Widget chỉ nhận một Widget con.
Center(
child: Text('Hello'),
)
children
Dùng khi Widget nhận nhiều Widget con.
Column(
children: [
Text('A'),
Text('B'),
Text('C'),
],
)
Một số Widget thường dùng child:
CenterContainerPaddingSizedBoxExpanded
Một số Widget thường dùng children:
ColumnRowStack- Một số Widget chứa danh sách con.
12. Layout Widget
Layout Widget được sử dụng để sắp xếp vị trí và kích thước của các Widget.
Một số Widget quan trọng:
Container
Dùng để tạo vùng chứa và thiết lập:
- width
- height
- padding
- margin
- color
- decoration
Ví dụ:
Container(
width: 200,
height: 100,
padding: const EdgeInsets.all(16),
child: const Text('Hello'),
)
Row
Sắp xếp Widget theo chiều ngang.
Row(
children: [
Icon(Icons.home),
Text('Home'),
],
)
Icon → Text
Column
Sắp xếp Widget theo chiều dọc.
Column(
children: [
Text('Name'),
Text('Email'),
Text('Phone'),
],
)
Name
↓
Email
↓
Phone
Stack
Cho phép đặt các Widget chồng lên nhau.
Stack(
children: [
Image.network('image-url'),
const Text('Product'),
],
)
Thường dùng để:
- Badge.
- Overlay.
- Text trên hình ảnh.
- Floating elements.
13. Padding và SizedBox
Padding
Dùng để tạo khoảng cách bên trong một vùng.
Padding(
padding: const EdgeInsets.all(16),
child: Text('Hello'),
)
SizedBox
Dùng để tạo kích thước hoặc khoảng cách.
Column(
children: [
Text('Hello'),
SizedBox(height: 20),
Text('Flutter'),
],
)
14. MaterialApp
MaterialApp thường được sử dụng làm Widget gốc của ứng dụng Flutter sử dụng Material Design.
Ví dụ:
void main() {
runApp(
MaterialApp(
home: HomePage(),
),
);
}
MaterialApp cung cấp nhiều chức năng quan trọng như:
- Theme.
- Navigation.
- Localization.
- Material Design.
- Cấu hình màn hình ban đầu.
15. Scaffold
Scaffold cung cấp cấu trúc cơ bản cho một màn hình.
Ví dụ:
Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: const Center(
child: Text('Hello'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
)
Một số thành phần thường dùng:
Scaffold
├── appBar
├── body
├── drawer
├── bottomNavigationBar
└── floatingActionButton
16. Widget Composition
Flutter khuyến khích Composition (kết hợp Widget) thay vì tạo những thành phần UI quá lớn.
Ví dụ thay vì tạo một màn hình có hàng trăm dòng code:
class HomePage extends StatelessWidget {
...
}
có thể chia thành:
HomePage
├── AppHeader
├── SearchBar
├── CategoryList
├── ProductList
└── BottomNavigation
Mỗi phần có thể là một Widget riêng:
class ProductCard extends StatelessWidget {
const ProductCard({super.key});
@override
Widget build(BuildContext context) {
return Card(
child: const Text('Product'),
);
}
}
Ưu điểm:
- Code dễ đọc.
- Dễ tái sử dụng.
- Dễ bảo trì.
- Dễ kiểm thử.
- Giảm độ phức tạp của từng Widget.
17. Widget Lifecycle cơ bản
Đối với StatefulWidget, State có một vòng đời.
Một số phương thức quan trọng:
createState()
↓
initState()
↓
build()
↓
didUpdateWidget()
↓
build()
↓
dispose()
initState()
Được gọi một lần khi State được tạo.
Thường dùng để:
- Khởi tạo dữ liệu.
- Tạo Controller.
- Đăng ký Listener.
- Thực hiện một số thao tác khởi tạo.
Ví dụ:
@override
void initState() {
super.initState();
// Khởi tạo
}
dispose()
Được gọi khi Widget bị loại khỏi Widget Tree.
Thường dùng để giải phóng:
- Controller.
- AnimationController.
- StreamSubscription.
- Listener.
Ví dụ:
@override
void dispose() {
controller.dispose();
super.dispose();
}
18. Widget Rebuild
Rebuild nghĩa là Flutter chạy lại build() để tạo cấu hình Widget mới.
Ví dụ:
User click button
↓
setState()
↓
State thay đổi
↓
build() được gọi lại
↓
Widget configuration mới
↓
Flutter cập nhật UI cần thiết
Quan trọng:
Rebuild không có nghĩa là Flutter xóa toàn bộ màn hình rồi vẽ lại mọi thứ từ đầu.
Flutter sử dụng cơ chế so sánh và quản lý Widget/Element/RenderObject để cập nhật phần cần thiết.
19. Widget, Element và RenderObject
Flutter có kiến trúc gồm nhiều lớp.
Có thể hiểu đơn giản:
Widget
↓
Element
↓
RenderObject
↓
Giao diện trên màn hình
Widget
Mô tả UI.
Element
Quản lý Widget trong Widget Tree và duy trì vị trí của Widget trong cây.
RenderObject
Chịu trách nhiệm về:
- Layout.
- Painting.
- Kích thước.
- Vị trí.
- Hiển thị.
Đây là kiến thức nền quan trọng để hiểu cách Flutter hoạt động sâu hơn.
20. Key
Key được sử dụng để xác định và nhận diện Widget trong Widget Tree.
Ví dụ:
ListTile(
key: const ValueKey(1),
title: const Text('Product'),
)
Key đặc biệt hữu ích khi:
- Làm việc với danh sách.
- Widget được reorder.
- Widget có State.
- Cần giữ State của Widget khi cấu trúc cây thay đổi.
Một số loại Key:
ValueKeyObjectKeyUniqueKeyGlobalKey
21. Const Widget
Flutter khuyến khích sử dụng const khi Widget và dữ liệu của nó có thể được xác định tại compile time.
Ví dụ:
const Text('Hello');
Thay vì:
Text('Hello');
const giúp Flutter có thể tái sử dụng các object bất biến trong những trường hợp phù hợp.
Ví dụ:
const Column(
children: [
Text('Hello'),
Text('Flutter'),
],
)
22. Ví dụ tổng hợp
Một màn hình Flutter đơn giản:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Fundamentals'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Text(
'Hello Flutter',
style: TextStyle(
fontSize: 24,
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {},
child: const Text('Click Me'),
),
],
),
),
),
);
}
}
Widget Tree:
MyApp
└── MaterialApp
└── Scaffold
├── AppBar
│ └── Text
│
└── Padding
└── Column
├── Text
├── SizedBox
└── ElevatedButton
└── Text
Qua ví dụ này có thể thấy Flutter UI được xây dựng bằng cách lồng các Widget vào nhau.
23. Các nguyên tắc quan trọng
1. Mọi UI đều được xây dựng từ Widget
UI
↓
Widget
↓
Widget
↓
Widget
2. Widget nên nhỏ và có trách nhiệm rõ ràng
Không nên tạo một Widget khổng lồ chứa toàn bộ logic và UI của ứng dụng.
3. Ưu tiên Composition
Kết hợp các Widget nhỏ để tạo Widget lớn.
4. Không đặt logic nặng trong build()
build() có thể được gọi nhiều lần.
5. Dùng StatelessWidget khi không cần State
Không nên dùng StatefulWidget nếu UI không có State cần quản lý.
6. Dùng setState() khi thay đổi State cục bộ
setState(() {
count++;
});
7. Giải phóng tài nguyên
Các Controller, Subscription hoặc Listener cần được dispose khi không còn sử dụng.
24. Các khái niệm cần nhớ
| Khái niệm | Ý nghĩa |
|---|---|
| Widget | Thành phần xây dựng UI |
| Widget Tree | Cấu trúc cây của các Widget |
| StatelessWidget | Widget không quản lý State thay đổi |
| StatefulWidget | Widget có State thay đổi |
| State | Dữ liệu có thể thay đổi và ảnh hưởng UI |
| setState() | Thông báo State thay đổi |
| build() | Mô tả UI |
| BuildContext | Vị trí Widget trong Widget Tree |
| child | Một Widget con |
| children | Nhiều Widget con |
| Parent | Widget cha |
| Child | Widget con |
| Key | Định danh Widget |
| Composition | Kết hợp nhiều Widget |
| Rebuild | Chạy lại quá trình build |
| Widget Tree | Cấu trúc UI dạng cây |
| RenderObject | Thành phần thực hiện layout và rendering |
25. Tóm tắt
Flutter Widget Fundamentals là nền tảng quan trọng nhất khi bắt đầu học Flutter.
Cần nắm chắc các ý chính:
Flutter UI
↓
Widget
↓
Widget Tree
↓
StatelessWidget / StatefulWidget
↓
State
↓
setState()
↓
build()
↓
Rebuild
↓
UI được cập nhật
Có thể ghi nhớ ngắn gọn:
Widget là building block của Flutter UI. Các Widget được kết hợp thành Widget Tree. StatelessWidget dùng cho UI không quản lý State nội bộ, StatefulWidget dùng khi UI có State thay đổi. Khi State thay đổi thông qua setState(), Flutter rebuild Widget và cập nhật giao diện cần thiết. BuildContext đại diện cho vị trí của Widget trong Widget Tree và cho phép truy cập các thông tin từ Widget Tree.






Khoá học lập trình game con rắn cho trẻ em




