I. Mở đầu
Khi các bạn build một ứng dụng với Flutter thì Widgets là thứ không thể thiếu đúng không ạ. Và 2 loại Widget không thể thiếu đó là StatefullWidget và StatelessWidget. Trong bài này mình sẽ chia sẻ với các bạn và 2 loại Widget này nhé !
II. Chi tiết
1. Widgets
- Widget chính là nền tảng của Flutter, một widget miêu tả một phần của giao diện người dùng. Tất cả các component như text, image, button hay animation, theme, layout hay thậm chí app cũng là 1 widget. Trong Flutter tất cả các widget hay giao diện đều được code bằng dart
- Khi một widget thay đổi trạng thái, chẳng hạn như do người dùng click hay animation, widget sẽ tự xây dựng lại theo trạng thái mới. Điều này tiết kiệm thời gian của nhà phát triển bởi vì UI có thể được mô tả như là một state functions. Ta không phải viết thêm code để chỉ update UI khi state change.
- Widget đơn giản là những class. Ví dụ: class Text. À ra là như vậy, nếu liên kết những yếu tố này lại ta có thể hình dung một class Widget có những property lưu trữ và thể hiện những thông tin của Widget như thế này:
class Text { // Widget chỉ là class String text; // các property lưu trữ thông tin của widget Color textColor; // thông tin về màu sắc của text int fontSize; // thông tin về kích cỡ của font
}
2. Build function
Bạn có biết công thức toán học quen thuộc y = f(x). Đó là một hàm số, khi ta có giá trị của x, dựa vào một function f ta sẽ tính được giá trị y. Bất cứ khi nào x thay đổi thì cũng cho ta một giá trị mới của y đúng ko. Flutter cũng tương tự như vậy, nó sử dụng công thức là:
UI = f(Data)
Khi Data của Widget thay đổi, UI sẽ được update theo công thức hàm f. Các bạn thấy khi mình click vào button màu xanh đó thì UI được thay đổi hiển thị con số khác đúng ko. Con số 0, 1, 2, 3, ... đó chính là Data của Widget. Data đó được lưu trữ trong một biến có kiểu int. Khi Data thay đổi nó sẽ thay đổi UI theo công thức hàm f.
Như vậy, chúng ta đã hình dung được Data của Widget và hàm build và mối quan hệ UI = build(Data)
. Đây chính là nguyên tắc hoạt động của Flutter. Chúng ta sẽ đi đến định nghĩa kế tiếp về State
.
3. State là gì
Giả sử như bây giờ, bạn tự tạo ra một cái Widget là cái bóng đèn đi. Cái bóng đèn sẽ có những thông tin nào nhỉ:
- Kích cỡ (size) của bóng đèn có kiểu
int
. Thông tin này không bao giờ thay đổi. Ví dụ cái bóng đèn khi sản xuất có size 20 chẳng hạn, thì 10 năm sau cũng sẽ có size 20 chứ nó không thể to ra hay nhỏ lại theo năm tháng được, trừ khi nó bị đập phá (Widget die) =)) - Màu sắc đang hiển thị đèn kiểu
Color
, màu bóng đèn default sẽ là yellow, nhưng có lúc sẽ đổi thành red, lúc thì blue. Đây là một thông tin có thể thay đổi, cứ một vài milisecond thì nó sẽ thay đổi một lần. Nếu màu đèn nó không thay đổi thì có thể là bị hư (Widget die) =))
Như vậy class được mình đặt tên là BulbLightWidget này có 2 property là size
(kích cỡ của bóng đèn) và currenLight (màu của bóng đèn hiện tại). Biến size
có 1 giá trị mãi mãi không thay đổi nên sẽ khai báo final height
, biến currentLight
có thể thay đổi được bằng cách gán lại giá trị mới nên khai báo var currentLight
hoặc Color currentLight
.
class BulbLightWidget { final size = 20; // height này cố định ko thay đổi nên để final Color currentLight = Colors.red; // màu đèn sẽ thay đổi từ đỏ, vàng, xanh
}
Như vậy, có thể phân chia data của Widget thành 2 loại: Thông tin có thể thay đổi và thông tin không thể thay đổi.
Còn đây là định nghĩa đơn giản, ngắn gọn nhất về State:
State là thông tin thể hiện trên Widget mà có thể thay đổi trong suốt thời gian sống sót trên đời của Widget
Trong cái widget bóng đèn đó thì cái data currentLight
đó chính là state vì nó thay đổi được. Khi nó thay đổi thì widget BulbLightWidget
phải build lại giao diện khác. Ví dụ currentLight = Colors.red
thì widget hiển thị đèn đỏ. Khi currentLight
thay đổi thành currentLight = Colors.green
thì widget phải build lại để hiển thị đèn xanh. Còn cái size chỉ là data bình thường, không phải là state vì nó luôn không đổi, cái bóng đèn sẽ mãi mãi to chừng đó.
Okay, mình đã hiểu có Data, có công thức là hàm build
thì mình sẽ xây được UI. Khi Data thay đổi, hàm build
sẽ được gọi lại để update UI (chúng ta gọi đây là rebuild Widget). Có 2 loại widget là StatefulWidget
và StatelessWidget
, loại nào cũng có hàm build
nhưng cách chúng gọi hàm build
để update UI là khác nhau:
Một là: StatefulWidget
, bản thân Widget này sẽ chủ động update UI.
Hai là: StatelessWidget
, bản thân Widget này sẽ thụ động update UI, hay nói cách khác là bị Widget khác ép phải update UI.
Như thế nào là chủ động, như thế nào là bị ép. Chúng ta sẽ đến với cách đầu tiên, chủ động update UI sử dụng StatefulWidget
.
4. StatelessWidget
Stateless widget không có state. Nó không chấp nhận sự thay đổi bên trong nó. Còn đối với sự thay đổi từ bên ngoài (widget cha) thì nó sẽ thụ động thay đổi theo.
Có nghĩa là StatelessWidget chỉ đơn thuần nhận dữ liệu và hiển thị 1 cách thụ động. Việc tương tác với nó không sinh ra bất kỳ một event nào để chính bản thân phải render lại. Nếu phải render lại thì là do tác động từ bên ngoài vào.
Vậy nên, nó không có liên quan gì đến State cả. Bản thân nó cũng không có hàm createState
mà thay vào đó là hàm build(BuildContext)
Ví dụ: ta có thể nhìn 1 vài mẫu Stateless widgets.
Xét loại Text widget là để khởi tạo trong một constructor và những properties thường để build widget và hiển thị lên màn hình
5. StatefullWidget
StatefulWidget
là chỉ đơn giản là một Widget mà CÓ State
tức là nó có data có thể thay đổi được. Khi state thay đổi, nó sẽ gọi lại hàm build
để rebuild widget. Nhờ đó mà UI thay đổi.
Đây là cấu tạo của một StatefulWidget:
class BulbLightWidget extends StatefulWidget { // 1 final size = 20; // mọi data trong class Widget phải immutable // data nào mutable xin mời qua class khác, là class State bên dưới :D // khi StatefulWidget được khởi tạo nó sẽ gọi hàm createState để tạo 1 object State @override State<StatefulWidget> createState() { return BulbLightState(); }
} // khi object Widget gọi hàm createState thì object State ra đời
class BulbLightState extends State<BulbLightWidget> { // 2 var currentLight = 'đỏ'; // hàm build @override Widget build(BuildContext context) { return Text('Kích thước đèn ${widget.size} và đèn đang có màu $currentLight'); // 3 }
}
Ban đầu khi khởi tạo StatefulWidget
, hàm build
đã được gọi một lần đầu tiên, và nó nhận default state
để update UI: UI = build(default state)
. Mỗi lần chúng ta gọi hàm setState thì nó sẽ gọi lại hàm build để update UI mới: UI = build(new state)
Demo StatefullWidget
import 'package:flutter/material.dart'; void main() { runApp(MyApp());
} class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); }
} // Đây là một StatefulWidget
class MyHomePage extends StatefulWidget { @override MyHomePageState createState() => MyHomePageState();
} // Đây là class State của StatefulWidget MyHomePage
class MyHomePageState extends State<MyHomePage> { int counter = 0; // Data của Widget @override Widget build(BuildContext context) { // hàm build return Scaffold( body: Center( // data là biến counter được truyền vào hàm build - công thức UI = build(Data) child: Text('Tui là widget Text. Data của tui hiện tại là: $counter') ), floatingActionButton: FloatingActionButton( onPressed: () { // khi click button màu xanh blue setState(() { // ta sẽ gọi hàm setState counter++; // để gán lại giá trị mới cho biến counter // bên trong hàm setState này sẽ tự động gọi lại hàm build nên UI được update (rebuild) }); }, child: Icon(Icons.add), ), ); }
}
Đây là thành quả:
III. Tổng kết lại kiến thức
Đây là những kiến thức quan trọng trong bài, mình sẽ tổng kết ngắn gọn lại như sau:
- Scene = f(Data) trong đó
f
là hàmbuild
có trong mỗiStatelessWidget
hayStatefulWidget
. Flutter hoạt động theo công thức đó: Khi thay đổi Data thì UI sẽ được update. State
chính là Mutable data của widget nênState
là data của Widget mà có thể thay đổi được.StatefulWidget
là widget cóState
, cònStatelessWidget
là widget không có State.StatefulWidget
có thể chủ động update UI bằng cách gọi hàmsetState
.- Data trong
StatelessWidget
không thể thay đổi được nên nó muốn update được UI thì phải nhờ thằng cha là mộtStatefulWidget
nào đó có khả năng thay đổi data giúp nó rồi truyền data xuống cho nó thông qua constructor. - Mọi data trong class
StatefulWidget
vàStatelessWidget
đều phải là immutable - Hãy sử dụng
StatelessWidget
hết mức có thể - các đồng nghiệp sẽ yêu mến bạn.