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

Flutter Riverpod页面返回后调用ref.read报已释放异常求助

解决CallScreen页面返回后调用ref.read抛出已释放异常的问题

问题原因

当你pop掉CallScreen页面时,对应的_CallScreenState会被销毁,但如果此时sendMessage内的异步请求(ChatGptApi.getChatGptAnswer)仍在运行,请求完成后旧的state实例会尝试调用已被销毁的ref,触发"Bad state: Cannot use 'ref' after the widget was disposed"异常。你仅在sendMessage开头检查了一次mounted,异步操作完成后未重复验证;且再次返回页面时会创建新的state实例,旧异步任务持有的是旧state的ref,自然无法正常使用。


解决方案

方法一:异步操作全程检查mounted状态

在每次调用ref.read、setState前都验证当前state是否仍处于活跃状态:

void sendMessage(String text) async {
  if (!mounted) return;
  
  // 添加用户消息
  addMessageToList(text: text, received: false, isLoading: false);
  setState(() {
    isLoadingSendMessage = true;
  });

  final oldMessages = ref.read(messagesProvider);
  final assistantMessage = addMessageToList(
    text: '', received: true, isLoading: isLoadingSendMessage);
  
  // 异步调用GPT
  final answer = await ChatGptApi.getChatGptAnswer(text, oldMessages);
  
  // 异步完成后先检查mounted
  if (!mounted) return;
  
  setState(() {
    isLoadingSendMessage = false;
  });
  
  if (answer.trim().isNotEmpty) {
    bool error = answer.trim() == 'An internal error occurred';
    // 更新消息前再次检查
    if (!mounted) return;
    updateMessageInList(
      messageModel: assistantMessage,
      text: answer.trim(),
      isLoading: isLoadingSendMessage,
      error: error);
    systemSpeak(answer);
  }
}

同时在操作ref的工具方法中也添加检查:

MessageModel addMessageToList(
    {required String text, required bool received, required bool isLoading}) {
  if (!mounted) return MessageModel(text: text, received: received, isLoading: isLoading);
  final messageModel =
     MessageModel(text: text, received: received, isLoading: isLoading);

  ref.read(messagesProvider.notifier).addMessage(messageModel);

  return messageModel;
}

void updateMessageInList(
   {required MessageModel messageModel,
   required String text,
   required bool isLoading,
   required bool error}) {
  if (!mounted) return;
  ref
     .read(messagesProvider.notifier)
    .updateMessage(messageModel, text, isLoading, error);
}

方法二:用CancelableOperation取消异步任务

在页面销毁时主动取消未完成的异步请求,避免旧任务继续执行:

class _CallScreenState extends ConsumerState<CallScreen> {
  CancelableOperation? _gptRequestOperation;

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

  void sendMessage(String text) async {
    if (!mounted) return;
    
    addMessageToList(text: text, received: false, isLoading: false);
    setState(() {
      isLoadingSendMessage = true;
    });

    final oldMessages = ref.read(messagesProvider);
    final assistantMessage = addMessageToList(
      text: '', received: true, isLoading: isLoadingSendMessage);
    
    // 将异步请求包装为可取消操作
    _gptRequestOperation = CancelableOperation.fromFuture(
      ChatGptApi.getChatGptAnswer(text, oldMessages),
    );

    try {
      final answer = await _gptRequestOperation?.value;
      if (!mounted || answer == null) return;
      
      setState(() {
        isLoadingSendMessage = false;
      });
      
      if (answer.trim().isNotEmpty) {
        bool error = answer.trim() == 'An internal error occurred';
        if (!mounted) return;
        updateMessageInList(
          messageModel: assistantMessage,
          text: answer.trim(),
          isLoading: isLoadingSendMessage,
          error: error);
        systemSpeak(answer);
      }
    } on OperationCanceledException {
      // 请求被取消,无需额外处理
    } finally {
      _gptRequestOperation = null;
    }
  }
}

方法三:将异步逻辑移至Provider处理

把GPT请求和消息更新逻辑迁移到StateNotifier中,组件仅负责触发请求和监听结果,彻底避开组件生命周期与异步任务的冲突:

// messages_provider.dart
final messagesProvider = StateNotifierProvider<MessagesNotifier, List<MessageModel>>((ref) {
  return MessagesNotifier();
});

class MessagesNotifier extends StateNotifier<List<MessageModel>> {
  MessagesNotifier() : super([]);
  
  Future<void> sendUserMessage(String text) async {
    // 添加用户消息
    final userMessage = MessageModel(text: text, received: false, isLoading: false);
    state = [...state, userMessage];
    
    // 添加加载中消息
    final loadingMessage = MessageModel(text: '', received: true, isLoading: true);
    state = [...state, loadingMessage];
    
    try {
      final answer = await ChatGptApi.getChatGptAnswer(text, state);
      bool error = answer.trim() == 'An internal error occurred';
      // 更新加载中消息为结果
      final updatedMessage = loadingMessage.copyWith(
        text: answer.trim(),
        isLoading: false,
        error: error,
      );
      state = state.map((m) => m == loadingMessage ? updatedMessage : m).toList();
      
      // 语音播报逻辑可在此处理,或通过回调通知组件
    } catch (e) {
      final errorMessage = loadingMessage.copyWith(
        text: '请求失败',
        isLoading: false,
        error: true,
      );
      state = state.map((m) => m == loadingMessage ? errorMessage : m).toList();
    }
  }
}

然后在CallScreen中简化调用:

void sendMessage(String text) {
  if (!mounted) return;
  ref.read(messagesProvider.notifier).sendUserMessage(text);
}

内容的提问来源于stack exchange,提问作者mouhamed menasria

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:33:14