Flutter 又為 AI 時代添磚加瓦:全新 ComponentLibrary 提議

近日,Flutter 又提出了一個新的提案:為 Flutter 增加一個標準原語:ComponentLibrary

ComponentLibrary 的主要想法是:為 Flutter 提供一個統一的「元件註冊表/元件目錄資料協議」,讓 GenUI、Stac、Widgetbook、OpenUI 這類工具可以用同一套方式發現、描述、呼叫元件。

前面我們應該聊過 GenUI,作為一個在執行階段由 Agent 動態生成 UI 的 lib,它支援的 A2UI 協議所生成的是一套與框架無關的 UI 中繼資料,而 Flutter 需要針對這份協議資料做 GenUI 實作。本來其實也沒什麼問題,但架不住現在場景不只 GenUI:

場景它需要什麼
GenUI
AI 需要知道有哪些元件、元件 ID 是什麼、怎麼傳參數
Server-driven UI,例如 Stac
伺服器端 payload 需要對應到 Flutter widget
Widgetbook / showcase 工具
需要枚舉元件,生成元件目錄和 playground
自動化測試 / storyboarding 工具
需要知道元件參數、範例、可變狀態

但目前這些工具都需要在專案裡做自己的自訂支援:

  • Stac 實作了自己的 custom widget registry
  • GenUI catalog items 透過 json_schema 解析執行
  • Widgetbook v3 叫 knobs,v4 叫 args
  • OpenUI Library 也透過 json_schema 處理參數

所以如果同一個企業設計系統想同時接 GenUI、Stac、Widgetbook、自動化測試,就得寫多套重複的 wrapper / registry / adapter,這就造成生態割裂:

同一批元件,需要不同工具用不同結構描述,但對 AI 時代來說,這種結構化能力本身就是必須的。

不得不說,這段時間在做 AI e2e 專案測試時確實很煩,Compose、iOS 和鴻蒙都沒有標準的 MCP 注入支援,像鴻蒙和 Android 都是 adb 和 hdc 注入加截圖辨識來取得點位,效率和效能都太難受了。Flutter 能走 MCP 執行,相對效率還好一點點,但是對 AI 仍然不夠友善。

而且隨著 Material / Cupertino 逐步解耦落地,Flutter 的整體設計風格會越來越中立,所以 Flutter core 未來應該會更傾向設計系統無關的方向,不把某個具體設計系統深度綁在核心層裡,而是:

「這個 App / 公司 / package 有一組元件,它們叫什麼、怎麼建構、怎麼被工具發現。」

這就是 ComponentLibrary 提議想要支援的場景,特別是 GenUI / SDUI 也在推動這個需求,因為傳統 Flutter 是開發者手寫 widget tree,像這樣:

scss 体验AI代码助手 代码解读复制代码Column(
  children: [
    Text(...),
    ProfileCard(...),
  ],
)

但是在 GenUI / SDUI 裡,widget tree 可能來自:

json 体验AI代码助手 代码解读复制代码{
  "type": "cards.profile",
  "args": {
    "name": "Asher",
    "avatarUrl": "..."
  }
}

也可能來自其他 LLM tool calling 或工具渲染,這時候系統就必須知道:

cards.profile 對應哪個 Flutter widget,參數怎麼傳,建構失敗怎麼報錯。

所以 ComponentLibrary 最重要的就是提供一個統一的元件庫契約,讓這些工具可以共用

對此,Flutter 官方這次主要設計了兩個類:ComponentComponentLibrary,其中 Component 主要用於描述單個元件,一個 Component 大概包含:

dart 体验AI代码助手 代码解读复制代码@immutable
class Component {
  final String id;
  final String description;
  final Widget Function(
    BuildContext context,
    Map<String, dynamic> arguments,
  ) builder;
}

也就是:

字段 作用
id 元件唯一識別,例如 text.primarycards.profile
description 給 AI / 工具 / 文件看的語意描述
builder 真正把參數轉換成 Flutter Widget 的函式

例如下面程式碼,Component 就是 widget 的「註冊描述和建構入口」,這一套感覺就是從 GenUI 拆出來的既視感:

php 体验AI代码助手 代码解读复制代码Component(
  id: 'cards.profile',
  description: 'Displays user details alongside avatar layouts within standard dashboard feeds.',
  builder: (context, args) {
    return ProfileCard(
      displayName: args['name'] as String,
      avatarUrl: args['avatarUrl'] as String?,
    );
  },
)

ComponentLibrary 主要是管理一套元件的概念,大概如下:

dart 体验AI代码助手 代码解读复制代码@immutable
class ComponentLibrary {
  final String name;
  final Map<String, Component> components;
​
  const ComponentLibrary({
    required this.name,
    required this.components,
  });
​
  factory ComponentLibrary.compose(
    String name,
    List<ComponentLibrary> libraries,
  ) {
    final aggregated = <String, Component>{};
    for (final lib in libraries) {
      aggregated.addAll(lib.components);
    }
    return ComponentLibrary(name: name, components: aggregated);
  }
​
  Widget buildComponent(
    BuildContext context,
    String componentId,
    Map<String, dynamic> arguments,
  ) {
    final component = components[componentId];
    if (component == null) {
      throw FlutterError.fromParts(<DiagnosticsNode>[
        ErrorSummary('Component resolution failed within library: $name'),
        ErrorDescription('The componentId "$componentId" was not registered.'),
      ]);
    }
    return component.builder(context, arguments);
  }
}

ComponentLibrary 主要做三件事:

  • 保存一套元件
  • 支援多個 library 組合 ComponentLibrary.compose(...),例如可以把 coreDesignSystemadminDashboardComponentsmarketingComponents 合成一個總庫
  • 根據 componentId 建構 widget,如果找不到元件,就拋出 Flutter 風格的 FlutterError
dart 体验AI代码助手 代码解读复制代码library.buildComponent(
  context,
  'cards.profile',
  {
    'name': 'Asher',
    'avatarUrl': '...',
  },
)

例如對應 App 企業的設計系統可以統一註冊,例如:

php 体验AI代码助手 代码解读复制代码final acmeDesignSystem = ComponentLibrary(
  name: 'AcmeCorporate',
  components: [
    Component(
      id: 'text.primary',
      description: 'Primary body text used when text is information or descriptive...',
      builder: (context, args) {
        return AcmeText(
          content: args['content'] as String,
        );
      },
    ),
    Component(
      id: 'cards.profile',
      description: 'Displays user details alongside avatar layouts within standard dashboard feeds.',
      builder: (context, args) {
        return ProfileCard(
          displayName: args['name'] as String,
          avatarUrl: args['avatarUrl'] as String?,
        );
      },
    ),
  ],
);

這個方向就是,設計系統的維護者把元件集中宣告出來,形成一個標準 library。

另一個方向就是給 GenUI / Server-driven UI 用,例如這種場景通常需要:

javascript 体验AI代码助手 代码解读复制代码Widget buildDynamicNode(
  BuildContext context,
  Map<String, dynamic> remotePayload,
) {
  return Renderer(
    context,
    library: acmeDesignSystem,
    payload: remotePayload,
  );
}

也就是伺服器或 AI 回傳 payload,Renderer 根據 payload 裡的 component id 去 ComponentLibrary 裡找對應元件,不過 Flutter 不負責做這個 Renderer,也不負責定義 JSON 協議,Flutter 只提供元件庫結構。

另一個場景就是給 Widgetbook 之類的專案使用,例如:

csharp 体验AI代码助手 代码解读复制代码Widget buildDevelopmentPlayground() {
  return Widgetbook.fromLibrary(
    library: acmeDesignSystem,
  );
}

也就是說,Widgetbook 這類工具可以直接讀取 ComponentLibrary,自動生成側邊欄、元件目錄、參數面板、預覽頁面。以前每個工具都要自己定義,以後理論上可以統一消費 ComponentLibrary

當然,ComponentLibrary 的目標是抽象出一套 Flutter 統一的控制項描述協議,但它不做完整的鏈路,因為這個設計本身不是 Remote UI Parsing Engine:

  • 不做 JSON-to-Widget parser
  • 不做 AI SDK,它只是一個 layout container primitive
  • 不接管 accessibility,Component 不改變 rendering tree、layout behavior 和 semantics nodes
  • 不接管國際化,Component.descriptionComponentLibrary.name 主要是開發期、除錯、工具索引用的,不要求 runtime localization,真正使用者可見的多語文案,還是要在 builder 裡實作

當然,目前這個設計還處於草稿階段,一些問題還需要後續解決,例如:

  • 多個庫合併時,ID 衝突策略還需要考慮怎麼處理
  • 是否需要 InheritedComponentLibrary,提供類似 Theme.of(context) 的能力還沒定
  • 它應該進 Flutter framework,還是應該先做 package?
  • 安全邊界也還沒選好,畢竟雖然不做 JSON parser,但典型使用場景就是遠端 payload / LLM payload 決定元件建構,這會帶來一些安全風險
  • ·····

所以 ComponentLibrary 的核心是暴露一個乾淨、獨立的註冊協議,第三方庫可以漸進式採用,不會破壞既有設計,同時又把元件設計支援提升成 Flutter framework 的統一實作,這在 AI 時代來說還蠻有用的。

就是不知道下一個版本能不能落地。

連結

flutter.dev/go/introduce-component-library


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


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

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝15   ❤️1
457
🥈
我愛JS
1
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登