You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中initState/按钮点击用ref.read如何避免autoDisposed自动销毁?

修复AutoDisposed FutureProvider在异步操作完成前被销毁的问题

问题根源

autoDisposed类型的Provider会在没有任何组件或订阅者监听它时自动销毁。你用ref.read获取Provider值时,只是一次性读取当前状态,不会建立持久的订阅关系——这就导致调用ref.read后,Provider立刻失去了监听者,直接触发onDispose,异步操作还没完成就被中断了。

两种修复方案

方案1:用ref.listen替代ref.read建立订阅

ref.listen会持续监听Provider的状态变化,只要订阅存在,autoDisposed的Provider就会保持存活。你可以在异步操作完成后手动关闭订阅,避免不必要的资源占用。

完整修复代码

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    const ProviderScope(
      child: MaterialApp(
        home: Sample(),
      ),
    ),
  );
}

final dataProvider =
    FutureProvider.autoDispose.family<int, int>((ref, base) async {
  ref.onDispose(() {
    print('onDispose');
  });
  await Future.delayed(const Duration(seconds: 3));
  return base * 2; // 实际场景替换为服务器请求逻辑
});

class Sample extends ConsumerStatefulWidget {
  const Sample({super.key});

  @override
  ConsumerState<Sample> createState() => _SampleState();
}

class _SampleState extends ConsumerState<Sample> {
  int _sampleResult = 0;

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

    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      // 用listen建立订阅,保持Provider存活
      final subscription = ref.listen(dataProvider(2), (previous, next) {
        next.when(
          data: (value) {
            setState(() {
              _sampleResult = value;
            });
            print(value);
            // 数据获取完成后关闭订阅
            subscription.close();
          },
          error: (error, stackTrace) {
            print(error);
            subscription.close();
          },
          loading: () {},
        );
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('$_sampleResult'),
          const SizedBox(width: 20),
          TextButton(
            onPressed: () {
              // 按钮点击时同样用listen处理
              final subscription = ref.listen(dataProvider(_sampleResult), (previous, next) {
                next.when(
                  data: (value) {
                    setState(() {
                      _sampleResult = value;
                    });
                    subscription.close();
                  },
                  error: (error, stackTrace) {
                    setState(() {
                      _sampleResult -= 10;
                    });
                    subscription.close();
                  },
                  loading: () {},
                );
              });
            },
            child: const Text('test button'),
          )
        ],
      ),
    );
  }
}

方案2:在Provider内部添加临时保活逻辑

如果不想手动管理订阅,可以在Provider中使用ref.keepAlive(),让Provider在异步操作执行期间保持存活,完成后再允许销毁。

修改后的Provider代码

final dataProvider = FutureProvider.autoDispose.family<int, int>((ref, base) async {
  // 调用keepAlive获取保活链接
  final keepAliveLink = ref.keepAlive();
  ref.onDispose(() {
    print('onDispose');
  });
  try {
    await Future.delayed(const Duration(seconds: 3));
    return base * 2;
  } finally {
    // 异步操作完成后,关闭保活链接,允许Provider被自动销毁
    keepAliveLink.close();
  }
});

这种方式无需修改页面中的监听逻辑,Provider会自动处理存活周期——只要异步操作在执行,它就不会被销毁;操作完成后,若没有新的监听者,就会自动销毁。

方案对比

  • 方案1适合需要精确控制订阅生命周期的场景,比如单次获取数据后就不再需要监听
  • 方案2更简洁,无需手动管理订阅,适合希望Provider自动处理存活状态的场景

内容的提问来源于stack exchange,提问作者野瀬田 裕樹

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 08:45:06