In Flutter 3.47, what is the Widget Previewer, how do you mark a widget with @Preview, and what can a preview not use?
answer
- stable in 3.47, experimental since 3.35
- package:flutter/widget_previews.dart
- top-level, static or no-arg constructor
- constant, public callbacks only
- built on Flutter web: no dart:io or plugins
basics
~20 sThe Widget Previewer renders single widgets outside the app, in the IDE or a browser. Annotate a public top-level function, static method or no-argument constructor with @Preview; it runs on Flutter web, so native plugins, dart:io and dart:ffi calls fail.
solid answer
~40 sThe **Widget Previewer**, stable in Flutter 3.47 after being experimental from 3.35, renders single widgets in real time outside the running app, in an IDE panel or in a browser via `flutter widget-preview start`. I import `package:flutter/widget_previews.dart` and put `@Preview(...)` on a public top-level function or static method that returns a `Widget` or `WidgetBuilder`, or on a public widget constructor or factory with no required arguments. `Preview` takes `name`, `group` (default `'Default'`), `size`, `textScaleFactor`, `wrapper`, `theme`, `brightness` and `localizations`; every value must be constant and callbacks public. Several annotations, or a `MultiPreview` subclass, give light and dark variants. It is built on Flutter web, so native plugins and `dart:io` or `dart:ffi` calls throw.
code
dart · 23 linesimport 'package:flutter/material.dart';
import 'package:flutter/widget_previews.dart';
class SettingsTile extends StatelessWidget {
const SettingsTile({super.key, required this.title});
final String title;
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(title),
trailing: const Icon(Icons.chevron_right),
);
}
}
// Public top-level function, so it can be passed as a constant wrapper.
Widget wrapInMaterial(Widget child) => Material(child: child);
@Preview(group: 'Settings', name: 'Tile - light', brightness: Brightness.light, wrapper: wrapInMaterial)
@Preview(group: 'Settings', name: 'Tile - dark', brightness: Brightness.dark, wrapper: wrapInMaterial)
Widget settingsTilePreview() => const SettingsTile(title: 'Notifications');go deeper
Know that @Preview from package:flutter/widget_previews.dart shows a widget in an IDE panel or browser without running the app, and that it became stable in Flutter 3.47.
Explain where the annotation may go (public top-level function, static method, no-arg constructor), the constant and public-callback rules, and the web-based limits on plugins and dart:io.
Show how you structure widgets so they preview cleanly: data passed in rather than fetched, conditional imports for platform code, wrapper functions for providers or Material, MultiPreview for theme matrices.
Weigh the previewer as a lightweight component catalogue next to the code against a separate showcase app, and what that means for design review and keeping widgets free of platform calls.
## What it is The **Flutter Widget Previewer** lets you render, inspect and iterate on **individual widgets** without launching the full app or navigating to the screen that uses them. It was introduced as experimental in Flutter 3.35 and is **stable as of Flutter 3.47**. Android Studio, IntelliJ and VS Code start it automatically and show it in a *Flutter Widget Preview* sidebar tab; from a terminal, `flutter widget-preview start` in the project root launches a local server and opens the previews in a browser. The tool caches builds in a `.widget_preview/` folder in the project. ## Marking a widget for preview Import `package:flutter/widget_previews.dart` and apply the `@Preview` annotation. It can go on: - a **public top-level function** that returns a `Widget` or a `WidgetBuilder`; - a **static method** in a class that returns either of those; - a **public widget constructor or factory** with no required arguments. Each preview gets its own controls: zoom, light and dark toggle, and a **hot restart of just that preview**. A separate button restarts the whole previewer when global state such as a static initializer changed. ## Configuring a preview | `Preview` parameter | Purpose | |---|---| | `name` | a label shown next to the preview | | `group` | groups related previews; defaults to `'Default'` | | `size` | artificial constraints, a `Size` (use `Size.fromWidth` or `Size.fromHeight` for one axis) | | `textScaleFactor` | a font scale for the preview | | `wrapper` | a `Widget Function(Widget)` that wraps the preview, for example to inject state or a `Material` ancestor | | `theme` | a function returning `PreviewThemeData` | | `brightness` | initial light or dark | | `localizations` | a function returning a localization configuration | Rules that trip people up: 1. **All annotation values must be constant**, and callback arguments must be **public** (and static), because the previewer generates code that references them. 2. **Several `@Preview` annotations** on one function produce several previews, for example light and dark. 3. To avoid repeating those, extend **`MultiPreview`** and return a list of `Preview`s, or extend `Preview` itself to bake in shared settings. Overriding `transform()` builds previews at runtime, where values no longer have to be constant. ## What a preview cannot use The previewer is **built with Flutter web**, which drives its limits: - **Native plugins** and any API from `dart:io` or `dart:ffi` are unsupported. Code that merely depends on them transitively still loads, but calling those APIs throws. Conditional imports keep such code out of previewed widgets. - **Asset paths** loaded through `dart:ui` `fromAsset` APIs must be package-based (`packages/my_app/assets/logo.png`). - **Unconstrained widgets** are currently constrained to about half the previewer's width and height; set `size` rather than relying on that. - IDE sessions show previews from a **single project or pub workspace**. ## Where it fits next to the inspector - The **inspector** examines a widget inside the running app, with its real ancestors, state and data. - The **previewer** examines a widget in isolation, fed by whatever the annotation supplies. In practice you design and tune a component in the previewer (themes, text scale, sizes) and use the inspector to debug how it behaves once placed in a real screen. Previews are also a lightweight catalogue of a design system's widgets that lives next to the code.
- A Flutter widget preview crashes when the widget reads a file with dart:io; why, and how do you fix it?The Widget Previewer runs on Flutter web, where `dart:io` and `dart:ffi` APIs throw when called. Move the file access out of the widget, pass the data in, or use a conditional import so the previewed code path uses a web-safe implementation.
- How do you avoid repeating the same light and dark @Preview annotations on every Flutter widget?Extend `MultiPreview` in `package:flutter/widget_previews.dart`, return a constant list of `Preview` objects from its `previews` getter, and annotate functions with your subclass. You can also extend `Preview` to bake in shared settings such as a theme, and override `transform()` to build names or themes at runtime.
saying these in an interview costs you the question
- Any private helper function can be used as a Preview wrapper.
- The Widget Previewer runs previews on the connected Android or iOS device.
- @Preview works on any constructor, including ones with required arguments.
- Native plugins work in previews because the previewer uses the app's platform code.
- The Widget Previewer has been stable since it first appeared in 3.35.