blob: 6836b266ba2fd0d02f96d8d683fa47c2d80537b3 [file] [view]
# Examples
Examples are Flutter projects offered as templates on the prompt page. The frontend serves them from TAR files in `packages/frontend/web/examples/`.
Most examples also have checked-in source directories under `examples/`. Very large examples may be checked in as a prebuilt TAR only, with their local source directory intentionally ignored.
## Table of Contents
- [Structure](#structure)
- [YAML Manifest](#yaml-manifest)
- [Bundling](#bundling)
- [Runtime Behavior](#runtime-behavior)
- [Adding a New Example](#adding-a-new-example)
- Examples
- [Counter App](#counter-app)
- [Overflow Debug](#overflow-debug)
- [Rebuild Weather App](#rebuild-weather-app)
- [Wonderous App](#wonderous-app)
## Structure
```text
examples/
├── examples.yaml # Source of truth for the template list
├── simple_counter/ # Normal checked-in example source
│ ├── pubspec.yaml
│ └── lib/
│ ├── main.dart
│ └── counter.dart
├── overflow_debug/
│ ├── pubspec.yaml
│ └── lib/
│ └── main.dart
├── rebuild_weather_app/
│ ├── pubspec.yaml
│ └── weather_app_screenshot.jpg # Image attached to the prompt
└── flutter-wonderous-app/ # Local-only, ignored; TAR is checked in
packages/frontend/web/examples/
├── examples.json # Generated manifest served by frontend
├── simple_counter.tar
├── overflow_debug.tar
├── rebuild_weather_app.tar
└── flutter-wonderous-app.tar # Prebundled Wonderous template
```
## YAML Manifest
The template list is defined in `examples/examples.yaml`. Each entry defines:
| Field | Required | Description |
| -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | | Template id. For source-backed examples this matches `examples/<id>/`; for prebundled examples it matches `packages/frontend/web/examples/<id>.tar`. |
| `title` | | Display name shown on the prompt page. |
| `prompt` | | Optional prompt pre-filled in the agent composer. |
| `image` | | Optional image file name inside the example TAR. |
```yaml
examples:
- id: simple_counter
title: Counter Template
- id: overflow_debug
title: Overflow Debug
prompt: >-
My profile screen doesn't look right — some content seems to be cut off
or missing. Can you take a screenshot and help me figure out what's wrong?
- id: rebuild_weather_app
title: Rebuild Weather App
prompt: >-
Rebuild this weather app and verify your result with screenshots comparing it against the attached image.
Generate background images for the different weather conditions. Use mocks for the weather data.
image: weather_app_screenshot.jpg
- id: flutter-wonderous-app
title: Wonderous App
```
### Template behavior based on fields
The editor and agent panel are visible for every template. Optional manifest fields only control composer prefill:
| Has prompt? | Has image? | Behavior |
| ----------- | ---------- | -------------------------------------------------------------------------------------------------------------- |
| | | Opens the editor and agent panel. If `lib/main.dart` exists, the preview starts automatically after `pub get`. |
| | | Same as above, with the prompt pre-filled in the composer. |
| | | Same as above, with the prompt and image thumbnail pre-filled in the composer. |
The user reviews any pre-filled prompt and hits **Send** manually. Selecting a template does not automatically start an agent run.
## Bundling
The script `tool/bundle_examples.dart` reads `examples.yaml` and:
1. Generates `examples.json` in `packages/frontend/web/examples/` with `id`, `title`, optional `prompt`, and optional image metadata (`imageMimeType`, `imageFileName`).
2. Creates TAR files for examples whose source directories exist locally.
3. Keeps checked-in TARs for prebundled examples whose source directories are intentionally absent.
4. Tracks checksums in `.checksums.json`; unchanged source-backed examples are not re-bundled.
```bash
dart run tool/bundle_examples.dart
```
The generated files under `packages/frontend/web/examples/` are checked in.
If an example appears in `examples.yaml` but has neither a local source directory nor an existing checked-in TAR, bundling fails. This protects normal examples from silently disappearing while still allowing large prebundled templates such as Wonderous.
The bundler skips generated local Flutter artifacts (`build/`, `.dart_tool/`, `.flutter-plugins*`, and `pubspec.lock`) so local `flutter pub get`, `flutter analyze`, or generator output does not accidentally bloat a template TAR.
## Runtime Behavior
On startup the frontend fetches `examples/examples.json` and displays the templates on the prompt page. When a template is selected:
1. The corresponding TAR (`examples/<id>.tar`) is fetched and extracted into the workspace.
2. The project session opens with the editor and agent panel visible.
3. If the template has a `prompt` and/or `image`, those are pre-filled in the agent composer.
4. The frontend runs `pub get`.
5. If `lib/main.dart` exists, the preview starts automatically with initial preview validation skipped. This avoids blocking startup on stale LSP diagnostics while the imported workspace is still settling.
## Adding a New Example
### Code template with checked-in source
1. Create `examples/<id>/` with a `pubspec.yaml` and usually `lib/main.dart`.
2. Add an entry to `examples/examples.yaml`:
```yaml
- id: my_example
title: My Example
```
3. Run `dart run tool/bundle_examples.dart`.
4. Check in both the source directory and the generated files under `packages/frontend/web/examples/`.
### Prompt-driven template
1. Create `examples/<id>/` with at least a `pubspec.yaml`.
2. Optionally add code in `lib/` and/or an image file.
3. Add an entry to `examples/examples.yaml`:
```yaml
- id: my_example
title: My Example
prompt: >-
Describe what the agent should do...
image: screenshot.png # optional
```
4. Run `dart run tool/bundle_examples.dart`.
### Large prebundled template
Use this for templates that are too large to check in as source, such as Wonderous.
1. Create the source directory locally at `examples/<id>/`.
2. Add an entry to `examples/examples.yaml`.
3. Add the source directory to `.gitignore`.
4. Run `dart run tool/bundle_examples.dart`.
5. Check in the generated TAR, `examples.json`, and `.checksums.json`, but not the local source directory.
After that, other developers can run `dart run tool/bundle_examples.dart` without having the large local source directory. The script will keep using the checked-in TAR.
---
## Counter App
A minimal Flutter counter app. Demonstrates basic `StatefulWidget` usage with `setState`.
## Overflow Debug
A Flutter profile screen with intentional overflow and layout issues. The pre-filled prompt asks the agent to take screenshots, diagnose the problems, and fix them iteratively.
## Rebuild Weather App
An empty project with a screenshot of a weather app UI. The agent is asked to rebuild the app from the screenshot and generate background images for different weather conditions.
## Wonderous App
The [Wonderous App](https://wonderous.app) by gskinner is the most complex example. It is intentionally checked in as `packages/frontend/web/examples/flutter-wonderous-app.tar`; the local source directory `examples/flutter-wonderous-app/` is ignored because it is large.
### Dynamic calls
Arbitrary module-to-module dynamic calls are currently not supported in dynamic interfaces (b/535465177). Unsupported dynamic calls have been removed from the Wonderous app.
### Assets
The preview sandbox serves project assets through a custom `DefaultAssetBundle` that forwards asset keys to the host workspace. Wonderous previously decoded illustration image dimensions with:
```dart
rootBundle.load(imgPath)
```
That bypassed the sandbox bundle and made Flutter Web fall back to HTTP requests such as `flutter/assets/assets/images/...`, which 404 against the static SDK asset directory. `lib/ui/wonder_illustrations/common/illustration_piece.dart` now uses:
```dart
DefaultAssetBundle.of(context).load(imgPath)
```
This keeps those manual image loads on the same sandbox asset path as `Image.asset(...)`.
### Why plugins do not auto-register in the sandbox
In a normal `flutter build web`, Flutter auto-generates `generated_plugin_registrant.dart` which registers web plugins. The preview sandbox skips the Flutter build step and compiles raw Dart through DDC (Dart Dev Compiler). Federated web plugins therefore need manual registration with the same `registerWith` pattern.
DDC also does not tree-shake unreachable imports in the same way a release `dart2js` build does. For this template, web-only and native-only code needs to be separated with conditional imports such as `if (dart.library.js_interop)`, not just runtime `kIsWeb` checks.
### Plugin registration
Registration happens in `lib/register_plugins_web.dart`, loaded via conditional import in `main.dart`:
```dart
// main.dart
import 'package:wonders/register_plugins_stub.dart'
if (dart.library.js_interop) 'package:wonders/register_plugins_web.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
registerWebPlugins(); // no-op on native, registers on web
// ...
}
```
```dart
// register_plugins_web.dart
void registerWebPlugins() {
final registrar = webPluginRegistrar;
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
registrar.registerMessageHandler();
}
```
```dart
// register_plugins_stub.dart
void registerWebPlugins() {} // no-op on native
```
### Problematic packages and fixes
#### `shared_preferences` — fixed via `registerWith`
| | Detail |
| ------------ | -------------------------------------------------------------------------------------- |
| Problem | `MissingPluginException` because the web plugin is not auto-registered in the sandbox. |
| Root cause | Missing generated plugin registrant. |
| Fix | `SharedPreferencesPlugin.registerWith(registrar)` in `register_plugins_web.dart`. |
| Web behavior | Uses browser `localStorage` through `shared_preferences_web`. |
#### `url_launcher` — fixed via `registerWith` and iframe sandbox flags
| | Detail |
| ---------- | ------------------------------------------------------------------------------------------------------------------------- |
| Problem | `MissingPluginException`, then links did not open. |
| Root cause | Missing plugin registration plus the preview iframe blocking `window.open()`. |
| Fix | `UrlLauncherPlugin.registerWith(registrar)` and `allow-popups` on the preview iframe sandbox attribute in `sandbox.dart`. |
#### `youtube_player_iframe` — currently bypassed on web
| | Detail |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Problem | The WebView-backed YouTube path can hit `createPlatformNavigationDelegate is not implemented on the current platform` in the sandbox if the wrong WebView platform implementation is registered or compiled into the web path. |
| Current fix | `fullscreen_video_viewer.dart` uses a conditional import. Web loads `video_player_web.dart`, which embeds YouTube directly with `HtmlElementView.fromTagName('iframe')`; native loads `video_player_stub.dart`, which keeps the original `YoutubePlayer` widget. |
| Why this is safe | A YouTube embed is just an iframe pointing at a YouTube embed URL. For the Wonderous preview, the direct iframe gives the same user-visible result with less plugin machinery. |
| Alternative | It may be possible to use `youtube_player_iframe` on web by manually registering `youtube_player_iframe_web`'s `WebYoutubePlayerIframePlatform` instead of `webview_flutter_web`'s `WebWebViewPlatform`. That plugin class is not exported from the package's public library today, so this would require either a `src/` import or an upstream/exported shim. If we try it, keep it behind a conditional web-only import and do not register `webview_flutter_web` globally. |
#### `webview_flutter` — bypassed on web
| | Detail |
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Problem | The generic `webview_flutter_web` implementation does not implement the full platform interface needed by the current resolved `webview_flutter_platform_interface`. |
| Fix | `fullscreen_web_view.dart` checks `kIsWeb` and falls back to `launchUrl()` on web, opening the link in a new tab. Native keeps the original `WebViewController` code. |
#### `google_maps_flutter` — stubbed
Requires Google Maps JavaScript API setup and a key. The Wonderous preview replaces maps with a "Maps not available in web preview" placeholder.
#### `home_widget` — stubbed
iOS/Android-only feature with no useful web equivalent for the preview. The template uses a no-op `NativeWidgetService`.
### Preview limitations
- Maps are placeholders.
- Home-screen widget integration is disabled.
- About links open in a new browser tab.
- YouTube videos currently use a direct iframe embed instead of the `youtube_player_iframe` widget on web.