LlmChatView dispose() does not cancel pending streaming response, causing setState on disposed widget
評価
- 難易度
- 1/5
- 見積もり時間
- 1時間未満
- 初心者へのやさしさ
- 70/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 静か
- 領域
- mobile-dev
調査の方向性
llm_chat_view.dart の _LlmChatViewState.dispose() から始め、すでに LlmResponse.cancel() を使用している _onCancelMessage() とライフサイクル処理を比較します。ストリーミング中に画面遷移して離れる状況を再現し、その後、保留中のレスポンスが更新コールバックに到達せず、dispose 後に setState をトリガーしないことを確認します。
索引モデルが issue の本文から書いたものです。
説明
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
- Open a chat with an LLM using
LlmChatView - Send a message that triggers a streaming response
- 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
LlmChatViewwhere 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)
- 主要言語
- Dart
- スター
- 292
- フォーク
- 93
- PR マージ指標
- 30日以内にマージされた PR はありません
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
flutter/ai のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 64/100
-
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
-
Loan appオープン
難易度 5/5 1週間以上 初心者へのやさしさ 15/100
-
IDX -> Antigravitiy再び着手できるかも @rodydavis が 147 日前に担当しましたが、オープン中のプルリクエストはありません。 オープン
-
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
linagora/tmail-flutter#5034 · コメント 1 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
fable-compiler/Fable#5157 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
bannzai/medicalarm#430 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
fossasia/badgemagic-app#2016 ·
メンテナーはふだん 1 日以内に返信
-
area-vm
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
dart-lang/sdk#64727 · コメント 1 件 ·
メンテナーはふだん 4 日以内に返信