Flutter Widget Previewer
In this guide, you will learn how to use the Flutter Widget Previewer.
Overview
#With the Flutter Widget Previewer, you can see your widgets render in real-time, separate from a full app, in the Chrome browser. To start the previewer, show a widget in it, and customize a preview, see the following sections.
Opening the previewer
#IDEs
#As of Flutter 3.38, Android Studio, Intellij, and Visual Studio Code automatically start the Flutter Widget Previewer on launch.
Android Studio and Intellij
#To open the Widget Previewer in Android Studio or Intellij, open the "Flutter Widget Preview" tab in the sidebar:
Visual Studio Code
#To open the Widget Previewer in Visual Studio Code, open the "Flutter Widget Preview" tab in the sidebar:
Command line
#To start the Flutter Widget Previewer, navigate to your Flutter project's root directory and run the following command in your terminal. This will launch a local server and open a Widget Preview environment in Chrome that automatically updates based on changes to your project.
flutter widget-preview start
Preview a widget
#
After you've started the previewer, to look at a widget,
you must use the @Preview
annotation defined in
package:flutter/widget_previews.dart. This annotation
can be applied to:
-
Top-level functions that return a
WidgetorWidgetBuilder. -
Static methods within a class that return a
WidgetorWidgetBuilder. - Public Widget constructors and factories with no required arguments.
Here is a basic example of how to use the
@Preview annotation to preview a Text widget:
import 'package:flutter/widget_previews.dart';
import 'package:flutter/material.dart'; // For Material widgets
@Preview(name: 'My Sample Text')
Widget mySampleText() {
return const Text('Hello, World!');
}
Each preview instance provides various controls for
interacting with the previewed widget. From left to right:
Zoom in: Magnifies the widget in the preview.
-
Zoom out: Reduces the magnification of the widget in the preview.
-
Reset zoom: Returns the widget preview to its default zoom level.
-
Toggle between light and dark mode: Switches the preview's theme between a light and dark color scheme.
-
Perform a hot restart for the individual preview: Restarts only the specific widget preview, allowing changes to be applied quickly without restarting the entire application.
For the case where global state has been modified (for example, a static initializer has been changed), the entire widget previewer can be told to hot restart using the button at the bottom right of the environment.
Filter previews by selected file
#When viewing previews within an IDE, the widget previewer is configured to filter the set of previews based on the currently selected file:
To disable this behavior, toggle the "Filter previews by selected file" option at the bottom left of the environment.
Customize a preview
#
The @Preview
annotation has several parameters you can
use to customize the preview:
name: A descriptive name for the preview.-
group: A name used to group related previews together in the widget previewer. -
size: Artificial size constraints using aSizeobject. textScaleFactor: A custom font scale.-
wrapper: A function that wraps your previewed widget in a specific widget tree (for example, to inject application state into the widget tree with anInheritedWidget). -
theme: A function to provide Material and Cupertino theming data. brightness: The initial theme brightness.-
localizations: A function to apply a localization configuration.
Create custom preview annotations
#
To reduce the amount of boilerplate needed to define previews with
a common set of properties, the Preview
annotation class can be
extended to create custom preview annotations tailored for your project.
Here's an example of a custom preview annotation that provides theming data:
final class MyCustomPreview extends Preview {
const MyCustomPreview({
super.name,
super.group,
super.size,
super.textScaleFactor,
super.wrapper,
super.brightness,
super.localizations,
}) : super(theme: MyCustomPreview.themeBuilder);
static PreviewThemeData themeBuilder() {
return PreviewThemeData(
materialLight: ThemeData.light(),
materialDark: ThemeData.dark(),
);
}
}
Extending the Preview
annotation class also allows for overriding
the Preview.transform()
method. This method is invoked by the widget previewer
and can be used to modify the preview at runtime, allowing for preview
configurations that would not otherwise be possible in a const context:
final class TransformativePreview extends Preview {
const TransformativePreview({
super.name,
super.group,
super.size,
super.textScaleFactor,
super.wrapper,
super.brightness,
super.localizations,
});
// Note: this is no longer public or static as it's injected
// at runtime when transform() is invoked.
PreviewThemeData _themeBuilder() {
return PreviewThemeData(
materialLight: ThemeData.light(),
materialDark: ThemeData.dark(),
);
}
@override
Preview transform() {
final originalPreview = super.transform();
// Create's a PreviewBuilder that can be used to modify
// the preview contents.
final builder = originalPreview.toBuilder();
builder
..name = 'Transformed - ${originalPreview.name}'
..theme = _themeBuilder;
// Return the updated Preview instance.
return builder.toPreview();
}
}
Creating multiple preview configurations
#
Creating multiple previews with different configurations can be as
simple as applying multiple @Preview
annotations to a single
function or constructor:
@Preview(
group: 'Brightness',
name: 'Example - light',
brightness: Brightness.light,
)
@Preview(
group: 'Brightness',
name: 'Example - dark',
brightness: Brightness.dark,
)
Widget buttonPreview() => const ButtonShowcase();
To simplify creating multiple previews with common configurations, you
can extend the MultiPreview
to create a custom annotation that creates
multiple previews. The following MultiPreview
creates
the same two previews as the previous example:
/// Creates light and dark mode previews.
final class MultiBrightnessPreview extends MultiPreview {
const MultiBrightnessPreview();
@override
List<Preview> get previews => const [
Preview(
group: 'Brightness',
name: 'Example - light',
brightness: Brightness.light,
),
Preview(
group: 'Brightness',
name: 'Example - dark',
brightness: Brightness.dark,
),
];
}
@MultiBrightnessPreview()
Widget buttonPreview() => const ButtonShowcase();
Like Preview,
MultiPreview
also provides a
MultiPreview.transform()
method to perform transformations
on each preview at runtime:
/// Creates light and dark mode previews.
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() {
final previews = super.transform();
return previews.map((preview) {
final builder = preview.toBuilder()
..group = 'Brightness'
// Building names based on values provided to the annotation
// isn't possible within a constant constructor. However,
// there's no such restriction when building a Preview at
// runtime.
..name = '$name - ${preview.brightness!.name}';
return builder.toPreview();
}).toList();
}
}
@MultiBrightnessPreview(name: 'Example')
Widget buttonPreview() => const ButtonShowcase();
Restrictions and limitations
#The Flutter Widget Previewer has certain restrictions you should be aware of:
-
Public callback names: All callback arguments provided to preview annotations must be public and constant. This is required for the previewer's code generation implementation to work correctly.
-
Unsupported APIs: Native plugins and any APIs from the
dart:ioordart:ffilibraries are not supported. This is because the widget previewer is built with Flutter Web, which doesn't have access to the underlying native platform APIs. While web plugins might work when using Chrome, there is no guarantee that they will work within other environments, such as when embedded in IDEs.Widgets with transitive dependencies on
dart:iowill load correctly, but all APIs fromdart:iowill throw an exception when invoked. Widgets with transitive dependencies ondart:ffiwill fail to load completely (#166431).See the Dart documentation on conditional imports for details on how to structure your application to cleanly support platform-specific libraries when targeting multiple platforms.
-
Asset paths: When using
fromAssetAPIs fromdart:uito load resources, you must use package-based paths instead of direct local paths. This ensures that the assets can be correctly located and loaded within the previewer's web environment. For example, use'packages/my_package_name/assets/my_image.png'instead of'assets/my_image.png'. -
Unconstrained widgets: Unconstrained widgets are automatically constrained to approximately half the height and width of the widget previewer. This behavior is likely to change in the future, so constraints should be applied using the
sizeparameter when possible. -
Multi-project support in IDEs: The widget previewer currently only supports displaying previews contained within a single project or Pub workspace. We’re actively investigating options to support IDE sessions with multiple Flutter projects (#173550).
Unless stated otherwise, the documentation on this site reflects Flutter 3.35.5. Page last updated on 2025-11-7. View source or report an issue.