LlmChatView dispose() does not cancel pending streaming response, causing setState on disposed widget

Open Beginner friendly
#186 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
1/5
Estimated time
Under an hour
Newbie friendliness
70/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
dart, flutter
Domain
mobile-dev

Research direction

Start in llm_chat_view.dart at _LlmChatViewState.dispose() and compare its lifecycle handling with _onCancelMessage(), which already uses LlmResponse.cancel(). Reproduce navigation away during streaming, then verify that the pending response no longer reaches the update callback or triggers setState after disposal.

Written by the indexing model from the issue text.

Description

waiting for customer response

Description

_LlmChatViewState.dispose() does not cancel _pendingPromptResponse, causing a Null check operator used on a null value crash when the user navigates away from the chat while the AI is still streaming a response.

Steps to Reproduce

  1. Open a chat with an LLM using LlmChatView
  2. Send a message that triggers a streaming response
  3. Navigate away (pop the page) before the response finishes streaming

Error

_TypeError: Null check operator used on a null value

Stack trace:
State.setState (package:flutter/src/widgets/framework.dart:1219)
_LlmChatViewState._onSendMessage.<fn> (package:flutter_ai_toolkit/src/views/llm_chat_view/llm_chat_view.dart:257)
FirebaseProvider._sendMessageStream (package:flutter_ai_toolkit/src/providers/implementations/firebase_provider.dart)

Root Cause

In llm_chat_view.dart, the _onSendMessage method creates an LlmResponse with an onUpdate callback that calls setState:

// line 253-258
_pendingPromptResponse = LlmResponse(
  stream: sendMessageStream(prompt, attachments: attachments),
  onUpdate: (_) => setState(() {}),
  onDone: _onPromptDone,
);

But dispose() (line 174) does not cancel this response:

@override
void dispose() {
  super.dispose();
  widget.viewModel.provider.removeListener(_onHistoryChanged);
}

When the widget is disposed while streaming is active, the LlmResponse subscription continues to receive data and calls setState on the disposed state, crashing at _element! inside State.setState.

Suggested Fix

Cancel the pending response in dispose():

@override
void dispose() {
  _pendingPromptResponse?.cancel();
  super.dispose();
  widget.viewModel.provider.removeListener(_onHistoryChanged);
}

The cancel() method already exists on LlmResponse and is used by _onCancelMessage() — it just needs to also be called during disposal.

Impact

  • 588 occurrences across 124 users in our production app (CraftNote)
  • Affects any app using LlmChatView where users can navigate away during streaming
  • Package version: flutter_ai_toolkit 1.0.0

Environment

  • Flutter AI Toolkit: 1.0.0
  • Flutter: 3.27+
  • Platform: iOS (but likely affects all platforms)
Dominant language
Dart
Stars
287
Forks
94
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from flutter/ai

All issues in flutter/ai

Similar issues

More Dart issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.