Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@
"type": "dart",
"flutterMode": "release"
},
{
"name": "liquid-gen",
"cwd": "apps/liquid-gen",
"request": "launch",
"type": "dart"
},
{
"name": "liquid-gen (profile mode)",
"cwd": "apps/liquid-gen",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "liquid-gen (release mode)",
"cwd": "apps/liquid-gen",
"request": "launch",
"type": "dart",
"flutterMode": "release"
},

{
"name": "Update goldens",
"request": "launch",
Expand Down
45 changes: 45 additions & 0 deletions apps/liquid-gen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
30 changes: 30 additions & 0 deletions apps/liquid-gen/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "bd7a4a6b5576630823ca344e3e684c53aa1a0f46"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: bd7a4a6b5576630823ca344e3e684c53aa1a0f46
base_revision: bd7a4a6b5576630823ca344e3e684c53aa1a0f46
- platform: macos
create_revision: bd7a4a6b5576630823ca344e3e684c53aa1a0f46
base_revision: bd7a4a6b5576630823ca344e3e684c53aa1a0f46

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
3 changes: 3 additions & 0 deletions apps/liquid-gen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# liquid_gen

A new Flutter project.
1 change: 1 addition & 0 deletions apps/liquid-gen/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:flutter_lints/flutter.yaml
95 changes: 95 additions & 0 deletions apps/liquid-gen/lib/ai/genui_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import 'package:flutter/foundation.dart';
import 'package:genui/genui.dart' as genui;
import 'package:genui_google_generative_ai/genui_google_generative_ai.dart';

import '../catalog/liquid_catalog.dart';
import '../services/api_key_service.dart';
import 'system_instructions.dart';

/// GenUI service that manages the conversation and content generation
class GenUIService {
static final GenUIService instance = GenUIService._();
GenUIService._();

genui.GenUiConversation? _conversation;
genui.A2uiMessageProcessor? _messageProcessor;
GoogleGenerativeAiContentGenerator? _contentGenerator;
ValueChanged<genui.SurfaceAdded>? _onSurfaceAdded;
ValueChanged<genui.SurfaceRemoved>? _onSurfaceDeleted;

/// Set callbacks for surface events
void setCallbacks({
ValueChanged<genui.SurfaceAdded>? onSurfaceAdded,
ValueChanged<genui.SurfaceRemoved>? onSurfaceDeleted,
}) {
_onSurfaceAdded = onSurfaceAdded;
_onSurfaceDeleted = onSurfaceDeleted;
}

/// Initialize the GenUI conversation with Google Gemini
///
/// Note: You may see a warning about "Unsupported keyword 'minItems'" when
/// GenUI adapts the catalog schema to function tool schemas. This is a known
/// GenUI limitation where `minItems` constraints are added to the `components`
/// property, which Gemini's function calling API doesn't support in that context.
/// The warning says "It will be ignored" and can be safely ignored - functionality
/// is not affected. See: https://github.com/flutter/genui/issues/428
Future<void> initialize() async {
// Get API key
final apiKey = await ApiKeyService.instance.getApiKey('gemini');
if (apiKey == null || apiKey.isEmpty) {
throw Exception('Gemini API key not found. Please set it in settings.');
}

// Create message processor with Liquid Flutter catalog
_messageProcessor = genui.A2uiMessageProcessor(
catalogs: [LiquidCatalog.catalog],
);

// Create content generator
_contentGenerator = GoogleGenerativeAiContentGenerator(
catalog: LiquidCatalog.catalog,
systemInstruction: systemInstructions,
modelName: 'models/gemini-2.5-flash',
apiKey: apiKey,
);

// Create conversation
_conversation = genui.GenUiConversation(
contentGenerator: _contentGenerator!,
a2uiMessageProcessor: _messageProcessor!,
onSurfaceAdded: _onSurfaceAdded,
onSurfaceDeleted: _onSurfaceDeleted,
);
}

/// Get the GenUI conversation instance
genui.GenUiConversation? get conversation => _conversation;

/// Get the message processor (host) for GenUiSurface
genui.A2uiMessageProcessor? get host => _messageProcessor;

/// Send a message to the AI
Future<void> sendMessage(String text) async {
if (_conversation == null) {
await initialize();
}
_conversation?.sendRequest(genui.UserMessage.text(text));
}

/// Dispose resources
void dispose() {
_conversation?.dispose();
_contentGenerator?.dispose();
_messageProcessor?.dispose();
_conversation = null;
_contentGenerator = null;
_messageProcessor = null;
}

/// Reinitialize with new API key if needed
Future<void> reinitialize() async {
dispose();
await initialize();
}
}
35 changes: 35 additions & 0 deletions apps/liquid-gen/lib/ai/system_instructions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/// System instructions for GenUI to guide AI in using Liquid Flutter widgets correctly
const String systemInstructions = '''
You are an expert UI generation assistant for Liquid Flutter. Your task is to generate Flutter widget trees using Liquid Flutter components.

CRITICAL RULES:
1. You MUST use ONLY the widgets available in the catalog. Do NOT use Column, Row, or basic Flutter Container widgets.
2. Use LdAutoSpace instead of Column for arranging items vertically with automatic spacing.
3. Use LdContainer (from the catalog) for theme-aware containers, not the basic Flutter Container widget.
4. Use LdScaffold as the root widget for screens with LdAppBar and LdScaffoldBody.
5. Use LdScaffoldBody with addContainer: true for automatic padding.
6. Follow the Liquid Flutter design system patterns:
- Use LdText.h() for headlines
- Use LdText.p() for paragraphs
- Use LdText.l() for labels
- Use LdText.caption() for captions
- Use LdAutoSpace() for vertical layouts with automatic spacing
- Use LdCard with padding: EdgeInsets.zero when containing LdListItems
- Group related content in LdBundle or LdCard

When generating UI:
1. Always start with LdScaffold as the root
2. Add LdAppBar in the appBars array if a title is needed
3. Use LdScaffoldBody with addContainer: true for the main content
4. Use LdAutoSpace for arranging widgets vertically
5. Use appropriate Liquid Flutter components from the catalog
6. Follow proper nesting and structure
7. Include appropriate properties for each widget

Example structure:
- LdScaffold
- appBars: [LdAppBar with title]
- body: LdScaffoldBody
- addContainer: true
- children: [LdAutoSpace with content widgets]
''';
Loading
Loading