| 1 | --- |
| 2 | name: flutter-add-widget-preview |
| 3 | description: Adds interactive widget previews to the project using the previews.dart system. Use when creating new UI components or updating existing screens to ensure consistent design and interactive testing. |
| 4 | metadata: |
| 5 | model: models/gemini-3.1-pro-preview |
| 6 | last_modified: Tue, 21 Apr 2026 20:05:23 GMT |
| 7 | --- |
| 8 | # Previewing Flutter Widgets |
| 9 | |
| 10 | ## Contents |
| 11 | - [Preview Guidelines](#preview-guidelines) |
| 12 | - [Handling Limitations](#handling-limitations) |
| 13 | - [Workflows](#workflows) |
| 14 | - [Examples](#examples) |
| 15 | |
| 16 | ## Preview Guidelines |
| 17 | |
| 18 | Use the Flutter Widget Previewer to render widgets in real-time, isolated from the full application context. |
| 19 | |
| 20 | - **Target Elements:** Apply the `@Preview` annotation to top-level functions, static methods within a class, or public widget constructors/factories that have no required arguments and return a `Widget` or `WidgetBuilder`. |
| 21 | - **Imports:** Always import `package:flutter/widget_previews.dart` to access the preview annotations. |
| 22 | - **Custom Annotations:** Extend the `Preview` class to create custom annotations that inject common properties (e.g., themes, wrappers) across multiple widgets. |
| 23 | - **Multiple Configurations:** Apply multiple `@Preview` annotations to a single target to generate multiple preview instances. Alternatively, extend `MultiPreview` to encapsulate common multi-preview configurations. |
| 24 | - **Runtime Transformations:** Override the `transform()` method in custom `Preview` or `MultiPreview` classes to modify preview configurations dynamically at runtime (e.g., generating names based on dynamic values, which is impossible in a `const` context). |
| 25 | |
| 26 | ## Handling Limitations |
| 27 | |
| 28 | Adhere to the following constraints when authoring previewable widgets, as the Widget Previewer runs in a web environment: |
| 29 | |
| 30 | - **No Native APIs:** Do not use native plugins or APIs from `dart:io` or `dart:ffi`. Widgets with transitive dependencies on `dart:io` or `dart:ffi` will throw exceptions upon invocation. Use conditional imports to mock or bypass these in preview mode. |
| 31 | - **Asset Paths:** Use package-based paths for assets loaded via `dart:ui` `fromAsset` APIs (e.g., `packages/my_package_name/assets/my_image.png` instead of `assets/my_image.png`). |
| 32 | - **Public Callbacks:** Ensure all callback arguments provided to preview annotations are public and constant to satisfy code generation requirements. |
| 33 | - **Constraints:** Apply explicit constraints using the `size` parameter in the `@Preview` annotation if your widget is unconstrained, as the previewer defaults to constraining them to approximately half the viewport. |
| 34 | |
| 35 | ## Workflows |
| 36 | |
| 37 | ### Creating a Widget Preview |
| 38 | Copy and track this checklist when implementing a new widget preview: |
| 39 | |
| 40 | - [ ] Import `package:flutter/widget_previews.dart`. |
| 41 | - [ ] Identify a valid target (top-level function, static method, or parameter-less public constructor). |
| 42 | - [ ] Apply the `@Preview` annotation to the target. |
| 43 | - [ ] Configure preview parameters (`name`, `group`, `size`, `theme`, `brightness`, etc.) as needed. |
| 44 | - [ ] If applying the same configuration to multiple widgets, extract the configuration into a custom class extending `Preview`. |
| 45 | |
| 46 | ### Interacting with Previews |
| 47 | Follow the appropriate conditional workflow to launch and interact with the Widget Previewer: |
| 48 | |
| 49 | **If using a supported IDE (Android Studio, IntelliJ, VS Code with Flutter 3.38+):** |
| 50 | 1. Launch the IDE. The Widget Previewer starts automatically. |
| 51 | 2. Open the "Flutter Widget Preview" tab in the sidebar. |
| 52 | 3. Toggle "Filter previews by selected file" at the bottom left if you want to view previews outside the currently active file. |
| 53 | |
| 54 | **If using the Command Line:** |
| 55 | 1. Navigate to the Flutter project's root directory. |
| 56 | 2. Run `flutter widget-preview start`. |
| 57 | 3. View the automatically opened Chrome environment. |
| 58 | |
| 59 | **Feedback Loop: Preview Iteration** |
| 60 | 1. Modify the widget code or preview configuration. |
| 61 | 2. Observe the automatic update in the Widget Previewer. |
| 62 | 3. If global state (e.g., static initializers) was modified: Click the global hot restart button at the bottom right. |
| 63 | 4. If only the local widget state needs resetting: Click the individual hot restart button on the specific preview card. |
| 64 | 5. Review errors in the IDE/CLI console -> fix -> repeat. |
| 65 | |
| 66 | ## Examples |
| 67 | |
| 68 | ### Basic Preview |
| 69 | ```dart |
| 70 | import 'package:flutter/widget_previews.dart'; |
| 71 | import 'package:flutter/material.dart'; |
| 72 | |
| 73 | @Preview(name: 'My Sample Text', group: 'Typography') |
| 74 | Widget mySampleText() { |
| 75 | return const Text('Hello, World!'); |
| 76 | } |
| 77 | ``` |
| 78 | |
| 79 | ### Custom Preview with Runtime Transformation |
| 80 | ```dart |
| 81 | import 'package:flutter/widget_previews.dart'; |
| 82 | import 'package:flutter/material.dart'; |
| 83 | |
| 84 | final class TransformativePreview extends Preview { |
| 85 | const TransformativePreview({ |
| 86 | super.name, |
| 87 | super.group, |
| 88 | }); |
| 89 | |
| 90 | PreviewThemeData _themeBuilder() { |
| 91 | return PreviewThemeData( |
| 92 | materialLight: ThemeData.light(), |
| 93 | materialDark: ThemeData.dark(), |
| 94 | ); |
| 95 | } |
| 96 | |
| 97 | @override |
| 98 | Preview transform() { |
| 99 | final originalPreview = super.transform(); |
| 100 | final builder = originalPreview.toBuilder(); |
| 101 | |
| 102 | builder |
| 103 | ..name = 'Transformed - ${originalPreview.name}' |
| 104 | ..theme = _themeBuilder; |
| 105 | |
| 106 | return builder.toPreview(); |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | @TransformativePreview(name: 'Custom Themed Button') |
| 111 | Widget myButton() => const ElevatedButton(onPressed: null, child: Text('Click')); |
| 112 | ``` |
| 113 | |
| 114 | ### MultiPreview Implementation |
| 115 | ```dart |
| 116 | import 'package:flutter/widget_previews.dart'; |
| 117 | import 'package:flutter/material.dart'; |
| 118 | |
| 119 | /// Creates light and dark mode previews automatically. |
| 120 | final class MultiBrightnessPreview extends MultiPreview { |
| 121 | const MultiBrightnessPreview({required this.name}); |
| 122 | |
| 123 | final String name; |
| 124 | |
| 125 | @override |
| 126 | List<Preview> get previews => const [ |
| 127 | Preview(brightness: Brightness.light), |
| 128 | Preview(brightness: Brightness.dark), |
| 129 | ]; |
| 130 | |
| 131 | @override |
| 132 | List<Preview> transform() { |
| 133 | final previews = super.transform(); |
| 134 | return previews.map((preview) { |
| 135 | final builder = preview.toBuilder() |
| 136 | ..group = 'Brightness' |
| 137 | ..name = '$name - ${preview.brightness!.name}'; |
| 138 | return builder.toPreview(); |
| 139 | }).toList(); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | @MultiBrightnessPreview(name: 'Primary Card') |
| 144 | Widget cardPreview() => const Card(child: Padding(padding: EdgeInsets.all(8.0), child: Text('Content'))); |
| 145 | ``` |