Flutter 官方 LLM 動態 UI 庫 flutter_genui 發布,讓 App UI 自己生成 UI

Image

今日,Flutter 官方正式發布了它們關於 AI 大模型的 package 項目:genui,它是一個非常有趣和前沿的探索類型的項目,它的目標是幫助開發者構建由生成式 AI 模型驅動的動態、對話式用戶界面

也就是它與傳統 App 中「寫死的」靜態界面不同,是一個可以基於 AI 模型,支持由 AI 根據與用戶的實時對話動態生成 UI 的 SDK 。

Image

當然,它並不是一個完全 Free 的動態 UI 項目,雖然看起來它是動態的,甚至可以用來做熱更新,但是實際上也是存在限制條件。

首先它的作用是:應用能夠實時渲染由 AI 返回的結構化數據,也就是 JSON 數據,所以實際上 flutter_genui 是一個基於文本描述,然後經過 AI 返回結構化數據進行渲染驅動的過程:

在 SDK 裡,主要是通過一個名為 UiAgent 的介面,它負責管理與 AI 之間的互動循環,簡化了開發流程

而對於 UI,開發者可以定義一個 AI "允許使用" 的 Flutter Widget 詞彙表 Catalog,AI 將基於這個 Catalog 來構建 UI,比如官方的 Demo 就提供了類似的 catalog 目錄來限制 UI 風格:

Image

具體來說,就是定義好的各種 CoreCatalogItems 透過 GenUiManagercatalog 配置,讓 AI 知道應該用哪些組件來生成需要的 UI:

Image Image

這其中 CatalogItem 就像是每個 Widget 的「角色卡」,它核心規定了三件重要的事情:

  • name: Widget 的名字(例如 "Column", "Text", "ElevatedButton"),這是給 AI 看的,AI 會透過這個名字來指定使用哪個 Widget
  • dataSchema: 一個 Schema 對象,它用 JSON Schema 的格式,精確定義了這個 Widget 需要的所有參數,包括參數名、類型(字符串、數字、布爾等)和是否必需,這為 AI 提供了結構化的指令
  • widgetBuilder: 它負責接收 AI 返回的、符合 dataSchema 規範的 JSON 數據,並將其真正地渲染成一個 Flutter Widget

如果沒有定義任何自定義組件,而是直接使用了 CoreCatalogItems.asCatalog(),那麼 AI 在生成 UI 時就只能使用 flutter_genui 內置的最核心的幾個基礎組件。

具體效果如下圖所示,甚至在整個過程中,flutter_genui 能夠捕捉用戶的交互行為(如按鈕點擊、文本輸入),並將這些事件作為上下文信息發送回 AI,以便 AI 在下一輪對話中做出響應:

Image Image

而對於 flutter_genui,主要的核心對象有:

  • UiAgent (門面): 這是開發者主要互動的入口點,它封裝了 GenUiManagerAiClient,是整個流程的協調者。
  • Catalog (Widget 目錄): 定義了 AI 可以使用的 Widget 集合,每個 CatalogItem 包含 Widget 的名稱、數據結構(Schema)和渲染它的構建函數
  • AiClient (AI 客戶端): 負責與大語言模型通信的介面,GeminiAiClient 是其針對 Gemini 模型的具體實現,可以拓展支持其他模型
  • GenUiManager (UI 狀態管理器): 管理所有動態生成的 UI 界面(稱為 "Surfaces")的狀態,它提供了 addOrUpdateSurfacedeleteSurface 等工具供 AI 調用,並透過流(Stream)將更新通知給 UI
  • GenUiSurface (UI 渲染器): 一個 Flutter Widget,負責根據 GenUiManager 提供的 UiDefinition 遞歸地構建和渲染整個 UI 樹

比如以下是一個簡單的例字,展示了如何使用 UiAgent

import 'package:flutter/material.dart';
import 'package:flutter_genui/flutter_genui.dart';

void main() {
  // 初始化 Firebase 等
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late final UiAgent _uiAgent;
  final List<GenUiUpdate> _updates = [];

  @override
  void initState() {
    super.initState();
    _uiAgent = UiAgent(
      'You are a helpful AI assistant that builds UIs.',
      catalog: CoreCatalogItems.asCatalog(),
      onSurfaceAdded: _onSurfaceAdded,
    );
  }

  void _onSurfaceAdded(SurfaceAdded update) {
    setState(() {
      _updates.add(update);
    });
  }

  void _sendPrompt(String text) {
    if (text.trim().isEmpty) return;
    _uiAgent.sendRequest(UserMessage.text(text));
  }

  @override
  void dispose() {
    _uiAgent.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('GenUI Demo')),
        body: Column(
          children: [
            Expanded(
              // 渲染動態 UI 界面
              child: ListView.builder(
                itemCount: _updates.length,
                itemBuilder: (context, index) {
                  final update = _updates[index];
                  return GenUiSurface(
                    host: _uiAgent.host,
                    surfaceId: update.surfaceId,
                    onEvent: (event) {
                      // UiAgent 會自動處理事件
                    },
                  );
                },
              ),
            ),
            // 聊天輸入框 Widget ,調用 _sendPrompt
            // ····
          ],
        ),
      ),
    );
  }
}

Image

另外,在前面我們說過,AI 返回的是一個結構化的 JSON 數據,而對於 genui 來說,他底層會有一個 dart_schema_builder 的基礎工具包支持,它的核心作用是讓你能夠用 Dart 代碼來創建和驗證 JSON Schema

可以把它理解成一個「翻譯器」,在 flutter_genui 的世界裡,AI 需要知道它有哪些 UI 組件 (Widgets) 可以使用,以及每個組件需要哪些參數,這些規則就是透過 JSON Schema 來定義

簡單說,它的作用是:

  • 用 Dart 構建 JSON Schema: 開發者不需要手動編寫複雜的 JSON 文件來定義規則,而是可以透過 Dart 的鏈式調用來構建,例如,你可以定義一個「卡片」組件,規定它必須有一個 title (字符串類型) 和一個 description (也是字符串),還有一個可選的 imageUrl
  • 數據驗證: 它可以根據你定義的 Schema 來驗證一個 JSON 對象是否合法,這在接收 AI 返回的數據時至關重要,可以確保 AI 給出的 UI 「指令」是完整且格式正確的,避免程序因數據格式錯誤而崩潰
  • 為 AI 提供「工具」的藍圖: flutter_genui 會將使用 dart_schema_builder 創建的 Schema 發送給大語言模型,這等於告訴 AI:「你可以使用這些工具(Widget),每個工具的參數和格式必須遵守這份說明書。」

也就是,對於 genui 底層,會使用 dart_schema_builder 為 Flutter Widgets (例如 InformationCard, ItineraryDay) 定義好 Schema,將這些定義好的 Widgets 註冊到 flutter_genuiCatalog

之後用戶輸入文本,UiAgent 啟動,AiClient 將對話和從 Catalog 中提取的 Schemas 發送給 LLM,LLM 返回一個符合某個 Schema 的 JSON 指令,UiAgentGenUiManager 解析該指令,更新 UI 狀態。

最後 GenUiSurface 監聽到狀態變化,使用 Catalog 中的構建函數,將 JSON 數據渲染成用戶可見的 Flutter 界面。

所以最終 UI 是透過 GenUiManager + GenUiSurface 渲染出來,具體大概流程為:

  • GenUiManager 負責管理所有 UI 界面的當前狀態,內部有一個 _surfaces map,用來存儲所有 UI 界面(Surface)的定義,每個 Surface 都有一個唯一的 surfaceId,其對應的值是一個 ValueNotifier<UiDefinition?>,這意味著當 UiDefinition 改變時,可以通知監聽者

  • 在 AI 更新時,GenUiManager 會更新 _surfaces map 中對應 surfaceIdUiDefinition,然後透過一個 StreamController (_surfaceUpdates) 發出 SurfaceAddedSurfaceUpdated 事件,這個廣播是 UI 能夠自動重建的關鍵。

  • GenUiSurface 是一個 StatefulWidget,它是將抽象的 UiDefinition 渲染為用戶可見界面的最終執行者,它監聽了 surfaceIdValueNotifier,並有一個遞歸 _buildWidget 函數

  • GenUiSurface 會從根 widget ID 開始,從 UiDefinition 中查找 widget 的 JSON 數據,然後調用 widget.host.catalog.buildWidget 方法,buildWidget 方法會找到對應的 CatalogItem 並執行其 widgetBuilder,從而創建出 Flutter Widget

所以可以看到,genui 的核心是利用 AI 大模型的 UI 組織能力,讓它透過用戶的描述和已有的控件目錄,動態渲染和生成所需的 UI 控件。

話說回來,這裡說到 AI 生成的核心產物是 UiDefinition 對象,它本質上是一個 JSON 結構,JSON 作為純文本數據其實是可以保存的,比如我們對這部分數據進行攔截緩存,並在啟動時加載渲染,實際上這也是一個有限能力的熱更新模型

另外,目前使用 flutter_genui 最方便的默認實現是基於 Firebase 使用默認的 FirebaseAiClient,它實現了與 Firebase AI(特別是 Gemini 模型)進行通信的支持,官方默認提供的 simple_chattravel_app 都是使用它。

你也可以在透過 AiClient 抽象介面實現自己自定義的 client 。

所以,你覺得 genui 有前景嗎?


原文出處:https://juejin.cn/post/7547639458385313855


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝10   💬6   ❤️11
423
🥈
我愛JS
📝1   💬5   ❤️4
89
🥉
AppleLily
📝1   💬4   ❤️1
48
#4
💬2  
6
#5
💬1  
5
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次