Chuyển đến nội dung chính

Flutter Widget Previews are stable: a practical guide to @Preview

Widget Previews shipped as experimental back in 3.35, and a lot of developers tried it once, found it slow, and went back to hot reload. Flutter 3.47 marks it stable, with local build caching and an abstract theming API that makes the feature worth a second look. If you build design systems, component libraries, or anything with more than three visual states, this changes your inner loop.

The pitch is narrow and honest: the previewer renders individual widgets in isolation, without booting your app, navigating to the right screen, or faking the right state. That is a different job from hot reload, and it is the job hot reload has always been bad at.

Starting the previewer

From your IDE — Android Studio, IntelliJ, or VS Code — the previewer starts automatically; open the Flutter Widget Preview tab in the sidebar. From a terminal:

flutter widget-preview start

That launches a local server and opens a live preview environment in your browser. Builds are cached in a .widget_preview/ folder in your project — this cache is the reason startup got noticeably faster in 3.47, and it is the thing to add to .gitignore if your tooling has not already.

What you can annotate

@Preview comes from package:flutter/widget_previews.dart and can be applied to:

  • top-level functions returning a Widget or WidgetBuilder
  • static methods in a class returning a Widget or WidgetBuilder
  • public widget constructors and factories that take no required arguments

The simplest possible case:

import 'package:flutter/material.dart';
import 'package:flutter/widget_previews.dart';

@Preview(name: 'My Sample Text')
Widget mySampleText() {
  return const Text('Hello, World!');
}

No app, no MaterialApp, no route. Save the file and the preview updates.

Every parameter, and what it is actually for

The Preview class is small, which is a good sign. Here is the full surface:

ParameterTypeWhat it does
groupStringGroups related previews in the UI. Defaults to 'Default'.
nameString?Label shown beside the preview.
sizeSize?Artificial constraints applied to the widget.
textScaleFactordouble?Font scaling, for accessibility checks.
wrapperWidgetWrapper?Wraps the widget in a tree — scaffolds, providers, InheritedWidget.
themePreviewTheme?Returns Material and Cupertino theming data to apply.
brightnessBrightness?Initial light/dark brightness.
localizationsPreviewLocalizations?Localization configuration for the preview.

wrapper is the one that makes previews usable in a real codebase. Most widgets are not standalone — they expect a Scaffold, a theme, or an injected repository. You supply that once:

@Preview(
  name: 'Submit button — pressed state',
  group: 'Form Controls',
  size: Size(240, 56),
  textScaleFactor: 1.5,
  wrapper: _inScaffold,
)
Widget submitButtonPreview() => const SubmitButton(isBusy: true);

Widget _inScaffold(Widget child) => MaterialApp(
      home: Scaffold(body: Center(child: child)),
    );

Rendering the same widget many ways

This is where previews beat hot reload outright. Stack annotations to get one widget rendered under several configurations at once:

@Preview(group: 'Brightness', name: 'Light', brightness: Brightness.light)
@Preview(group: 'Brightness', name: 'Dark', brightness: Brightness.dark)
Widget buttonPreview() => const ButtonShowcase();

When the same three or four variants repeat across dozens of components, promote them to a MultiPreview:

final class MultiBrightnessPreview extends MultiPreview {
  const MultiBrightnessPreview();

  @override
  List<Preview> get previews => const [
        Preview(group: 'Brightness', name: 'Light', brightness: Brightness.light),
        Preview(group: 'Brightness', name: 'Dark', brightness: Brightness.dark),
      ];
}

@MultiBrightnessPreview()
Widget buttonPreview() => const ButtonShowcase();

Baking your design system into a custom annotation

The abstract theme API added in 3.47 is the piece that makes this scale. Rather than repeating theme: on every preview, subclass Preview and supply the builder once:

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(),
    );
  }
}

Now every @MyCustomPreview(...) in the codebase renders against your real tokens. PreviewThemeData carries both Material and Cupertino data, which matters more in 3.47 than it used to — with the design system moving into standalone material_ui and cupertino_ui packages, previews are a cheap way to see whether a component actually survives both.

There is also transform(), which lets a custom annotation rewrite the preview at runtime — useful for prefixing names or swapping themes across a whole class of previews without touching call sites.

Previews are not golden tests

Worth being blunt about this, because teams confuse the two. Previews are an authoring tool: fast, visual, human-in-the-loop, no assertions. Golden tests are a regression tool: slow, headless, and they fail your CI when a pixel moves. Previews do not replace goldens, and a preview passing means nothing except that it rendered.

The useful pairing is previews while you build the component, goldens once its states are settled.

The limitations you will hit

The previewer runs in a web-based environment, which sets hard boundaries:

  1. No native plugins, and no dart:io or dart:ffi. Anything touching the filesystem, platform channels, or FFI will not render — inject a fake through wrapper instead.
  2. Callback arguments must be public and constant. Private closures will not be picked up.
  3. Asset paths must be package-based: use 'packages/my_package_name/assets/my_image.png', not 'assets/my_image.png'.
  4. Unconstrained widgets are automatically clamped to roughly 50% of the previewer’s height and width. Pass size when that squashes your layout.
  5. Single project or Pub workspace only. Multi-project IDE support is still being investigated, so a large monorepo may not light up entirely.

Adopting it without a rewrite

  1. Upgrade to 3.47 and run flutter widget-preview start once to confirm the previewer builds your project.
  2. Add .widget_preview/ to .gitignore.
  3. Pick one leaf component — a button, a badge, a list tile — and add a single @Preview. Do not start with a screen.
  4. Write one wrapper that installs your app’s theme and providers, and reuse it.
  5. Promote your recurring variants (light/dark, text scale 1.0/2.0, LTR/RTL) into a MultiPreview.
  6. Subclass Preview with your design system’s PreviewThemeData and standardise on that annotation.
  7. Add goldens for the states you now know are correct.

The bottom line

Widget Previews stopped being a demo in 3.47. The caching makes startup tolerable, and the theme APIs are the difference between a toy and something a design-system team can standardise on. The limitations are real — no native code, package-based asset paths, single-workspace — but they are limitations of scope, not of maturity. If you tried previews in 3.35 and shrugged, the honest advice is to try again: this is the release where the feature earns its place in the loop.


Originally published on FlutterCook. Read the latest version there — that copy is the one kept up to date.

Nhận xét

Bài đăng phổ biến từ blog này

Thiết kế giao diện với DotNetBar (Phần 1)

Đây là phiên bản DotNetBar hỗ trợ C# và Visual Basic https://www.dropbox.com/s/wx80jpvgnlrmtux/DotNetBar.rar  , phiên bản này hỗ trợ giao diện Metro cực kỳ “dễ thương” Các bạn load về và cài đặt, khi cài đặt xong sẽ có source code mẫu của tất cả các control. Để sử dụng được các control của DotNetBar các bạn nhớ add item vào controls box. Thiết kế giao diện với DotNetBar, giao diện sẽ rất đẹp. Link các video hướng dẫn chi tiết cách sử dụng và coding: http://www.devcomponents.com/dotnetbar/movies.aspx Hiện tại DotNetBar có rất nhiều công cụ cực mạnh, trong đó có 3 công cụ dưới đây: DotNetBar for Windows Forms Requires with Visual Studio 2003, 2005, 2008, 2010 or 2012.   DotNetBar for WPF Requires with Visual Studio 2010 or 2012 and Windows Presentation Foundation.   DotNetBar for Silverlight Requires with Visual Studio 2010 or 2012 and Silverlight. Dưới đây là một số hình ảnh về các control trong DotnetBar.   Metro User Interface  controls with Metro Tiles, toolba...

5 concepts every Flutter dev should know

  Phụ lục: State management architecture Testing IDE Shortcuts Platform channel Maintaining a project Tôi đã làm việc với Flagship trong một thời gian dài, và đây là những điều mà tôi phát hiện ra là điều cần phải có đối với bất kỳ nhà phát triển Flagship nào, về tổng thể nó sẽ khiến bạn trở thành một nhà phát triển Flagship giỏi trong thời gian dài. 1. State management architecture Đây là một trong những chủ đề quan trọng nhất trong cộng đồng thiết bị rung, nó khá quan trọng nếu bạn muốn duy trì một dự án rung kích thước trung bình hoặc lớn. Nó sẽ giúp tạo một dự án suôn sẻ và thêm các tính năng mới một cách hoàn hảo.  2. Testing Đây là một chủ đề duy nhất mà tôi không hiểu tại sao nó lại quan trọng trước đó trong sự nghiệp của tôi, nhưng khi tôi tiến lên trong sự nghiệp của mình và có kinh nghiệm với nhiều dự án và vấn đề xảy ra trong môi trường sản xuất. Tôi đã nhận ra một cách khó khăn, tại sao điều này lại quan trọng như vậy. Nếu bạn vẫn muốn có thêm lý do để cân nhắc thử...

Announcing Flutter 2

  Phụ lục: Flutter on the web Flutter 2 on desktops, foldables, and embedded devices The growing Flutter ecosystem Dart: The secret sauce behind Flutter Flutter 2: Available now Hôm nay, chúng tôi sẽ công bố Flutter 2: một bản nâng cấp lớn cho Flutter cho phép các nhà phát triển tạo các ứng dụng đẹp, nhanh chóng và di động cho bất kỳ nền tảng nào. Với Flutter 2, bạn có thể sử dụng cùng một cơ sở mã để gửi các ứng dụng gốc cho năm hệ điều hành: IOS, Android, Windows, macOS và Linux; cũng như trải nghiệm web nhắm mục tiêu các trình duyệt như Chrome, Firefox, Safari hoặc Edge. Flutter thậm chí có thể được nhúng vào ô tô, TV và thiết bị gia dụng thông minh, mang đến trải nghiệm di động và lan tỏa nhất cho thế giới điện toán xung quanh. Mục tiêu của chúng tôi là thay đổi cơ bản cách các nhà phát triển nghĩ về việc xây dựng ứng dụng, bắt đầu không phải với nền tảng bạn đang nhắm mục tiêu mà là với trải nghiệm bạn muốn tạo. Flutter cho phép bạn tạo ra những trải nghiệm tuyệt đẹp trong đó ...