integrate-genui-firebase について
既存の Flutter アプリに genui を追加し、Firebase AI Logic を使う基本的な会話エージェントを接続するスキルです。依存関係、コントローラー、イベント処理、画面表示を案内し、状態を持つウィジェットと入力 UI は事前確認を求めます。
主な機能
- 01Firebase 初期化と依存関係の確認
- 02genui の追加とログ設定
- 03会話・サーフェスコントローラーの接続
- 04Firebase AI Logic 経由のメッセージ送信
- 05会話イベント処理とサーフェス表示
- 06状態ウィジェットと入力 UI の事前確認
活用シーン
- 01Flutter のチャット画面への GenUI 組み込み
- 02チャットセッションと Firebase AI Logic の接続
- 03アプリ内でのエージェント提供 UI の表示
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ flutter/ genui --skill integrate-genui-firebaseインストール後、Claude Code で/integrate-genui-firebaseを入力して読み込まれたか確認します。
詳細
GenUI を Firebase AI Logic と統合する
目標
genui パッケージを Flutter アプリに正常に統合し、Firebase AI Logic を使用する基本的な会話エージェントを設定します。このスキルでは、Firebase AI Logic の設定がプロジェクトですでに完了し、動作していることを前提とします。
手順
genui を統合して簡単な会話を開始するよう依頼された場合は、次の手順に従います。
-
Firebase の設定を確認する:
firebase_coreとfirebase_aiがpubspec.yamlに含まれていることを確認します。main()関数内でFirebase.initializeAppが呼び出されていることを確認します。WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); -
GenUI パッケージを追加する:
pubspec.yamlの依存関係にgenuiを追加します。 -
必要なライブラリをインポートする:
TextPartが他のパッケージと競合しないようにgenuiをインポート時に非表示にし、その後エイリアスを付けて再度インポートします。import 'package:genui/genui.dart' hide TextPart; import 'package:genui/genui.dart' as genui; -
基本的なロギングを設定する:
main()関数の冒頭で GenUI のロギングを設定します。configureLogging( logCallback: (level, msg) => debugPrint('GenUI $level: $msg'), ); -
モデルとチャットセッションを作成する: 生成モデルを初期化し、チャットセッションを開始します。
final model = FirebaseAI.googleAI().generativeModel( model: 'gemini-3-flash-preview', ); final _chatSession = model.startChat(); -
対象の StatefulWidget を特定する: 不明な場合は処理を中断してユーザーに確認してください: この統合では、GenUI コントローラー(
SurfaceController、A2uiTransportAdapter、Conversation)への参照を保持するStatefulWidgetが必要です。アプリケーションで使用するStatefulWidgetを特定してください。この状態を保持するウィジェットがわからない場合は、先に進む前にユーザーに確認してください。 -
State 内で GenUI コントローラーを接続する: 特定した
Stateクラス内で、SurfaceController、A2uiTransportAdapter、Conversationのインスタンスを作成します。final catalog = BasicCatalogItems.asCatalog(); // Optionally inject custom CatalogItems final _controller = SurfaceController(catalogs: [catalog]); final _transport = A2uiTransportAdapter(onSend: _sendAndReceive); final _conversation = Conversation( controller: _controller, transport: _transport, ); -
_sendAndReceiveメソッドを実装する: トランスポートアダプターからメッセージを受け取り、Firebase に送信し、AI の応答をトランスポートに返すメソッドを作成します。Future<void> _sendAndReceive(ChatMessage msg) async { final buffer = StringBuffer(); for (final part in msg.parts) { if (part.isUiInteractionPart) { buffer.write(part.asUiInteractionPart!.interaction); } else if (part is genui.TextPart) { buffer.write(part.text); } } if (buffer.isEmpty) return; final text = buffer.toString(); final response = await _chatSession.sendMessage(Content.text(text)); if (response.text?.isNotEmpty ?? false) { _transport.addChunk(response.text!); } } -
Conversation イベントをリッスンする: DartDoc コメントで必要な動作を説明しながら、各イベントタイプに対応するスタブメソッドを
Stateクラスに作成します。インターフェースの設計に応じて、エージェントから届く新しいサーフェスやテキストの処理方法は異なります。たとえば、会話型インターフェースでは、すべてをリストに追加してListViewに表示できます。一方、ヘッダーやフッターなど特定の場所に UI コンポーネントを表示するインターフェースでは、どのサーフェスをどこに表示するかを判断するために、システム指示でエージェントに指定したサーフェス ID を利用できます。/// Updates state to include the new [surfaceId] so a new `Surface` widget can be built. void _onSurfaceAdded(String surfaceId) { // TODO: Implement state update to add surfaceId } /// Updates state to remove the [surfaceId] so its `Surface` widget is no longer built. void _onSurfaceRemoved(String surfaceId) { // TODO: Implement state update to remove surfaceId } /// Handles displaying raw text content received from the AI to the user. void _onContentReceived(String text) { // TODO: Implement displaying the received text } /// Handles errors that occur during the conversation appropriately. void _onError(Object error) { // TODO: Implement error handling }_conversation.eventsをサブスクライブして UI サーフェスやチャットメッセージの到着を追跡し、適切なスタブメソッドに振り分けます。_conversation.events.listen((event) { switch (event) { case ConversationSurfaceAdded added: _onSurfaceAdded(added.surfaceId); case ConversationSurfaceRemoved removed: _onSurfaceRemoved(removed.surfaceId); case ConversationContentReceived content: _onContentReceived(content.text); case ConversationError error: _onError(error.error); default: } }); -
システムプロンプトを初期化する:
PromptBuilderを使って AI に基本的な指示を与え、システムメッセージとして送信します。final promptBuilder = PromptBuilder.chat( catalog: catalog, instructions: 'You are a helpful assistant. Respond to messages in a chatty way.', ); _conversation.sendRequest(ChatMessage.system(promptBuilder.systemPrompt)); -
サーフェスを表示する: Flutter の
build()メソッドで、手順 9 で収集したsurfaceIdsを使用し、GenUI ウィジェットを描画する場所にSurfaceウィジェットを配置します。Surface(surfaceContext: _controller.contextFor(surfaceId)); -
ユーザー入力の設定について確認する: 処理を中断してユーザーに確認してください: ユーザー入力にどの UI 要素を使用するか、ユーザーに確認してください。
TextFieldとElevatedButtonは適切なデフォルトの選択肢ですが、ユーザーが明確に希望を伝えない限り、それらのウィジェットを使うものと決めつけないでください。
制約
- ユーザー入力に使う UI 要素を決めつけないでください。手順 12 を参照してください。
- ウィジェットの
dispose()メソッド内で GenUI コントローラー(_transport.dispose()、_controller.dispose())を適切にクリーンアップしてください。