Trong phát triển ứng dụng di động, khái niệm app state đóng vai trò rất quan trọng, ảnh hưởng trực tiếp đến khả năng phản hồi và tương tác của ứng dụng với người dùng. Để hiểu rõ hơn về app state, bài viết này sẽ giúp bạn nắm vững các khái niệm cơ bản cũng như cách quản lý trạng thái trong ứng dụng Flutter.

1. Mở bài: App State là gì?

App state (trạng thái của ứng dụng) đề cập đến thông tin mà ứng dụng lưu giữ trong suốt vòng đời hoạt động. Nó quyết định cách ứng dụng hiển thị dữ liệu, tương tác với người dùng, và thực hiện các hoạt động khác. Trong Flutter, quản lý app state hiệu quả là yếu tố cốt lõi để đảm bảo ứng dụng chạy mượt mà và cung cấp trải nghiệm tốt nhất cho người dùng.

App state có thể được chia thành hai loại chính:

2. Các loại app state trong Flutter

Ephemeral State (Local State)

Ephemeral state (trạng thái tạm thời) là trạng thái chỉ tồn tại trong một widget cụ thể và không cần được chia sẻ hoặc lưu trữ lâu dài. Trạng thái này có tính chất cục bộ, chỉ liên quan đến các thành phần giao diện nhỏ, như trạng thái của nút bấm hoặc ô nhập văn bản.

Ví dụ:

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  bool isChecked = false;

  @override
  Widget build(BuildContext context) {
    return Checkbox(
      value: isChecked,
      onChanged: (bool? value) {
        setState(() {
          isChecked = value!;
        });
      },
    );
  }
}

Trong ví dụ trên, biến isChecked lưu trạng thái của checkbox. Đây là một dạng ephemeral state vì nó chỉ liên quan đến widget MyWidget và không cần phải chia sẻ với các thành phần khác.

App State (Shared State)

Ngược lại, app state (trạng thái toàn cục) cần được chia sẻ giữa nhiều phần của ứng dụng. Dữ liệu cần đồng bộ hóa ở nhiều nơi khác nhau như trạng thái đăng nhập của người dùng, các cài đặt chung, hoặc dữ liệu từ API. Để quản lý app state, Flutter cung cấp nhiều công cụ và phương pháp khác nhau, phù hợp cho từng trường hợp sử dụng.

Ví dụ: khi người dùng đăng nhập vào ứng dụng, thông tin đăng nhập cần được chia sẻ giữa nhiều widget khác nhau để hiển thị đúng tên người dùng, ảnh đại diện, hoặc các quyền truy cập.

3. Các phương pháp quản lý app state

Flutter cung cấp nhiều cách để quản lý app state, mỗi cách có những ưu và nhược điểm riêng. Dưới đây là một số phương pháp phổ biến:

InheritedWidget

InheritedWidget là cách đơn giản nhất để truyền trạng thái qua cây widget trong Flutter. Nó hoạt động bằng cách cung cấp dữ liệu từ widget cha cho các widget con thông qua phương thức of().

Ví dụ:

class MyInheritedWidget extends InheritedWidget {
  final int counter;

  MyInheritedWidget({
    required this.counter,
    required Widget child,
  }) : super(child: child);

  static MyInheritedWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
  }

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return oldWidget.counter != counter;
  }
}

Trong ví dụ trên, MyInheritedWidget cho phép các widget con truy cập vào giá trị counter mà không cần truyền trực tiếp từ widget cha đến widget con.

Provider

Provider là một gói quản lý trạng thái phổ biến trong Flutter. Nó được xây dựng dựa trên InheritedWidget nhưng đơn giản hóa quá trình cung cấp và sử dụng trạng thái.

Ví dụ:

class Counter extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

// Sử dụng Provider trong ứng dụng
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MyApp(),
    ),
  );
}

Với Provider, trạng thái Counter có thể được truy cập và thay đổi từ bất kỳ widget nào trong ứng dụng.

Bloc (Business Logic Component)

Bloc là một mẫu kiến trúc giúp tách biệt logic nghiệp vụ khỏi giao diện người dùng, giúp ứng dụng dễ dàng bảo trì và mở rộng. Bloc quản lý trạng thái thông qua các sự kiện (events) và luồng dữ liệu (streams).

Ví dụ:

class CounterBloc {
  int _counter = 0;
  final _counterController = StreamController<int>();

  Stream<int> get counterStream => _counterController.stream;

  void increment() {
    _counter++;
    _counterController.sink.add(_counter);
  }

  void dispose() {
    _counterController.close();
  }
}

Bloc giúp quản lý trạng thái thông qua các event, và các widget chỉ cần lắng nghe các thay đổi trạng thái từ các stream.

Riverpod

Riverpod là một gói quản lý trạng thái hiện đại, cung cấp khả năng kiểm soát và an toàn cao hơn so với Provider. Nó giúp giảm thiểu các vấn đề về kết hợp và quản lý trạng thái phức tạp.

Ví dụ:

final counterProvider = StateProvider<int>((ref) => 0);

class MyHomePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('Counter Example'),
      ),
      body: Center(
        child: Text('$count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterProvider.notifier).state++;
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

Với Riverpod, bạn có thể dễ dàng quản lý trạng thái toàn cục mà không phải lo lắng về các lỗi liên quan đến bộ nhớ hoặc kết hợp trạng thái.

4. Kết bài: Tối ưu hóa quản lý app state

Quản lý app state là một yếu tố quan trọng giúp ứng dụng Flutter của bạn vận hành mượt mà và hiệu quả. Tùy thuộc vào quy mô và độ phức tạp của ứng dụng, bạn có thể chọn phương pháp quản lý trạng thái phù hợp. Từ InheritedWidget cho các trường hợp đơn giản đến BlocRiverpod cho các ứng dụng lớn, việc lựa chọn công cụ đúng đắn sẽ giúp bạn xây dựng và duy trì ứng dụng dễ dàng hơn.