flutter / flutter/ai

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

Open
#186 1 comment 0 reactions 0 assignees View on GitHub
waiting for customer response
Dominant language
Dart
Stars
286
Forks
94
PR merge metrics
No merged PRs in 30d

Description

## 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. (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`:

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

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

```dart
@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()`:

```dart
@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)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.