昨天將設計藍圖化為實際可運作的 UI,並透過假資料模擬了 AI 聊天編輯器的核心功能,今天,將迎來另一個關鍵時刻:串接真實的 AI API。
與假資料的無痛串接不同,真實的網路請求充滿了不確定性,需要考慮:
因此,今天的文章將聚焦於如何將 FakeChatRepository
替換為 ApiChatRepository
,並為各種可能發生的狀態(如載入中、錯誤、成功)設計對應的 UI 顯示,確保使用者能獲得流暢且穩定的體驗。
還記得 Day 26 設計的 ChatRepository
抽象類別嗎?這個設計現在派上用場了!不需要修改任何 ChatController
或 UI 邏輯,只需要新增一個實作 ChatRepository
的類別,並在 Riverpod 中將其注入即可,萬分感謝前天的自己讓我可以速速完成~
ApiChatRepository
首先,建立一個新的 ApiChatRepository
類別來處理真實的 API 請求。這裡將使用 Dio
套件來發送 HTTP 請求,並處理來自後端的串流回應。
程式碼範例
// repositories/chat_repository.dart
class ApiChatRepository implements ChatRepository {
final Dio _dio;
ApiChatRepository(this._dio);
@override
Stream<ChatMessage> streamReply({
String? sessionId,
required String text,
Trip? itinerary,
}) async* {
final response = await _dio.post(
'/api/ai-chat',
data: {
'itinerary': itinerary?.toJson(),
'text': text,
'sessionId': sessionId,
},
options: Options(responseType: ResponseType.stream),
);
await for (final chunk in response.data.stream) {
yield ChatMessage.fromJson(chunk);
}
}
}
這個 streamReply
方法會發送一個 POST 請求到後端 API,並將 itinerary
和使用者輸入的 text
一併送出。由於後端是以串流方式回傳資料,我們也將 responseType
設定為 stream
,並使用 await for
語法來逐塊處理收到的資料。
chatRepositoryProvider
現在,只需要簡單地修改 chatRepositoryProvider
,讓它從原本的 FakeChatRepository
指向新建立的 ApiChatRepository
。
程式碼範例
// providers/chat_provider.dart
final chatRepositoryProvider = Provider<ChatRepository>((ref) {
// 將假資料替換為真實 API 實作
// return FakeChatRepository();
return ApiChatRepository(ref.read(dioProvider));
});
透過這種抽象化的設計,就可以成功地在不改動任何 UI 程式碼的前提下,完成了資料源的切換。
當串接真實 API 後,必須在 UI 上處理各種狀態,避免畫面無回饋感或無預警崩潰。
AI 回應可能需要幾秒鐘,這段期間 UI 應該顯示一個明確的載入狀態。可以在 ChatController
的 sendText
方法中,在開始網路請求時,為 AI 訊息加上一個 pending
標記。
程式碼範例
// providers/chat_provider.dart
Future<void> sendText(String text) async {
// 1. 添加使用者訊息
// 2. 添加 AI 佔位泡泡,並標記為 pending
final aiPending = ChatMessage(
sender: ChatSender.ai,
timestamp: DateTime.now(),
text: '',
pending: true,
);
state = [...state, aiPending];
// 3. 開始串流,並更新狀態
// ...
}
在 UI 層,可以根據 ChatMessage
中的 pending
標記,在訊息泡泡上顯示一個小的 Loading 動畫。
當網路請求失敗或伺服器回傳錯誤時,需要明確地通知使用者。在 StreamSubscription
的 onError
回呼中,可以將最後一則 AI 訊息的狀態更新為「錯誤」,並顯示一個「重試」按鈕。
程式碼範例
// providers/chat_provider.dart
.listen(
(chunk) {
// 成功時的更新邏輯
},
onError: (e, st) {
// 錯誤時的處理
final lastIndex = state.lastIndexWhere((m) => m.id == aiPending.id);
if (lastIndex == -1) return;
final updated = state[lastIndex].copyWith(
pending: false,
isError: true,
text: '發生錯誤,請稍後再試。', // 提供使用者友善的錯誤訊息
);
final newList = [...state];
newList[lastIndex] = updated;
state = newList;
},
);
在完成了上述的實作後,現在的 AI 聊天編輯器已經是一個能應對真實世界挑戰了。
調整現有行程
點擊刷新按鈕重新送