StatelessWidget vs StatefulWidget

1. Khái niệm

Trong Flutter, StatelessWidget và StatefulWidget là hai loại Widget cơ bản dùng để xây dựng giao diện.

Điểm khác biệt quan trọng nhất nằm ở State (trạng thái).

StatelessWidget: Widget không có State nội bộ có thể thay đổi.

StatefulWidget: Widget có State nội bộ có thể thay đổi trong quá trình ứng dụng chạy.

Có thể hiểu đơn giản:

StatelessWidget
→ UI phụ thuộc vào dữ liệu hiện tại
→ Không tự quản lý State thay đổi

StatefulWidget
→ UI có dữ liệu thay đổi
→ Có State để quản lý dữ liệu đó

2. StatelessWidget

StatelessWidget là Widget dùng để xây dựng những thành phần UI không cần quản lý trạng thái thay đổi bên trong Widget.

Ví dụ:

class WelcomeText extends StatelessWidget {
  const WelcomeText({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text(
      'Welcome to Flutter',
    );
  }
}

Widget trên chỉ hiển thị một dòng chữ.

Không có dữ liệu nào bên trong Widget cần thay đổi trong quá trình sử dụng.

Đặc điểm

StatelessWidget
├── Không có State riêng
├── Không sử dụng setState()
├── Có phương thức build()
├── UI phụ thuộc vào dữ liệu được cung cấp
└── Phù hợp với UI tĩnh hoặc UI không quản lý trạng thái nội bộ

3. Ví dụ StatelessWidget

Một ProductCard có thể được xây dựng bằng StatelessWidget:

class ProductCard extends StatelessWidget {
  final String name;
  final double price;

  const ProductCard({
    super.key,
    required this.name,
    required this.price,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Column(
        children: [
          Text(name),
          Text('$price VNĐ'),
        ],
      ),
    );
  }
}

Sử dụng:

ProductCard(
  name: 'Nike Air Max',
  price: 2500000,
)

Ở đây ProductCard không tự thay đổi name hoặc price.

Nếu dữ liệu thay đổi, Widget cha có thể truyền dữ liệu mới xuống.

4. Khi nào sử dụng StatelessWidget?

Sử dụng StatelessWidget khi Widget:

• Chỉ hiển thị dữ liệu.

• Không có State nội bộ cần thay đổi.

• Không cần gọi setState().

• Có thể nhận dữ liệu từ Widget cha thông qua constructor.

Một số ví dụ:

Logo
Text
Icon
ProductCard
ProfileHeader
CustomButton
CategoryItem
StaticInformation

5. StatefulWidget

StatefulWidget là Widget được sử dụng khi giao diện cần quản lý dữ liệu có thể thay đổi trong quá trình ứng dụng chạy.

Ví dụ phổ biến:

Counter
Checkbox
Switch
TextField
Form
Loading
Animation
Tab
Like Button
Shopping Cart

Một StatefulWidget  thường gồm hai phần:

StatefulWidget
      ↓
   State<T>
      ↓
    build()

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'),
        ),
      ],
    );
  }
}

6. State

State là dữ liệu có thể thay đổi trong quá trình ứng dụng chạy và sự thay đổi đó có thể ảnh hưởng đến UI.

Ví dụ:

int count = 0;

Ban đầu:

count = 0

Người dùng nhấn nút:

count = 1

UI thay đổi:

0
↓
1

Đây chính là trường hợp cần sử dụng StatefulWidget.

7. setState()

setState() được sử dụng để thông báo cho Flutter rằng State đã thay đổi và UI cần được rebuild.

Ví dụ:

setState(() {
  count++;
});

Quá trình hoạt động:

User click
    ↓
setState()
    ↓
State thay đổi
    ↓
build() được gọi lại
    ↓
UI cập nhật

Nếu chỉ viết:

count++;

Flutter không được thông báo rằng UI cần rebuild theo cơ chế StatefulWidget.

8. Cấu trúc StatefulWidget

Một StatefulWidget có cấu trúc khác StatelessWidget.

StatelessWidget

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Hello');
  }
}

Chỉ cần một class.

StatefulWidget

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    return const Text('Hello');
  }
}

Thông thường sẽ có hai class:

MyWidget
    ↓
State<MyWidget>
    ↓
_MyWidgetState

Class StatefulWidget đại diện cho cấu hình của Widget.

Class State lưu trữ dữ liệu có thể thay đổi và xây dựng UI.

9. So sánh StatelessWidget và StatefulWidget

Tiêu chí StatelessWidget StatefulWidget
Có State nội bộ Không Có
State thay đổi Không quản lý Có
setState() Không Có
Số class cơ bản 1 2
Có build() Có Có
UI có thể thay đổi Có, khi nhận dữ liệu mới Có, dựa trên State
Độ phức tạp Thấp hơn Cao hơn
Sử dụng UI không cần State nội bộ UI cần State thay đổi

10. Một điểm dễ nhầm

StatelessWidget không có nghĩa là UI không bao giờ thay đổi.

Ví dụ:

class UserName extends StatelessWidget {
  final String name;

  const UserName({
    super.key,
    required this.name,
  });

  @override
  Widget build(BuildContext context) {
    return Text(name);
  }
}

Widget này vẫn có thể hiển thị dữ liệu khác:

UserName(name: 'Tien')

sau đó Widget cha rebuild và truyền:

UserName(name: 'Minh')

UI sẽ thay đổi từ:

Tien

thành:

Minh

Nhưng bản thân UserName không tự quản lý State thay đổi.

Vì vậy, cách hiểu chính xác là:

StatelessWidget không có State nội bộ có thể thay đổi.

11. StatefulWidget không có nghĩa là toàn bộ Widget đều thay đổi

Khi State thay đổi:

setState(() {
  count++;
});

Flutter gọi lại build() của State.

Flutter không đơn giản là xóa toàn bộ ứng dụng và vẽ lại từ đầu.

Flutter sử dụng Widget Tree, Element Tree và RenderObject Tree để xác định những phần cần được cập nhật.

12. Ví dụ thực tế

Nút Like

Nếu chỉ hiển thị một nút:

ElevatedButton(
  onPressed: () {},
  child: const Text('Like'),
)

Không nhất thiết cần StatefulWidget.

Nhưng nếu muốn khi nhấn nút thì số Like tăng:

Like: 0
     ↓
User click
     ↓
Like: 1

thì cần State để lưu số Like:

int likes = 0;

và cập nhật:

setState(() {
  likes++;
});

Trường hợp này phù hợp với StatefulWidget.

13. Ví dụ thực tế trong ứng dụng thương mại điện tử

StatelessWidget

ProductCard:

ProductCard
├── Image
├── Product Name
├── Price
└── Rating

Nếu Widget chỉ nhận thông tin sản phẩm và hiển thị chúng thì có thể sử dụng StatelessWidget.

StatefulWidget

QuantitySelector:

Quantity: 1

        [ - ]  1  [ + ]

Khi người dùng nhấn +:

1 → 2 → 3 → 4

Quantity thay đổi liên tục.

Do đó Widget có thể cần State:

int quantity = 1;

và:

setState(() {
  quantity++;
});

14. Lifecycle cơ bản của StatefulWidget

StatefulWidget có vòng đời khác với StatelessWidget.

Một số phương thức quan trọng:

createState()
    ↓
initState()
    ↓
build()
    ↓
setState()
    ↓
build()
    ↓
dispose()

initState()

Được gọi một lần khi State được tạo.

@override
void initState() {
  super.initState();

  // Khởi tạo
}

Thường dùng để:

  • Khởi tạo Controller.
  • Đăng ký Listener.
  • Khởi tạo dữ liệu.
  • Thực hiện các thao tác cần chạy một lần.

dispose()

Được gọi khi State không còn được sử dụng.

@override
void dispose() {
  controller.dispose();

  super.dispose();
}

Thường dùng để giải phóng tài nguyên như:

  • Controller.
  • AnimationController.
  • StreamSubscription.
  • Listener.

15. Quy tắc lựa chọn

Có thể sử dụng quy tắc đơn giản:

Widget có State nội bộ thay đổi?
        ↓
       Không
        ↓
StatelessWidget

Nếu:

Widget có State nội bộ thay đổi?
        ↓
       Có
        ↓
StatefulWidget

Ví dụ:

Text
→ StatelessWidget

ProductCard
→ Thường là StatelessWidget

Counter
→ StatefulWidget

Checkbox
→ StatefulWidget

Switch
→ StatefulWidget

Form
→ Thường cần StatefulWidget hoặc cơ chế quản lý State khác

16. StatelessWidget và StatefulWidget trong kiến trúc ứng dụng

Trong ứng dụng thực tế, không phải mọi Widget có dữ liệu thay đổi đều nhất thiết phải tự quản lý State bằng StatefulWidget.

Ví dụ ứng dụng lớn có thể sử dụng:

Provider
Riverpod
Bloc
Cubit
GetX

để quản lý State bên ngoài Widget.

Khi đó nhiều Widget vẫn có thể là StatelessWidget nhưng UI vẫn thay đổi khi State Management cung cấp dữ liệu mới.

Vì vậy:

StatefulWidget là cách quản lý State cục bộ cơ bản của Flutter, không phải cách duy nhất để quản lý State trong ứng dụng.

17. Tóm tắt

StatelessWidget
│
├── Không quản lý State nội bộ
├── Không dùng setState()
├── Đơn giản
├── Phù hợp với UI không cần State nội bộ
└── Có thể nhận dữ liệu từ Widget cha


StatefulWidget
│
├── Có State
├── State có thể thay đổi
├── Có State class
├── Có thể sử dụng setState()
├── build() được gọi lại khi State thay đổi
└── Phù hợp với UI có trạng thái thay đổi

Cách nhớ nhanh

StatelessWidget = UI không có State nội bộ cần thay đổi.

StatefulWidget = UI có State nội bộ thay đổi trong quá trình chạy.

Ví dụ dễ nhớ nhất:

Text "Hello"
→ StatelessWidget

Counter 0 → 1 → 2 → 3
→ StatefulWidget

Bài viết khác

Language Switching trong Flutter

        Trong quá trình phát triển ứng dụng di động, khả năng hỗ trợ nhiều ngôn ngữ ngày càng trở nên quan trọng. Một ứng dụng chỉ sử dụng duy nhất một ngôn ngữ có thể gặp khó khăn khi tiếp cận người dùng ở các quốc gia và khu vực khác […]

UTC & Local Time Conversion

Trong thời đại toàn cầu hóa, con người thường xuyên làm việc, học tập và giao tiếp với những người ở các quốc gia khác nhau. Một cuộc họp trực tuyến có thể được tổ chức tại Việt Nam, nhưng người tham gia lại đang ở Hoa Kỳ, Anh, Nhật Bản hoặc Australia. Vì vậy, […]

Logging / Monitoring / CI/CD

1. Logging / Monitoring / CI/CD là gì? Trong quá trình phát triển phần mềm, việc viết code để ứng dụng chạy được chỉ là một phần của công việc. Khi đưa ứng dụng vào sử dụng thực tế, lập trình viên còn phải quan tâm đến nhiều vấn đề khác như ứng dụng có […]

Docker

1. Docker là gì? Docker là một nền tảng được sử dụng để đóng gói, triển khai và chạy ứng dụng trong các môi trường độc lập, được gọi là Container. Docker giúp lập trình viên đưa mã nguồn, thư viện, các gói phụ thuộc và những thành phần cần thiết để chạy ứng dụng […]

Unit Testing và Integration Testing

1. Unit Testing và Integration Testing là gì? Unit Testing và Integration Testing là hai phương pháp kiểm thử phần mềm thường được sử dụng trong quá trình phát triển ứng dụng. Mục đích của hai phương pháp này là kiểm tra xem chương trình có hoạt động đúng theo yêu cầu hay không, đồng […]

Message Broker

1. Message Broker là gì? Message Broker là một thành phần trung gian được sử dụng để tiếp nhận, quản lý và chuyển tiếp thông điệp giữa các ứng dụng hoặc các thành phần trong một hệ thống phần mềm. Công nghệ này thường xuất hiện trong những hệ thống Backend, kiến trúc Microservices và […]

Leave a Reply

Your email address will not be published. Required fields are marked *