AI x FlutterでリアルタイムにUIを生成する
スニダンを開発している@natsuk4zeです!
今回は生成UIというリアルタイムにUIを生成する技術を、Flutterで試してきたので、ご紹介します✨
GitHubもぜひフォーローお願いします!
デモ動画
今回はこのようなデモを用意してみました。
ローティングを挟んだ後に表示される沖縄に関する画面は丸ごとAIが生成しています。
設計
ユーザーの入力を元に、AIがJSON形式でWidgetを生成し、アプリはそのJSONをWidgetに変換して表示しています。
ユーザー入力画面
ここでは、ユーザーが入力したテキストを、プッシュ遷移で生成UI画面に渡しているのみで、特別なことはしていません。
/// ユーザー入力画面
class UserInputScreen extends HookWidget {
const UserInputScreen({super.key});
Widget build(BuildContext context) {
final textController = useTextEditingController();
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(controller: textController),
const Gap(16),
FilledButton(
// テキストを生成UI画面に渡す
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GenUIScreen(
inputText: textController.text,
),
),
),
// 省略
),
],
),
);
}
}
生成UI画面
ここでは、ユーザー入力画面から受け取ったTextを元に、APIからJsonを取得し、StacでWidgetに変換して表示しています。
Scaffold
やCard
等のWidgetが一切登場していないことから、一画面丸ごとJsonから生成されていることが分かります。
/// 生成UI画面
class GenUIScreen extends ConsumerWidget {
const GenUIScreen({super.key, required this.inputText});
final String inputText;
Widget build(BuildContext context, WidgetRef ref) {
final json = ref.watch(stacJsonProvider(text: inputText));
return switch (json) {
AsyncData(:final value) => Stac.fromJson(value, context),
_ => const Center(child: CircularProgressIndicator()),
}!;
}
}
Stacについて
Stacとはpub.devで公開中のServer Driven UI Frameworkパッケージです!
JSON形式でWidgetを定義、表示できることが大きな特徴です。
全てのWidgetに対応している訳ではない点や、Widget to JSONは開発中である点には注意が必要です。
Gemini API
ここでは、ユーザーから入力されたTextを元に、Gemini APIでJson形式のWidgetを生成しています。生成されたJsonは非同期Providerで公開されています。
Future<Json> stacJson(Ref ref, {required String inputText}) async {
final dio = Dio();
const apiKey = String.fromEnvironment('GEMINI_API_KEY');
final response = await dio.post<Json>(
'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=$apiKey',
data: {
"system_instruction": {
"parts": [
{
"text": "1. あなたは Flutter の Stac パッケージ向け JSON API ジェネレーターです。"
},
{
"text": "2. ルートの \"type\" は必ず \"scaffold\"。"
},
{
"text": "3. Material Design 3 準拠のデザインを使用すること。"
},
,,, // その他指示
]
},
"contents": [
{
"parts": [
{"text": inputText}
]
}
]
},
);
/*
整形
*/
return jsonDecode(extractedJson);
}
モデルは、gemini-2.0-flashを使用しています。無料で簡単に使えることと、Json系を扱うのが上手なイメージがあったからです!
system_instruction
にAIへの指示を渡しています。ここでJsonジェネレータとして振る舞うように指示したり、UIに関する制約を指示しています。
contents
にユーザーが入力したtext
(今回であれば「沖縄のおすすめスポット」)を渡しています。
結果
というわけで、今回はGeminiとStacを組み合わせたFlutterの生成UIにチャレンジしてみました!
生成UIによって、ユーザー単位でのUI表示が可能になるので、よりパーソナライズされた体験を届けることができそうです✨
今後は画面単位ではなく、コンポーネント単位で活用したり、loadingで生成してる感を演出したりしても面白そうです...!
(Webで実装した方が速そうではある...笑)
ではではー👋

株式会社SODAの開発組織がお届けするZenn Publicationです。 是非Entrance Bookもご覧ください! → recruit.soda-inc.jp/engineer
Discussion