3.4 状态同步与生命周期管理

良好的状态管理不仅关注数据流,还需要正确处理控制器的初始化、销毁与生命周期事件,避免内存泄漏。


一、控制器封装(Controller)

1.1 通用控制器基类

dart 复制代码
// 定义通用控制器接口
abstract class BaseController {
  void onInit();
  void onDispose();
}

// 带加载/错误状态的控制器基类
abstract class AsyncController<T> extends ChangeNotifier {
  T? _data;
  String? _errorMessage;
  bool _isLoading = false;

  T? get data => _data;
  String? get errorMessage => _errorMessage;
  bool get isLoading => _isLoading;
  bool get hasError => _errorMessage != null;
  bool get hasData => _data != null;

  Future<void> load() async {
    _isLoading = true;
    _errorMessage = null;
    notifyListeners();

    try {
      _data = await fetchData();
    } catch (e) {
      _errorMessage = e.toString();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }

  // 子类实现具体数据获取逻辑
  Future<T> fetchData();

  void refresh() => load();
}

1.2 业务控制器实现

dart 复制代码
class ProductListController extends AsyncController<List<Product>> {
  final ProductRepository _repository;
  String _searchQuery = '';
  ProductCategory? _selectedCategory;

  ProductListController(this._repository);

  String get searchQuery => _searchQuery;
  ProductCategory? get selectedCategory => _selectedCategory;

  @override
  Future<List<Product>> fetchData() async {
    return await _repository.fetchProducts(
      query: _searchQuery,
      category: _selectedCategory,
    );
  }

  void updateSearchQuery(String query) {
    _searchQuery = query;
    // 防抖:延迟500ms再请求
    _debounce?.cancel();
    _debounce = Timer(const Duration(milliseconds: 500), load);
  }

  void filterByCategory(ProductCategory? category) {
    _selectedCategory = category;
    notifyListeners();
    load();
  }

  Timer? _debounce;

  @override
  void dispose() {
    _debounce?.cancel();
    super.dispose();
  }
}

二、生命周期事件(GetX 风格)

2.1 GetxController 生命周期

dart 复制代码
class OrderController extends GetxController {
  final RxList<Order> orders = <Order>[].obs;
  StreamSubscription? _orderSubscription;
  Timer? _pollingTimer;

  // ① 控制器首次注入时调用(类似 initState)
  @override
  void onInit() {
    super.onInit();
    _loadInitialOrders();
    _startOrderTracking();
    _setupEverListeners();
  }

  // ② 在 Widget 树中渲染完成后调用
  @override
  void onReady() {
    super.onReady();
    // 适合做需要 UI 就绪后才能执行的操作
    // 如:展示欢迎弹窗
  }

  // ③ 控制器销毁时调用(类似 dispose)
  @override
  void onClose() {
    _orderSubscription?.cancel();
    _pollingTimer?.cancel();
    super.onClose();
  }

  void _setupEverListeners() {
    // ever:每次变化都触发
    ever(orders, (_) => _updateBadgeCount());

    // once:只触发一次
    once(orders, (_) => _showFirstOrderHint());

    // debounce:防抖
    debounce(orders, (_) => _saveToLocal(), time: const Duration(seconds: 1));

    // interval:节流
    interval(orders, (_) => _syncToServer(), time: const Duration(seconds: 3));
  }

  void _startOrderTracking() {
    _orderSubscription = OrderService.orderStream.listen(
      (order) => orders.add(order),
    );
  }

  void _loadInitialOrders() async {
    final data = await OrderRepository.fetchAll();
    orders.assignAll(data);
  }

  void _updateBadgeCount() {
    // 更新应用角标
  }
}

2.2 多控制器协作

dart 复制代码
class CheckoutController extends GetxController {
  // 依赖其他控制器
  final cartController = Get.find<CartController>();
  final userController = Get.find<UserController>();
  final paymentController = Get.find<PaymentController>();

  final RxBool isProcessing = false.obs;
  final Rx<OrderResult?> orderResult = Rx<OrderResult?>(null);

  Future<void> placeOrder() async {
    if (cartController.items.isEmpty) {
      Get.snackbar('提示', '购物车为空');
      return;
    }

    isProcessing.value = true;

    try {
      final result = await OrderService.createOrder(
        userId: userController.currentUser.value!.id,
        items: cartController.items,
        paymentMethod: paymentController.selectedMethod.value,
      );

      orderResult.value = result;
      cartController.clear(); // 清空购物车
      Get.toNamed('/order-success', arguments: result);
    } catch (e) {
      Get.snackbar('下单失败', e.toString());
    } finally {
      isProcessing.value = false;
    }
  }
}

三、Stream 状态同步

3.1 Riverpod + Stream

dart 复制代码
// 实时监听订单状态
@riverpod
Stream<List<Order>> activeOrders(ActiveOrdersRef ref) {
  final userId = ref.watch(currentUserProvider).value?.id;
  if (userId == null) return const Stream.empty();

  // 使用 ref.onDispose 清理资源
  final subscription = OrderService.watchOrders(userId);
  ref.onDispose(() => subscription.cancel()); // ⚠️ 自动清理

  return subscription.stream;
}

// 在 Widget 中使用
Consumer(
  builder: (context, ref, _) {
    final ordersAsync = ref.watch(activeOrdersProvider);
    return ordersAsync.when(
      data: (orders) => OrderList(orders: orders),
      loading: () => const CircularProgressIndicator(),
      error: (e, _) => Text('Error: $e'),
    );
  },
)

3.2 状态同步常见问题

dart 复制代码
// ❌ 问题:在已销毁的 State 上调用 setState
class _MyState extends State<MyWidget> {
  Future<void> _loadData() async {
    final data = await fetchData();
    setState(() => _data = data); // 如果此时 Widget 已销毁,会报错
  }
}

// ✅ 解决:使用 mounted 检查
class _MyState extends State<MyWidget> {
  Future<void> _loadData() async {
    final data = await fetchData();
    if (!mounted) return; // ⚠️ 检查是否还在 Widget 树中
    setState(() => _data = data);
  }
}

// ✅ 更优:使用 Riverpod(自动处理生命周期)
@riverpod
Future<Data> loadData(LoadDataRef ref) async {
  // Riverpod 自动处理取消,无需 mounted 检查
  return await fetchData();
}

四、生命周期与 Widget 绑定

4.1 WidgetsBindingObserver

监听应用级生命周期事件:

dart 复制代码
class _HomePageState extends State<HomePage>
    with WidgetsBindingObserver {

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        // 应用回到前台:刷新数据、恢复播放
        _refreshData();
        break;
      case AppLifecycleState.paused:
        // 应用进入后台:保存草稿、暂停播放
        _saveDraft();
        break;
      case AppLifecycleState.detached:
        // 应用即将关闭
        break;
      case AppLifecycleState.inactive:
        // 应用非活跃(来电、切换等)
        break;
      case AppLifecycleState.hidden:
        // 应用不可见(Flutter 3.13+)
        break;
    }
  }

  @override
  void didChangeLocales(List<Locale>? locales) {
    // 系统语言变化
  }

  @override
  void didChangePlatformBrightness() {
    // 系统主题变化(深色/浅色)
  }
}

小结

知识点 核心要点
控制器封装 将业务逻辑从 Widget 中分离,提高可测试性
onInit / onClose GetX 控制器生命周期,替代 initState/dispose
mounted 检查 异步操作后必须检查,防止 setState 报错
Stream 订阅清理 在 dispose 中取消订阅,防止内存泄漏
WidgetsBindingObserver 监听应用前后台切换等系统事件

👉 下一章:四、路由与导航

相关推荐
航Hang*2 小时前
Windows Server 配置与管理——第7章:配置DNS服务器
运维·服务器·网络·windows·安全·虚拟化
xixixi777772 小时前
通信产业的“全维度加速”:从5G-A商用、6G冲刺到卫星互联网密集组网
大数据·网络·人工智能·ai·多模型
砖厂小工2 小时前
Android 开发的 AI coding 与 AI debugging
android·ai编程
peakmain92 小时前
CmComposeUI —— 基于 Kotlin Multiplatform Compose 的 UI 组件库
android
studyForMokey2 小时前
【Android面试】Glide专题
android·面试·glide
@insist1232 小时前
网络工程师-网络安全核心加密技术体系:对称 / 非对称加密、数字签名与证书全解析
网络·安全·web安全·网络工程师·软考·软件水平考试
盐真卿2 小时前
华为数通 | VRRP负载分担与网关冗余实验:主备切换+流量分流,企业高可用网络实战
网络·华为
m0_738120722 小时前
渗透知识ctfshow——Web应用安全与防护(三)
android·前端·安全
y = xⁿ2 小时前
【保姆级 :图解MySQL 执行全链路讲解】主键索引扫描,全局扫描,索引下推还是分不清楚?这一篇就够啦
android·mysql