Contents
- Preview Guidelines
- Handling Limitations
- IDE and CLI Integration
- Custom Annotations
- Workflow: Adding a Widget Preview
- Examples
Preview Guidelines
Use the Flutter Widget Previewer to render widgets in real-time, isolated from the full application context.
- Target Elements: Apply the
@Previewannotation to:- Top-level functions returning
Widget - Static methods within a class returning
Widget - Public widget constructors/factories with no required arguments
- Top-level functions returning
- Import: Always import
package:flutter/widget_previews.dart. - Multiple Configurations: Apply multiple
@Previewannotations to a single target for multiple preview instances (e.g., light/dark mode). - Naming: Use
nameandgroupparameters for organized preview panels. - Sizing: Apply explicit constraints using the
sizeparameter if the widget is unconstrained — the previewer defaults to approximately half the viewport.
Handling Limitations
The Widget Previewer runs in a web environment. Adhere to these constraints:
| Limitation | Impact | Workaround |
|---|---|---|
No dart:io |
File system, sockets unavailable | Use conditional imports to mock |
No dart:ffi |
Native code won’t execute | Stub native calls in preview mode |
| Asset paths | dart:ui fromAsset requires package paths |
Use packages/my_package/assets/... |
| Callbacks | Must be public and constant | No closures in annotation params |
| Unconstrained widgets | May render incorrectly | Set size parameter in @Preview |
IDE and CLI Integration
IDE (Android Studio, IntelliJ, VS Code with Flutter 3.38+)
- Launch the IDE. The Widget Previewer starts automatically.
- Open the “Flutter Widget Preview” tab in the sidebar.
- Toggle “Filter previews by selected file” at the bottom left to view previews outside the active file.
Command Line
flutter widget-preview start
Opens a Chrome environment with live previews.
Feedback Loop
- Modify the widget code or preview configuration.
- Observe the automatic update in the Widget Previewer.
- If global state was modified → click global hot restart (bottom right).
- If only local widget state needs resetting → click individual hot restart on the preview card.
- Review errors in the IDE/CLI console → fix → repeat.
Custom Annotations
Extending Preview
Create reusable preview configurations by extending the Preview class:
import 'package:flutter/widget_previews.dart';
import 'package:flutter/material.dart';
final class ThemedPreview extends Preview {
const ThemedPreview({super.name, super.group});
PreviewThemeData _themeBuilder() {
return PreviewThemeData(
materialLight: ThemeData.light(),
materialDark: ThemeData.dark(),
);
}
@override
Preview transform() {
final originalPreview = super.transform();
final builder = originalPreview.toBuilder()
..name = 'Themed - ${originalPreview.name}'
..theme = _themeBuilder;
return builder.toPreview();
}
}
@ThemedPreview(name: 'Primary Button')
Widget primaryButton() => const ElevatedButton(onPressed: null, child: Text('Click'));
MultiPreview for Brightness Variants
final class MultiBrightnessPreview extends MultiPreview {
const MultiBrightnessPreview({required this.name});
final String name;
@override
List<Preview> get previews => const [
Preview(brightness: Brightness.light),
Preview(brightness: Brightness.dark),
];
@override
List<Preview> transform() {
return super.transform().map((preview) {
final builder = preview.toBuilder()
..group = 'Brightness'
..name = '$name - ${preview.brightness!.name}';
return builder.toPreview();
}).toList();
}
}
@MultiBrightnessPreview(name: 'User Card')
Widget userCard() => const Card(
child: Padding(padding: EdgeInsets.all(16), child: Text('John Doe')),
);
Workflow: Adding a Widget Preview
Task Progress
- [ ] Step 1: Import
package:flutter/widget_previews.dart. - [ ] Step 2: Identify valid target (top-level function, static method, or no-arg constructor).
- [ ] Step 3: Apply
@Previewannotation withname,group,sizeparams. - [ ] Step 4: If config is reused across widgets → extract into custom
Previewsubclass. - [ ] Step 5: Launch previewer (IDE tab or
flutter widget-preview start). - [ ] Step 6: Iterate — modify widget → observe auto-update → fix errors → repeat.
Examples
Basic Preview
import 'package:flutter/widget_previews.dart';
import 'package:flutter/material.dart';
@Preview(name: 'Greeting Text', group: 'Typography')
Widget greetingText() {
return const Text('Hello, World!', style: TextStyle(fontSize: 24));
}
Preview with Size Constraints
@Preview(
name: 'Login Form',
group: 'Forms',
size: Size(400, 600),
)
Widget loginFormPreview() {
return const MaterialApp(home: LoginForm());
}
Preview on a Constructor
@Preview(name: 'Default Avatar')
class UserAvatar extends StatelessWidget {
const UserAvatar({super.key});
@override
Widget build(BuildContext context) {
return const CircleAvatar(radius: 40, child: Icon(Icons.person));
}
}