Flutter: So sánh StatefulWidget và StatelessWidget
Khi tiếp xúc với Flutter, có lẽ các bạn đều đã quen hoặc nếu không quen thì cũng đã từng nghe câu “Everything is a widget!” – có nghĩa là tất cả mọi thứ đều là widget. Text là một widget, Button là một Widget, các Animation cũng là Widget, thậm chí bản thân app cũng là một Widget. Nghe nhiều là vậy nhưng thực chất các widget này lại được chia thành hai nhóm chính là StalessWidget và StatefulWidget.
Mình cũng khá chắc là trong số những bạn đang đọc bài viết này cũng có một số bạn đã biết về hai loại này nhưng để hiểu về nó thì còn khá mơ hồ. Vậy nên bài viết này mình sẽ cố gắng trình bày về chúng một cách đơn giản nhất có thể để các bạn nắm bắt được.
1. Chuẩn bị
Về IDE:
- Visual Studio Code
- Android Studio
- XCode
- Flutter SDK
- Kiến thức: ngôn ngữ lập trình Dart và Flutter cơ bản
2. Khái niệm State
State trong tiếng Anh có nghĩa là trạng thái. Trong thế giới của mobile dev, nó thể hiện các trạng thái của ứng dụng. Mình ví dụ đơn giản nhé. Khi các bạn check / uncheck check box thì đó là hai trạng thái của check box. Ví dụ rõ ràng hơn nữa là màn hình đăng nhập thể hiện ba trạng thái nhập email / mật khẩu sai định dạng (validate error), đăng nhập thành công hoặc đăng nhập fail (api error).
Flutter chuyển đổi cách lập trình truyền thống từ lập trình mệnh lệnh (imperative) sang lập trình khai báo (declarative). Điều này có nghĩa là Flutter UI ánh xạ các trạng thái trong app:
UI = f(state)
Trong định nghĩa của trang Flutter ở đây có nhắc đến, State có thể được đọc một cách đồng bộ khi widget được build và có thể thay đổi trong suốt vòng đời của widget đó.
Vậy thì Stateless Widget và Stateful Widget là gì? Chúng khác nhau ra sao? Chúng ta cùng tìm hiểu tiếp nhé!
3. StatelessWidget vs StatefulWidget
3.1. StatelessWidget
Stateless widget là các widget có trạng thái không thể thay đổi sau khi chúng được build. Chúng chỉ đơn thuần nhận dữ liệu và hiển thị một cách thụ động. Nếu muốn render lại thì ta phải khởi tạo lại chúng.
Một ví dụ về stateles widget là Text. Nó chỉ đơn giản hiển thị các văn bản mà chúng ta truyền cho nó. Ví dụ khác là IconButton. Nó chỉ khác Text ở phần là chúng ta có thể tương tác với button nhưng điều đó cũng không làm thay đổi bất cứ điều gì ở button.
Vòng đời: Vì không thay đổi nên vòng đời của stateless widget khá đơn giản, chỉ xoay quanh hàm build(). Câu hỏi được đặt ra ở đây là hàm build() của chúng sẽ được gọi khi nào? Mình có thể liệt kê ra những thời điểm như sau:
- Lần đầu tiên widget được khởi tạo và chèn vào trong widget tree. Tìm hiểu thêm về widget tree tại Flutter cơ bản: Widget Tree, Element Tree và Render Tree.
- Khi widget cha thay đổi cấu hình thì cũng sẽ kích hoạt hàm build() của các widget con và khiến chúng được render lại.
3.2. StatefulWidget
Stateful widget là các widget có thể thay đổi trạng thái. Khác với stateless widget, chúng ta không cần thiết phải khởi tạo lại stateful widget để thay đổi chúng mà chỉ cần thay đổi state của chúng và Flutter sẽ gọi hàm build() để render lại UI cho phù hợp với state đó.
Nghe hơi mơ hồ phải không? Mình ví dụ như nút like của facebook. Khi user bấm like thì nó sẽ đổi thành màu xanh phải không? Và khi user click thêm một lần nữa thì nó chuyển lại màu xám. Đó chính xác là một stateful widget.
Vòng đời: Vòng đời của Stateful widget thì khá phức tạp, các bạn hãy theo dõi hình sau đây nhé!
Giải thích:
Trước khi đi vào giải thích trên từng con số ở ảnh thì mình muốn mọi người lưu ý một chút. Một statefut widget sẽ bao gồm hai phần là một widget và một state object. Bản thân đối tượng StatefulWidget là bất biến (immutable) và lưu trữ trạng thái có thể thay đổi (mutable) của chúng trong State object.
Trên hình mình chia ra làm 3 phần là:
- Time to trigger: thời điểm kích hoạt
- StatefulWidget: phần widget mà mình nhắc đến của StatefulWidget
- State object: phần trạng thái có thể thay đổi được của StatefulWidget
Bây giờ cùng phân tích những con số trên hình nào!!!
(1): Thời điểm đầu tiên widget được khởi tạo và chèn vào trong widget tree.
(2) và (3): Sau khi gọi constructor, Flutter sẽ gọi hàm createState() để tạo ra đối tượng của State cho widget. Kể từ đó về sau, Flutter đa số chỉ làm việc State object mà thôi (vì đối tượng của StatefulWidget là bất biến, như mình đã nói ở trên).
(4): Trên State object, sau khi gọi hàm createState() thì hàm initState() sẽ được gọi. Và hàm này chỉ được gọi chỉ một lần duy nhất cho mỗi 1 đối tượng của State. Bạn có thể override phương thức này để khởi tạo các thuộc tính, khởi tạo dữ liệu, đăng ký Streams, ChangeNotifiers, vân vân và mây mây….
(5): Phương thức didChangeDependencies() được gọi ngay sau initState lần đầu tiên.
(6): Hàm build() được gọi để render UI. Vậy là hoàn thành các bước kể từ lần đầu tiên widget được khởi tạo. Sau đây sẽ là những bước thực hiện khi có bất kỳ sự thay đổi nào.
(7): Trường hợp InheritedWidget mà widget có dữ liệu phụ thuộc vào thay đổi thì sẽ kích hoạt hàm didChangeDependencies(), sau đó Flutter sẽ gọi build() để render lại UI.
(8) và (9): Giả sử như user tác động lên UI để thay đổi state của widget (ví dụ như like / unlike) thì khi đó việc của các developer chúng ta sẽ là gọi hàm setState() để thay đổi trạng thái của object. Việc này sẽ giúp cho framework được thông báo về sự thay đổi này và sẽ gọi hàm build() để render lại UI tương ứng.
(10) và (11): Khi widget cha của nó build lại và yêu cầu các widget con build cập nhật lại, Flutter sẽ thực hiện việc gọi hàm didUpdateWidget() . Sau đó, hàm build() cũng sẽ được gọi.
(12): Hàm dispose() được gọi khi state object bị xoá khỏi widget tree vĩnh viễn.
Tóm lại, như bạn đã thấy ở trên hàm build() là cái hàm được gọi liên tục. Bởi vì sao? Bởi vì nó là nơi quyết định UI, nên khi có bất kỳ sự thay đổi cần thiết nào trên UI, nó sẽ được gọi lại để render tương ứng.
4. Cách làm việc với StatefulWidget và StatelessWidget trong Flutter
4.1. Đối với StatefullWidget
Dưới đây là một ví dụ khi sử dụng StatefulWidget
Trong ví dụ này, chúng ta sử dụng StatefulWidget để tạo một ứng dụng đếm số đơn giản. Khi người dùng nhấn vào nút “+” trên giao diện, giá trị của _counter sẽ tăng lên và giao diện sẽ được cập nhật lại thông qua hàm setState, cho phép hiển thị giá trị mới.
4.2. Đối với StatelessWidget
Dưới đây là một ví dụ khi sử dụng StatelessWidget
Trong ví dụ này, chúng ta sử dụng StatelessWidget để hiển thị một tin nhắn cố định “Hello, this is a Stateless Widget example.” trên giao diện. Do đây là một Stateless Widget, nên nội dung không thể thay đổi trong quá trình chạy ứng dụng.
Lưu ý rằng StatefulWidget có khả năng thay đổi trạng thái (mutable state) trong khi StatelessWidget không thể thay đổi trạng thái của nó sau khi được tạo ra.
5. Tổng kết
Tóm lại sự khác nhau rõ rệt và có thể dễ dàng thấy được nhất giữa StalessWidget và StatefulWidget chính là khả năng reload của widget ở runtime. Nếu như StalessWidget không thể thay đổi chỉ trừ khi được khởi tạo lại thì StatefulWidget có các trạng thái có thể thay đổi được và Flutter sẽ render lại UI cho phù hợp với từng trạng thái.
Bài viết cũng khá dài nên mình xin tạm gác bút lại ở đây. Hẹn gặp các bạn vào các bài viết tiếp theo của mình nhé!
Leave a Reply
Want to join the discussion?Feel free to contribute!