diff --git a/coverage_baseline.yaml b/coverage_baseline.yaml
index 7bc26ea10..3c4bcc367 100644
--- a/coverage_baseline.yaml
+++ b/coverage_baseline.yaml
@@ -5,12 +5,11 @@
# Monorepo Coverage High-Water Marks (Auto-generated by test_and_fix --update-baseline)
dev_tools/catalog_gallery: 52.91
dev_tools/composer: 20.49
+examples/express_chat: 35.76
+examples/simple_chat: 37.33
packages/a2ui_core: 76.30
packages/genai_primitives: 100.00
-packages/genui: 79.71
+packages/genui: 79.72
packages/genui_a2a: 91.37
-packages/json_schema_builder: 79.09
-tool/e2e: 100.00
-tool/fix_copyright: 89.83
-tool/release: 78.01
-tool/test_and_fix: 94.37
+packages/genui_express: 63.34
+packages/json_schema_builder: 79.27
diff --git a/coverage_policy.yaml b/coverage_policy.yaml
index 714d300ed..83c66fb3a 100644
--- a/coverage_policy.yaml
+++ b/coverage_policy.yaml
@@ -18,11 +18,14 @@ exclude:
- "**/*.freezed.dart"
- "**/*.mocks.dart"
- "**/generated/**"
+ - "**/*_web.dart"
# Package-specific overrides (paths relative to monorepo root).
packages:
packages/genui:
threshold: 75.0
+ packages/genui_express:
+ threshold: 60.0
packages/a2ui_core:
threshold: 75.0
packages/json_schema_builder:
@@ -36,6 +39,8 @@ packages:
tool/release:
threshold: 75.0
examples/simple_chat:
- enabled: false
+ threshold: 30.0
+ examples/express_chat:
+ threshold: 30.0
examples/verdure/client:
enabled: false
diff --git a/dev_tools/catalog_gallery/linux/flutter/generated_plugins.cmake b/dev_tools/catalog_gallery/linux/flutter/generated_plugins.cmake
index 8e2a1900c..a2eef970f 100644
--- a/dev_tools/catalog_gallery/linux/flutter/generated_plugins.cmake
+++ b/dev_tools/catalog_gallery/linux/flutter/generated_plugins.cmake
@@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
+ jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/dev_tools/catalog_gallery/macos/Flutter/GeneratedPluginRegistrant.swift b/dev_tools/catalog_gallery/macos/Flutter/GeneratedPluginRegistrant.swift
index ad1073ab5..7b265476e 100644
--- a/dev_tools/catalog_gallery/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/dev_tools/catalog_gallery/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -12,5 +12,5 @@ import video_player_avfoundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
- FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
+ VideoPlayerPlugin.register(with: registry.registrar(forPlugin: "VideoPlayerPlugin"))
}
diff --git a/dev_tools/composer/linux/flutter/generated_plugins.cmake b/dev_tools/composer/linux/flutter/generated_plugins.cmake
index c085ca836..a1bc1781f 100644
--- a/dev_tools/composer/linux/flutter/generated_plugins.cmake
+++ b/dev_tools/composer/linux/flutter/generated_plugins.cmake
@@ -10,6 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
+ jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/dev_tools/composer/macos/Flutter/GeneratedPluginRegistrant.swift b/dev_tools/composer/macos/Flutter/GeneratedPluginRegistrant.swift
index 802a33c8d..4825c9db2 100644
--- a/dev_tools/composer/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/dev_tools/composer/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -15,6 +15,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
- FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
+ VideoPlayerPlugin.register(with: registry.registrar(forPlugin: "VideoPlayerPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
}
diff --git a/dev_tools/composer/windows/flutter/generated_plugins.cmake b/dev_tools/composer/windows/flutter/generated_plugins.cmake
index f74fccabd..79fabcfb8 100644
--- a/dev_tools/composer/windows/flutter/generated_plugins.cmake
+++ b/dev_tools/composer/windows/flutter/generated_plugins.cmake
@@ -11,6 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
+ jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/examples/express_chat/.metadata b/examples/express_chat/.metadata
new file mode 100644
index 000000000..5492cf186
--- /dev/null
+++ b/examples/express_chat/.metadata
@@ -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: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
+ channel: "stable"
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
+ base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
+ - platform: linux
+ create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
+ base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
+
+ # 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'
diff --git a/examples/express_chat/README.md b/examples/express_chat/README.md
new file mode 100644
index 000000000..152846d85
--- /dev/null
+++ b/examples/express_chat/README.md
@@ -0,0 +1,43 @@
+# Simple Chat Example
+
+This application is a minimal example of how to use the `genui` package to create a simple, conversational chat application.
+
+## Purpose
+
+The main goal of this example is to demonstrate the fundamental concepts of `genui` in a straightforward chat context. It shows how to:
+1. Initialize and use the `SurfaceController`, the core engine for the package.
+2. Provide a simple system prompt to guide the AI's behavior.
+3. Send user messages to the AI and receive responses.
+4. Handle the creation of new UI "surfaces" generated by the AI.
+5. Render these dynamic UI surfaces within a standard chat message list.
+6. Manage a conversation history that interleaves user text messages with AI-generated UI responses.
+
+Unlike more complex examples, this app does not define a custom widget catalog. Instead, it relies on the default `coreCatalog` provided by `genui`, meaning the AI can only respond with basic widgets like `Text`, `Column`, `ElevatedButton`, etc.
+
+## How it Works
+
+The application's logic is contained almost entirely within `lib/chat_session.dart`.
+
+1. **Initialization**: A `SurfaceController` is created to manage the state of UI surfaces.
+2. **User Input**: The user types a message into a `TextField` and hits send.
+3. **Sending the Message**:
+ - The user's text is immediately added to the local message list.
+ - The request is sent to the `AiClient`.
+4. **AI Response**:
+ - The `AiClient` streams `A2uiMessage`s back.
+ - These messages are piped into the `SurfaceController`.
+5. **UI Rendering**:
+ - The UI listens to `SurfaceController.surfaceUpdates` or `A2uiTransportAdapter` streams.
+ - When a surface is added, a `Surface` widget is rendered, dynamically building the UI based on the `UiDefinition` managed by `SurfaceController`.
+
+## Getting Started
+
+Follow the instructions in [Running the app with a Gemini key](../../docs/usage/run_app_with_gemini_key.md).
+
+## Video
+
+https://github.com/user-attachments/assets/469fb2cf-09cf-463c-8c9c-b9c0cb39203b
+
+This video is recorded on May 11, 2026, for [PR#905](https://github.com/flutter/genui/pull/905).
+
+Please update the link if you have a fresher version of the demo.
diff --git a/examples/express_chat/android/.gitignore b/examples/express_chat/android/.gitignore
new file mode 100644
index 000000000..be3943c96
--- /dev/null
+++ b/examples/express_chat/android/.gitignore
@@ -0,0 +1,14 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+.cxx/
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/examples/express_chat/android/app/build.gradle.kts b/examples/express_chat/android/app/build.gradle.kts
new file mode 100644
index 000000000..244285367
--- /dev/null
+++ b/examples/express_chat/android/app/build.gradle.kts
@@ -0,0 +1,47 @@
+plugins {
+ id("com.android.application")
+ id("kotlin-android")
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id("dev.flutter.flutter-gradle-plugin")
+ // START: FlutterFire Configuration
+ id("com.google.gms.google-services")
+ // END: FlutterFire Configuration
+}
+
+android {
+ namespace = "com.example.simple_chat"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_11.toString()
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId = "com.example.simple_chat"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/examples/express_chat/android/app/src/debug/AndroidManifest.xml b/examples/express_chat/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..a90346878
--- /dev/null
+++ b/examples/express_chat/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/examples/express_chat/android/app/src/main/AndroidManifest.xml b/examples/express_chat/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..bb1441467
--- /dev/null
+++ b/examples/express_chat/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/express_chat/android/app/src/main/kotlin/com/example/simple_chat/MainActivity.kt b/examples/express_chat/android/app/src/main/kotlin/com/example/simple_chat/MainActivity.kt
new file mode 100644
index 000000000..89498754b
--- /dev/null
+++ b/examples/express_chat/android/app/src/main/kotlin/com/example/simple_chat/MainActivity.kt
@@ -0,0 +1,9 @@
+// Copyright 2025 The Flutter Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package com.example.simple_chat
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity : FlutterActivity()
diff --git a/examples/express_chat/android/app/src/main/res/drawable-v21/launch_background.xml b/examples/express_chat/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 000000000..3827ad38b
--- /dev/null
+++ b/examples/express_chat/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/express_chat/android/app/src/main/res/drawable/launch_background.xml b/examples/express_chat/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 000000000..6e3711468
--- /dev/null
+++ b/examples/express_chat/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/express_chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/express_chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..db77bb4b7
Binary files /dev/null and b/examples/express_chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/examples/express_chat/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/express_chat/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..17987b79b
Binary files /dev/null and b/examples/express_chat/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/examples/express_chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/express_chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..09d439148
Binary files /dev/null and b/examples/express_chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/examples/express_chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/express_chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..d5f1c8d34
Binary files /dev/null and b/examples/express_chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/examples/express_chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/examples/express_chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..4d6372eeb
Binary files /dev/null and b/examples/express_chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/examples/express_chat/android/app/src/main/res/values-night/styles.xml b/examples/express_chat/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 000000000..deb01c1c9
--- /dev/null
+++ b/examples/express_chat/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/examples/express_chat/android/app/src/main/res/values/styles.xml b/examples/express_chat/android/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..45f156a54
--- /dev/null
+++ b/examples/express_chat/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/examples/express_chat/android/app/src/profile/AndroidManifest.xml b/examples/express_chat/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 000000000..a90346878
--- /dev/null
+++ b/examples/express_chat/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/examples/express_chat/android/build.gradle.kts b/examples/express_chat/android/build.gradle.kts
new file mode 100644
index 000000000..dbee657bb
--- /dev/null
+++ b/examples/express_chat/android/build.gradle.kts
@@ -0,0 +1,24 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+val newBuildDir: Directory =
+ rootProject.layout.buildDirectory
+ .dir("../../build")
+ .get()
+rootProject.layout.buildDirectory.value(newBuildDir)
+
+subprojects {
+ val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
+ project.layout.buildDirectory.value(newSubprojectBuildDir)
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean") {
+ delete(rootProject.layout.buildDirectory)
+}
diff --git a/examples/express_chat/android/gradle.properties b/examples/express_chat/android/gradle.properties
new file mode 100644
index 000000000..f018a6181
--- /dev/null
+++ b/examples/express_chat/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/examples/express_chat/android/gradle/wrapper/gradle-wrapper.properties b/examples/express_chat/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..e4ef43fb9
--- /dev/null
+++ b/examples/express_chat/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
diff --git a/examples/express_chat/android/settings.gradle.kts b/examples/express_chat/android/settings.gradle.kts
new file mode 100644
index 000000000..7712fad2d
--- /dev/null
+++ b/examples/express_chat/android/settings.gradle.kts
@@ -0,0 +1,29 @@
+pluginManagement {
+ val flutterSdkPath =
+ run {
+ val properties = java.util.Properties()
+ file("local.properties").inputStream().use { properties.load(it) }
+ val flutterSdkPath = properties.getProperty("flutter.sdk")
+ require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
+ flutterSdkPath
+ }
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id("dev.flutter.flutter-plugin-loader") version "1.0.0"
+ id("com.android.application") version "8.9.1" apply false
+ id("org.jetbrains.kotlin.android") version "2.1.0" apply false
+ // START: FlutterFire Configuration
+ id("com.google.gms.google-services") version("4.3.15") apply false
+ // END: FlutterFire Configuration
+}
+
+include(":app")
diff --git a/examples/express_chat/assets/climbing/10x2500x1667.jpg b/examples/express_chat/assets/climbing/10x2500x1667.jpg
new file mode 100644
index 000000000..46f6946d1
Binary files /dev/null and b/examples/express_chat/assets/climbing/10x2500x1667.jpg differ
diff --git a/examples/express_chat/assets/climbing/121x1600x1067.jpg b/examples/express_chat/assets/climbing/121x1600x1067.jpg
new file mode 100644
index 000000000..103e03552
Binary files /dev/null and b/examples/express_chat/assets/climbing/121x1600x1067.jpg differ
diff --git a/examples/express_chat/assets/climbing/128x3823x2549.jpg b/examples/express_chat/assets/climbing/128x3823x2549.jpg
new file mode 100644
index 000000000..b2983ca7b
Binary files /dev/null and b/examples/express_chat/assets/climbing/128x3823x2549.jpg differ
diff --git a/examples/express_chat/assets/climbing/134x4928x3264.jpg b/examples/express_chat/assets/climbing/134x4928x3264.jpg
new file mode 100644
index 000000000..b645e5ced
Binary files /dev/null and b/examples/express_chat/assets/climbing/134x4928x3264.jpg differ
diff --git a/examples/express_chat/assets/climbing/136x4032x2272.jpg b/examples/express_chat/assets/climbing/136x4032x2272.jpg
new file mode 100644
index 000000000..5a43e125f
Binary files /dev/null and b/examples/express_chat/assets/climbing/136x4032x2272.jpg differ
diff --git a/examples/express_chat/assets/climbing/15x2500x1667.jpg b/examples/express_chat/assets/climbing/15x2500x1667.jpg
new file mode 100644
index 000000000..f87e06869
Binary files /dev/null and b/examples/express_chat/assets/climbing/15x2500x1667.jpg differ
diff --git a/examples/express_chat/assets/climbing/166x1280x720.jpg b/examples/express_chat/assets/climbing/166x1280x720.jpg
new file mode 100644
index 000000000..36f3eb64f
Binary files /dev/null and b/examples/express_chat/assets/climbing/166x1280x720.jpg differ
diff --git a/examples/express_chat/assets/climbing/177x2515x1830.jpg b/examples/express_chat/assets/climbing/177x2515x1830.jpg
new file mode 100644
index 000000000..511fc06cb
Binary files /dev/null and b/examples/express_chat/assets/climbing/177x2515x1830.jpg differ
diff --git a/examples/express_chat/assets/climbing/184x4288x2848.jpg b/examples/express_chat/assets/climbing/184x4288x2848.jpg
new file mode 100644
index 000000000..70182daba
Binary files /dev/null and b/examples/express_chat/assets/climbing/184x4288x2848.jpg differ
diff --git a/examples/express_chat/assets/climbing/191x2560x1707.jpg b/examples/express_chat/assets/climbing/191x2560x1707.jpg
new file mode 100644
index 000000000..c4b43f633
Binary files /dev/null and b/examples/express_chat/assets/climbing/191x2560x1707.jpg differ
diff --git a/examples/express_chat/assets/climbing/231x4088x2715.jpg b/examples/express_chat/assets/climbing/231x4088x2715.jpg
new file mode 100644
index 000000000..5de42021c
Binary files /dev/null and b/examples/express_chat/assets/climbing/231x4088x2715.jpg differ
diff --git a/examples/express_chat/assets/climbing/235x5000x3333.jpg b/examples/express_chat/assets/climbing/235x5000x3333.jpg
new file mode 100644
index 000000000..0d6fb7fef
Binary files /dev/null and b/examples/express_chat/assets/climbing/235x5000x3333.jpg differ
diff --git a/examples/express_chat/assets/climbing/243x2300x1533.jpg b/examples/express_chat/assets/climbing/243x2300x1533.jpg
new file mode 100644
index 000000000..27b1fae73
Binary files /dev/null and b/examples/express_chat/assets/climbing/243x2300x1533.jpg differ
diff --git a/examples/express_chat/assets/climbing/247x3264x2168.jpg b/examples/express_chat/assets/climbing/247x3264x2168.jpg
new file mode 100644
index 000000000..a06f61ba3
Binary files /dev/null and b/examples/express_chat/assets/climbing/247x3264x2168.jpg differ
diff --git a/examples/express_chat/assets/climbing/287x4288x2848.jpg b/examples/express_chat/assets/climbing/287x4288x2848.jpg
new file mode 100644
index 000000000..0f065f29c
Binary files /dev/null and b/examples/express_chat/assets/climbing/287x4288x2848.jpg differ
diff --git a/examples/express_chat/assets/climbing/28x4928x3264.jpg b/examples/express_chat/assets/climbing/28x4928x3264.jpg
new file mode 100644
index 000000000..ba10a2852
Binary files /dev/null and b/examples/express_chat/assets/climbing/28x4928x3264.jpg differ
diff --git a/examples/express_chat/assets/climbing/296x3072x2048.jpg b/examples/express_chat/assets/climbing/296x3072x2048.jpg
new file mode 100644
index 000000000..15a8303b8
Binary files /dev/null and b/examples/express_chat/assets/climbing/296x3072x2048.jpg differ
diff --git a/examples/express_chat/assets/climbing/29x4000x2670.jpg b/examples/express_chat/assets/climbing/29x4000x2670.jpg
new file mode 100644
index 000000000..96b8ace76
Binary files /dev/null and b/examples/express_chat/assets/climbing/29x4000x2670.jpg differ
diff --git a/examples/express_chat/assets/climbing/315x2100x1500.jpg b/examples/express_chat/assets/climbing/315x2100x1500.jpg
new file mode 100644
index 000000000..e4aa9cfa3
Binary files /dev/null and b/examples/express_chat/assets/climbing/315x2100x1500.jpg differ
diff --git a/examples/express_chat/assets/climbing/343x2304x1536.jpg b/examples/express_chat/assets/climbing/343x2304x1536.jpg
new file mode 100644
index 000000000..825aa5081
Binary files /dev/null and b/examples/express_chat/assets/climbing/343x2304x1536.jpg differ
diff --git a/examples/express_chat/assets/climbing/368x4896x3264.jpg b/examples/express_chat/assets/climbing/368x4896x3264.jpg
new file mode 100644
index 000000000..41f022786
Binary files /dev/null and b/examples/express_chat/assets/climbing/368x4896x3264.jpg differ
diff --git a/examples/express_chat/assets/climbing/377x4884x3256.jpg b/examples/express_chat/assets/climbing/377x4884x3256.jpg
new file mode 100644
index 000000000..9a3c64ff7
Binary files /dev/null and b/examples/express_chat/assets/climbing/377x4884x3256.jpg differ
diff --git a/examples/express_chat/assets/climbing/450x4288x2848.jpg b/examples/express_chat/assets/climbing/450x4288x2848.jpg
new file mode 100644
index 000000000..30c8971f7
Binary files /dev/null and b/examples/express_chat/assets/climbing/450x4288x2848.jpg differ
diff --git a/examples/express_chat/assets/climbing/46x3264x2448.jpg b/examples/express_chat/assets/climbing/46x3264x2448.jpg
new file mode 100644
index 000000000..c6a4f2975
Binary files /dev/null and b/examples/express_chat/assets/climbing/46x3264x2448.jpg differ
diff --git a/examples/express_chat/assets/climbing/472x5000x3333.jpg b/examples/express_chat/assets/climbing/472x5000x3333.jpg
new file mode 100644
index 000000000..4a31d8112
Binary files /dev/null and b/examples/express_chat/assets/climbing/472x5000x3333.jpg differ
diff --git a/examples/express_chat/assets/climbing/475x4288x2848.jpg b/examples/express_chat/assets/climbing/475x4288x2848.jpg
new file mode 100644
index 000000000..ac707f630
Binary files /dev/null and b/examples/express_chat/assets/climbing/475x4288x2848.jpg differ
diff --git a/examples/express_chat/assets/climbing/485x4084x2713.jpg b/examples/express_chat/assets/climbing/485x4084x2713.jpg
new file mode 100644
index 000000000..2d512b1a6
Binary files /dev/null and b/examples/express_chat/assets/climbing/485x4084x2713.jpg differ
diff --git a/examples/express_chat/assets/climbing/62x2000x1333.jpg b/examples/express_chat/assets/climbing/62x2000x1333.jpg
new file mode 100644
index 000000000..0837b0867
Binary files /dev/null and b/examples/express_chat/assets/climbing/62x2000x1333.jpg differ
diff --git a/examples/express_chat/assets/climbing/66x3264x2448.jpg b/examples/express_chat/assets/climbing/66x3264x2448.jpg
new file mode 100644
index 000000000..3bd8c5cea
Binary files /dev/null and b/examples/express_chat/assets/climbing/66x3264x2448.jpg differ
diff --git a/examples/express_chat/assets/climbing/67x2848x4288.jpg b/examples/express_chat/assets/climbing/67x2848x4288.jpg
new file mode 100644
index 000000000..65d12fc4e
Binary files /dev/null and b/examples/express_chat/assets/climbing/67x2848x4288.jpg differ
diff --git a/examples/express_chat/assets/climbing/79x2000x3011.jpg b/examples/express_chat/assets/climbing/79x2000x3011.jpg
new file mode 100644
index 000000000..2f142b541
Binary files /dev/null and b/examples/express_chat/assets/climbing/79x2000x3011.jpg differ
diff --git a/examples/express_chat/integration_test/README.md b/examples/express_chat/integration_test/README.md
new file mode 100644
index 000000000..dbc27d6a5
--- /dev/null
+++ b/examples/express_chat/integration_test/README.md
@@ -0,0 +1,14 @@
+# Integration tests
+
+Renders canned A2UI samples through `ChatScreen` with a `FakeAiClient` — no API key.
+
+From `examples/express_chat`:
+
+```bash
+flutter pub get
+flutter test integration_test/app_test.dart -d macos
+```
+
+Swap `macos` for any device from `flutter devices`. `flutter pub get` is
+required first; without it you'll see a misleading `'../pubspec.yaml'` error
+from the pub-workspace lookup.
diff --git a/examples/express_chat/integration_test/app_test.dart b/examples/express_chat/integration_test/app_test.dart
new file mode 100644
index 000000000..299114d04
--- /dev/null
+++ b/examples/express_chat/integration_test/app_test.dart
@@ -0,0 +1,141 @@
+// Copyright 2025 The Flutter Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:express_chat/main.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:genkit/genkit.dart' as genkit;
+import 'package:integration_test/integration_test.dart';
+import 'package:logging/logging.dart';
+import 'package:network_image_mock/network_image_mock.dart';
+
+// Import from ../test via relative path since it is not in lib
+import '../test/fake_ai_client.dart';
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ // Configure logging
+ Logger.root.level = Level.ALL;
+ Logger.root.onRecord.listen((record) {
+ debugPrint('${record.level.name}: ${record.time}: ${record.message}');
+ });
+
+ group('Simple Chat Integration Tests', () {
+ testWidgets('render hello world sample', (tester) async {
+ await mockNetworkImagesFor(() async {
+ await _runTestForSample(
+ tester,
+ 'integration_test/samples/sample_1_hello.json',
+ (tester, client) async {
+ expect(find.textContaining('Hello, World!'), findsOneWidget);
+ },
+ );
+ });
+ });
+
+ testWidgets('render button sample', (tester) async {
+ await mockNetworkImagesFor(() async {
+ await _runTestForSample(
+ tester,
+ 'integration_test/samples/sample_2_button.json',
+ (tester, client) async {
+ // Button might be ElevatedButton, TextButton, or FilledButton.
+ // Just finding text is safer for integration test unless we care
+ // about specific styling.
+ expect(find.text('Click Me'), findsOneWidget);
+
+ // Interaction Verification
+ await tester.tap(find.text('Click Me'));
+ await tester.pump();
+ // Button action does not trigger a response if the fake client is
+ // empty, but it should send the prompt.
+ expect(
+ client.receivedPrompts,
+ contains(contains('Button Clicked')),
+ );
+ },
+ );
+ });
+ });
+
+ testWidgets('render form sample', (tester) async {
+ await mockNetworkImagesFor(() async {
+ await _runTestForSample(
+ tester,
+ 'integration_test/samples/sample_4_form.json',
+ (tester, client) async {
+ // Debug dump if fails
+ expect(find.text('Type'), findsOneWidget);
+ expect(find.text('Size'), findsOneWidget);
+ expect(find.text('Submit Filters'), findsOneWidget);
+ },
+ );
+ });
+ });
+
+ testWidgets('render mixed sample', (tester) async {
+ await mockNetworkImagesFor(() async {
+ await _runTestForSample(
+ tester,
+ 'integration_test/samples/sample_5_mixed.json',
+ (tester, client) async {
+ expect(find.text('Do you want to proceed?'), findsOneWidget);
+ expect(find.text('Yes, proceed'), findsOneWidget);
+ },
+ );
+ });
+ });
+ });
+}
+
+Future _runTestForSample(
+ WidgetTester tester,
+ String samplePath,
+ Future Function(WidgetTester, FakeGenkitClient) verify,
+) async {
+ // Read sample file
+ final file = File(samplePath);
+ if (!file.existsSync()) {
+ fail('Sample file not found: $samplePath');
+ }
+ final String jsonString = await file.readAsString();
+
+ // Initialize FakeGenkitClient
+ final fakeClient = FakeGenkitClient();
+
+ // Queue the response
+ // SurfaceController expects A2UI messages to be wrapped in markdown code
+ // blocks or detectable as structured content. Standard LLM behavior using
+ // GenUi is to return ```json ... ``` blocks.
+ fakeClient.addResponse('Here is the UI:\n```json\n$jsonString\n```');
+
+ await tester.pumpWidget(
+ MaterialApp(
+ home: ChatScreen(
+ ai: fakeClient.ai,
+ model: genkit.modelRef('local/fake-model'),
+ ),
+ ),
+ );
+
+ // Trigger a message to start the flow
+ await tester.enterText(find.byType(TextField), 'Test Trigger');
+ await tester.tap(find.byIcon(Icons.send));
+ await tester.pump(); // Start processing
+
+ // Wait for response and rendering.
+ // The FakeAiClient splits it into chunks with delays.
+ // We can't use pumpAndSettle() because some catalog widgets (e.g. Image's
+ // loadingBuilder shows a CircularProgressIndicator) have indeterminate
+ // animations that is not handled by pumpAndSettle.
+ for (var i = 0; i < 30; i++) {
+ await tester.pump(const Duration(milliseconds: 100));
+ }
+
+ // Run verification
+ await verify(tester, fakeClient);
+}
diff --git a/examples/express_chat/integration_test/samples/sample_1_hello.json b/examples/express_chat/integration_test/samples/sample_1_hello.json
new file mode 100644
index 000000000..68168ce1d
--- /dev/null
+++ b/examples/express_chat/integration_test/samples/sample_1_hello.json
@@ -0,0 +1,22 @@
+[
+ {
+ "version": "v0.9",
+ "createSurface": {
+ "surfaceId": "sample_1_hello",
+ "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json"
+ }
+ },
+ {
+ "version": "v0.9",
+ "updateComponents": {
+ "surfaceId": "sample_1_hello",
+ "components": [
+ {
+ "id": "root",
+ "component": "Text",
+ "text": "Hello, World!"
+ }
+ ]
+ }
+ }
+]
diff --git a/examples/express_chat/integration_test/samples/sample_2_button.json b/examples/express_chat/integration_test/samples/sample_2_button.json
new file mode 100644
index 000000000..c0b0b01cd
--- /dev/null
+++ b/examples/express_chat/integration_test/samples/sample_2_button.json
@@ -0,0 +1,40 @@
+[
+ {
+ "version": "v0.9",
+ "createSurface": {
+ "surfaceId": "sample_2_button",
+ "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json"
+ }
+ },
+ {
+ "version": "v0.9",
+ "updateComponents": {
+ "surfaceId": "sample_2_button",
+ "components": [
+ {
+ "id": "root",
+ "component": "Column",
+ "children": ["the_button"]
+ },
+ {
+ "id": "the_button",
+ "component": "Button",
+ "child": "btn_label",
+ "action": {
+ "event": {
+ "name": "submit",
+ "context": {
+ "value": "Button Clicked"
+ }
+ }
+ }
+ },
+ {
+ "id": "btn_label",
+ "component": "Text",
+ "text": "Click Me"
+ }
+ ]
+ }
+ }
+]
diff --git a/examples/express_chat/integration_test/samples/sample_4_form.json b/examples/express_chat/integration_test/samples/sample_4_form.json
new file mode 100644
index 000000000..f4687de47
--- /dev/null
+++ b/examples/express_chat/integration_test/samples/sample_4_form.json
@@ -0,0 +1,49 @@
+[
+ {
+ "version": "v0.9",
+ "createSurface": {
+ "surfaceId": "sample_4_form",
+ "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json"
+ }
+ },
+ {
+ "version": "v0.9",
+ "updateComponents": {
+ "surfaceId": "sample_4_form",
+ "components": [
+ {
+ "id": "root",
+ "component": "Column",
+ "children": ["field_type", "field_size", "submit_btn"]
+ },
+ {
+ "id": "field_type",
+ "component": "TextField",
+ "label": "Type",
+ "text": ""
+ },
+ {
+ "id": "field_size",
+ "component": "TextField",
+ "label": "Size",
+ "text": ""
+ },
+ {
+ "id": "submit_btn",
+ "component": "Button",
+ "child": "btn_text",
+ "action": {
+ "event": {
+ "name": "submit_form"
+ }
+ }
+ },
+ {
+ "id": "btn_text",
+ "component": "Text",
+ "text": "Submit Filters"
+ }
+ ]
+ }
+ }
+]
diff --git a/examples/express_chat/integration_test/samples/sample_5_mixed.json b/examples/express_chat/integration_test/samples/sample_5_mixed.json
new file mode 100644
index 000000000..39fc6eb79
--- /dev/null
+++ b/examples/express_chat/integration_test/samples/sample_5_mixed.json
@@ -0,0 +1,54 @@
+[
+ {
+ "version": "v0.9",
+ "createSurface": {
+ "surfaceId": "sample_5_mixed",
+ "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json"
+ }
+ },
+ {
+ "version": "v0.9",
+ "updateComponents": {
+ "surfaceId": "sample_5_mixed",
+ "components": [
+ {
+ "id": "root",
+ "component": "Column",
+ "children": ["question", "btn_yes", "btn_no"]
+ },
+ {
+ "id": "question",
+ "component": "Text",
+ "text": "Do you want to proceed?"
+ },
+ {
+ "id": "btn_yes",
+ "component": "Button",
+ "child": "yes_text",
+ "action": {
+ "event": { "name": "proceed_yes" }
+ }
+ },
+ {
+ "id": "yes_text",
+ "component": "Text",
+ "text": "Yes, proceed"
+ },
+ {
+ "id": "btn_no",
+ "component": "Button",
+ "child": "no_text",
+ "variant": "borderless",
+ "action": {
+ "event": { "name": "proceed_no" }
+ }
+ },
+ {
+ "id": "no_text",
+ "component": "Text",
+ "text": "No, cancel"
+ }
+ ]
+ }
+ }
+]
diff --git a/examples/express_chat/ios/.gitignore b/examples/express_chat/ios/.gitignore
new file mode 100644
index 000000000..b3afe3f58
--- /dev/null
+++ b/examples/express_chat/ios/.gitignore
@@ -0,0 +1,28 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
diff --git a/examples/express_chat/ios/Flutter/AppFrameworkInfo.plist b/examples/express_chat/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 000000000..0d1408009
--- /dev/null
+++ b/examples/express_chat/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 15.0
+
+
diff --git a/examples/express_chat/ios/Flutter/Debug.xcconfig b/examples/express_chat/ios/Flutter/Debug.xcconfig
new file mode 100644
index 000000000..ec97fc6f3
--- /dev/null
+++ b/examples/express_chat/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/examples/express_chat/ios/Flutter/Release.xcconfig b/examples/express_chat/ios/Flutter/Release.xcconfig
new file mode 100644
index 000000000..c4855bfe2
--- /dev/null
+++ b/examples/express_chat/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/examples/express_chat/ios/Podfile b/examples/express_chat/ios/Podfile
new file mode 100644
index 000000000..6649374d4
--- /dev/null
+++ b/examples/express_chat/ios/Podfile
@@ -0,0 +1,43 @@
+# Uncomment this line to define a global platform for your project
+platform :ios, '15.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/examples/express_chat/ios/Podfile.lock b/examples/express_chat/ios/Podfile.lock
new file mode 100644
index 000000000..59bb23525
--- /dev/null
+++ b/examples/express_chat/ios/Podfile.lock
@@ -0,0 +1,130 @@
+PODS:
+ - AppCheckCore (11.2.0):
+ - GoogleUtilities/Environment (~> 8.0)
+ - GoogleUtilities/UserDefaults (~> 8.0)
+ - PromisesObjC (~> 2.4)
+ - Firebase/Auth (12.4.0):
+ - Firebase/CoreOnly
+ - FirebaseAuth (~> 12.4.0)
+ - Firebase/CoreOnly (12.4.0):
+ - FirebaseCore (~> 12.4.0)
+ - firebase_app_check (0.4.1-2):
+ - Firebase/CoreOnly (~> 12.4.0)
+ - firebase_core
+ - FirebaseAppCheck (~> 12.4.0)
+ - Flutter
+ - firebase_auth (6.1.2):
+ - Firebase/Auth (= 12.4.0)
+ - firebase_core
+ - Flutter
+ - firebase_core (4.2.1):
+ - Firebase/CoreOnly (= 12.4.0)
+ - Flutter
+ - FirebaseAppCheck (12.4.0):
+ - AppCheckCore (~> 11.0)
+ - FirebaseAppCheckInterop (~> 12.4.0)
+ - FirebaseCore (~> 12.4.0)
+ - GoogleUtilities/Environment (~> 8.1)
+ - GoogleUtilities/UserDefaults (~> 8.1)
+ - FirebaseAppCheckInterop (12.4.0)
+ - FirebaseAuth (12.4.0):
+ - FirebaseAppCheckInterop (~> 12.4.0)
+ - FirebaseAuthInterop (~> 12.4.0)
+ - FirebaseCore (~> 12.4.0)
+ - FirebaseCoreExtension (~> 12.4.0)
+ - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
+ - GoogleUtilities/Environment (~> 8.1)
+ - GTMSessionFetcher/Core (< 6.0, >= 3.4)
+ - RecaptchaInterop (~> 101.0)
+ - FirebaseAuthInterop (12.4.0)
+ - FirebaseCore (12.4.0):
+ - FirebaseCoreInternal (~> 12.4.0)
+ - GoogleUtilities/Environment (~> 8.1)
+ - GoogleUtilities/Logger (~> 8.1)
+ - FirebaseCoreExtension (12.4.0):
+ - FirebaseCore (~> 12.4.0)
+ - FirebaseCoreInternal (12.4.0):
+ - "GoogleUtilities/NSData+zlib (~> 8.1)"
+ - Flutter (1.0.0)
+ - GoogleUtilities/AppDelegateSwizzler (8.1.0):
+ - GoogleUtilities/Environment
+ - GoogleUtilities/Logger
+ - GoogleUtilities/Network
+ - GoogleUtilities/Privacy
+ - GoogleUtilities/Environment (8.1.0):
+ - GoogleUtilities/Privacy
+ - GoogleUtilities/Logger (8.1.0):
+ - GoogleUtilities/Environment
+ - GoogleUtilities/Privacy
+ - GoogleUtilities/Network (8.1.0):
+ - GoogleUtilities/Logger
+ - "GoogleUtilities/NSData+zlib"
+ - GoogleUtilities/Privacy
+ - GoogleUtilities/Reachability
+ - "GoogleUtilities/NSData+zlib (8.1.0)":
+ - GoogleUtilities/Privacy
+ - GoogleUtilities/Privacy (8.1.0)
+ - GoogleUtilities/Reachability (8.1.0):
+ - GoogleUtilities/Logger
+ - GoogleUtilities/Privacy
+ - GoogleUtilities/UserDefaults (8.1.0):
+ - GoogleUtilities/Logger
+ - GoogleUtilities/Privacy
+ - GTMSessionFetcher/Core (5.0.0)
+ - PromisesObjC (2.4.0)
+ - RecaptchaInterop (101.0.0)
+
+DEPENDENCIES:
+ - firebase_app_check (from `.symlinks/plugins/firebase_app_check/ios`)
+ - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
+ - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
+ - Flutter (from `Flutter`)
+
+SPEC REPOS:
+ trunk:
+ - AppCheckCore
+ - Firebase
+ - FirebaseAppCheck
+ - FirebaseAppCheckInterop
+ - FirebaseAuth
+ - FirebaseAuthInterop
+ - FirebaseCore
+ - FirebaseCoreExtension
+ - FirebaseCoreInternal
+ - GoogleUtilities
+ - GTMSessionFetcher
+ - PromisesObjC
+ - RecaptchaInterop
+
+EXTERNAL SOURCES:
+ firebase_app_check:
+ :path: ".symlinks/plugins/firebase_app_check/ios"
+ firebase_auth:
+ :path: ".symlinks/plugins/firebase_auth/ios"
+ firebase_core:
+ :path: ".symlinks/plugins/firebase_core/ios"
+ Flutter:
+ :path: Flutter
+
+SPEC CHECKSUMS:
+ AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
+ Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e
+ firebase_app_check: 61fb3578a0761c806533482aca240a2d5cc5b5ef
+ firebase_auth: 9225db04db5d8e3b46dc8940e04bc6aec6833e27
+ firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594
+ FirebaseAppCheck: 73721d98fa29cf199da6004e57715cbaddd49651
+ FirebaseAppCheckInterop: f734c802f21fe1da0837708f0f9a27218c8a4ed0
+ FirebaseAuth: 4a2aed737c84114a9d9b33d11ae1b147d6b94889
+ FirebaseAuthInterop: 858e6b754966e70740a4370dd1503dfffe6dbb49
+ FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3
+ FirebaseCoreExtension: 7e1f7118ee970e001a8013719fb90950ee5e0018
+ FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6
+ Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
+ GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
+ GTMSessionFetcher: 02d6e866e90bc236f48a703a041dfe43e6221a29
+ PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
+ RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba
+
+PODFILE CHECKSUM: 53a6aebc29ccee84c41f92f409fc20cd4ca011f1
+
+COCOAPODS: 1.16.2
diff --git a/examples/express_chat/ios/Runner.xcodeproj/project.pbxproj b/examples/express_chat/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..6b5a68502
--- /dev/null
+++ b/examples/express_chat/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,758 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 13BE413A7A6F0715D37BE240 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5C343F7D99DDE3FFA47912B5 /* GoogleService-Info.plist */; };
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 648F713DD7F9A1E19447DA35 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2423408A58C6DDAF60EB235 /* Pods_Runner.framework */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 93C0D28C73168168CD7FFE52 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EA1AA490C057AD827AF63AC /* Pods_RunnerTests.framework */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 175B4A03834CA8F680CD2202 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 4B7DABC696DF2988D9D7A1D4 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 5C343F7D99DDE3FFA47912B5 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; };
+ 6EA1AA490C057AD827AF63AC /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 91A4FAAFD9106577D5E76C9A /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 9EB25C26CF76E8A920F22D5A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ A582BB0C4D57BE8171CA4C04 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
+ E2423408A58C6DDAF60EB235 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ F95ED38A4EF475EE27E888DC /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 648F713DD7F9A1E19447DA35 /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ C7883C98504D9C01B0C0B8DB /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 93C0D28C73168168CD7FFE52 /* Pods_RunnerTests.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 0FF38C0DE60523A00C090E29 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 238CBA884696395134813285 /* Pods-Runner.debug.xcconfig */,
+ 5E3B4A8BDEF2FF6E6FC6EDCC /* Pods-Runner.release.xcconfig */,
+ 508DF401F69417764E1E5B66 /* Pods-Runner.profile.xcconfig */,
+ 6EEB7E7FB6E5A6E29821B663 /* Pods-RunnerTests.debug.xcconfig */,
+ 9A097EFF4049C4AE69A97A4F /* Pods-RunnerTests.release.xcconfig */,
+ 429904E3B2952C32432502DE /* Pods-RunnerTests.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
+ 331C8082294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C807B294A618700263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "";
+ };
+ 47AB846AF9F050FE2C708492 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ D8CE641C45D7677CAFE8A9D5 /* Pods_Runner.framework */,
+ 4986175F9B388CE631CC1EC2 /* Pods_RunnerTests.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ 331C8082294A63A400263BE5 /* RunnerTests */,
+ 5C343F7D99DDE3FFA47912B5 /* GoogleService-Info.plist */,
+ 9E383EE76196043F53C0F088 /* Pods */,
+ BC1235CD57B2C51D54A332BA /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ 9E383EE76196043F53C0F088 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 9EB25C26CF76E8A920F22D5A /* Pods-Runner.debug.xcconfig */,
+ 175B4A03834CA8F680CD2202 /* Pods-Runner.release.xcconfig */,
+ 4B7DABC696DF2988D9D7A1D4 /* Pods-Runner.profile.xcconfig */,
+ A582BB0C4D57BE8171CA4C04 /* Pods-RunnerTests.debug.xcconfig */,
+ 91A4FAAFD9106577D5E76C9A /* Pods-RunnerTests.release.xcconfig */,
+ F95ED38A4EF475EE27E888DC /* Pods-RunnerTests.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
+ BC1235CD57B2C51D54A332BA /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ E2423408A58C6DDAF60EB235 /* Pods_Runner.framework */,
+ 6EA1AA490C057AD827AF63AC /* Pods_RunnerTests.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 331C8080294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ E042B4F72D8E0D6A85A07AB4 /* [CP] Check Pods Manifest.lock */,
+ 331C807D294A63A400263BE5 /* Sources */,
+ 331C807F294A63A400263BE5 /* Resources */,
+ C7883C98504D9C01B0C0B8DB /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 89ACBDE759E82DA6A7743559 /* [CP] Check Pods Manifest.lock */,
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ D3A66B593851E25A1A8795D6 /* [CP] Embed Pods Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = YES;
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 331C8080294A63A400263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ 331C8080294A63A400263BE5 /* RunnerTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 331C807F294A63A400263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ 13BE413A7A6F0715D37BE240 /* GoogleService-Info.plist in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 89ACBDE759E82DA6A7743559 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+ D3A66B593851E25A1A8795D6 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ E042B4F72D8E0D6A85A07AB4 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C807D294A63A400263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.simpleChat;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 331C8088294A63A400263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = A582BB0C4D57BE8171CA4C04 /* Pods-RunnerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.simpleChat.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Debug;
+ };
+ 331C8089294A63A400263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 91A4FAAFD9106577D5E76C9A /* Pods-RunnerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.simpleChat.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Release;
+ };
+ 331C808A294A63A400263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F95ED38A4EF475EE27E888DC /* Pods-RunnerTests.profile.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.simpleChat.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.simpleChat;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.simpleChat;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C8088294A63A400263BE5 /* Debug */,
+ 331C8089294A63A400263BE5 /* Release */,
+ 331C808A294A63A400263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/examples/express_chat/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/express_chat/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..919434a62
--- /dev/null
+++ b/examples/express_chat/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/examples/express_chat/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/express_chat/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/examples/express_chat/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/examples/express_chat/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/express_chat/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/examples/express_chat/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/examples/express_chat/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/express_chat/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 000000000..e3773d42e
--- /dev/null
+++ b/examples/express_chat/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/express_chat/ios/Runner.xcworkspace/contents.xcworkspacedata b/examples/express_chat/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..21a3cc14c
--- /dev/null
+++ b/examples/express_chat/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/examples/express_chat/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/express_chat/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/examples/express_chat/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/examples/express_chat/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/express_chat/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/examples/express_chat/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/examples/express_chat/ios/Runner/AppDelegate.swift b/examples/express_chat/ios/Runner/AppDelegate.swift
new file mode 100644
index 000000000..4cb238206
--- /dev/null
+++ b/examples/express_chat/ios/Runner/AppDelegate.swift
@@ -0,0 +1,17 @@
+// Copyright 2025 The Flutter Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Flutter
+import UIKit
+
+@main
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..d36b1fab2
--- /dev/null
+++ b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 000000000..dc9ada472
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 000000000..7353c41ec
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 000000000..797d452e4
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 000000000..6ed2d933e
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 000000000..4cd7b0099
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 000000000..fe730945a
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 000000000..321773cd8
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 000000000..797d452e4
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 000000000..502f463a9
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 000000000..0ec303439
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 000000000..0ec303439
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 000000000..e9f5fea27
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 000000000..84ac32ae7
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 000000000..8953cba09
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 000000000..0467bf12a
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 000000000..0bedcf2fd
--- /dev/null
+++ b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 000000000..89c2725b7
--- /dev/null
+++ b/examples/express_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/examples/express_chat/ios/Runner/Base.lproj/LaunchScreen.storyboard b/examples/express_chat/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 000000000..f2e259c7c
--- /dev/null
+++ b/examples/express_chat/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/express_chat/ios/Runner/Base.lproj/Main.storyboard b/examples/express_chat/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 000000000..f3c28516f
--- /dev/null
+++ b/examples/express_chat/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/express_chat/ios/Runner/Info.plist b/examples/express_chat/ios/Runner/Info.plist
new file mode 100644
index 000000000..61f7ff009
--- /dev/null
+++ b/examples/express_chat/ios/Runner/Info.plist
@@ -0,0 +1,49 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Simple Chat
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ simple_chat
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ CADisableMinimumFrameDurationOnPhone
+
+ UIApplicationSupportsIndirectInputEvents
+
+
+
diff --git a/examples/express_chat/ios/Runner/Runner-Bridging-Header.h b/examples/express_chat/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 000000000..02588e01d
--- /dev/null
+++ b/examples/express_chat/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1,5 @@
+// Copyright 2025 The Flutter Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "GeneratedPluginRegistrant.h"
diff --git a/examples/express_chat/ios/RunnerTests/RunnerTests.swift b/examples/express_chat/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 000000000..7d077cd5a
--- /dev/null
+++ b/examples/express_chat/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,16 @@
+// Copyright 2025 The Flutter Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/examples/express_chat/lib/chat_session.dart b/examples/express_chat/lib/chat_session.dart
new file mode 100644
index 000000000..aa74fd4be
--- /dev/null
+++ b/examples/express_chat/lib/chat_session.dart
@@ -0,0 +1,322 @@
+// Copyright 2025 The Flutter Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:genkit/genkit.dart' as genkit;
+import 'package:genui/genui.dart';
+import 'package:genui_express/genui_express.dart';
+import 'package:logging/logging.dart';
+
+import 'chrome_plugin_stub.dart'
+ if (dart.library.js_interop) 'chrome_plugin_web.dart';
+
+import 'primitives/app_mode.dart';
+import 'primitives/climbing/a2ui_components/climbing.dart';
+import 'primitives/message.dart';
+
+/// System prompts used to configure the chat sessions in this example.
+abstract final class Prompts {
+ Prompts._();
+
+ static const String summary =
+ 'You are a helpful assistant who chats with a user.';
+
+ static final String choicePicker =
+ '''
+When you need additional information from the user, try to use the component '${BasicCatalogItems.choicePicker.name}' to ask for it.
+''';
+
+ static final String textFieldFallback =
+ '''
+If there is no way to itemize all the options, either use the component '${BasicCatalogItems.textField.name}' or add option 'Other' to the '${BasicCatalogItems.choicePicker.name}'.
+''';
+
+ static final String climbingLocations =
+ '''
+IMPORTANT: Always immediately display the matching climbing locations using the rich 'ClimbingLocation' component card in your response. Do not ask the user for more information, preferences, or clarification first. Show the best matches (like beginner-friendly locations) immediately in A2UI Express syntax.
+IMPORTANT: You MUST surround the entire A2UI Express layout DSL block with the sentinel tags '' and '' to separate it from your conversational explanation.
+
+Available Climbing Locations (use these exact identifiers in ClimbingLocation):
+- 'kraft_boulders': Kraft Boulders (Outdoor, Free, Bouldering, Beginner/Intermediate/Advanced)
+- 'calico_hills_i': Calico Hills I (Outdoor, Paid, Lead/Top Rope, Beginner/Intermediate)
+- 'willow_springs': Willow Springs (Outdoor, Paid, Lead/Bouldering, Beginner/Intermediate)
+- 'origin_climbing_fitness': Origin Climbing + Fitness (Indoor, Paid, Bouldering/Lead/Top Rope, Beginner/Intermediate/Advanced)
+- 'the_refuge_climbing_center': The Refuge Climbing Center (Indoor, Paid, Bouldering, Beginner/Intermediate/Advanced)
+- 'red_rock_climbing_center': Red Rock Climbing Center (Indoor, Paid, Lead/Top Rope/Bouldering, Beginner/Intermediate/Advanced)
+- 'lone_mountain': Lone Mountain (Outdoor, Free, Lead, Beginner/Intermediate)
+
+When the user asks about climbing locations, you must choose the most appropriate location identifiers matching their query (e.g., beginner-friendly, indoor/outdoor, bouldering, free/paid) and display them.
+Always use the component named '${climbingLocationItem.name}' with the chosen identifier to display each location.
+You must compose the final layout tree under the reserved 'root' variable using Column or Row to hold the location components.
+Do not add any extra submit or confirmation buttons next to '${climbingLocationItem.name}' since it already contains a 'Learn more' button.
+
+Example:
+
+root = Column([loc1, loc2])
+loc1 = ClimbingLocation("kraft_boulders")
+loc2 = ClimbingLocation("lone_mountain")
+
+
+When the user clicks 'Learn more' on a '${climbingLocationItem.name}', a UI action named 'learnMoreAboutLocation' will be sent with the location's identifier and name in its context. Respond with detailed information about that specific location.
+''';
+}
+
+final Catalog _basicCatalog = BasicCatalogItems.asNoAssetCatalog(
+ systemPromptFragments: [Prompts.choicePicker, Prompts.textFieldFallback],
+);
+
+final Catalog _customCatalog = _basicCatalog.copyWith(
+ systemPromptFragments: [
+ Prompts.climbingLocations,
+ ..._basicCatalog.systemPromptFragments,
+ ],
+ newItems: [climbingLocationItem],
+);
+
+sealed class ChatSession extends ChangeNotifier {
+ ChatSession._();
+
+ factory ChatSession({
+ genkit.Genkit? ai,
+ genkit.ModelRef? model,
+ required AppMode mode,
+ }) {
+ final genkit.Genkit effectiveAi =
+ ai ?? genkit.Genkit(isDevEnv: false, plugins: getPlatformPlugins());
+ if (ai == null) {
+ GenuiExpressLocalModels.register(effectiveAi);
+ }
+ final genkit.ModelRef