flutter-add-widget-preview

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Previewing Flutter Widgets

Flutter Widget 预览

Contents

目录

Preview Guidelines

预览指南

Use the Flutter Widget Previewer to render widgets in real-time, isolated from the full application context.
  • 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
    .
  • Imports: Always import
    package:flutter/widget_previews.dart
    to access the preview annotations.
  • Custom Annotations: Extend the
    Preview
    class to create custom annotations that inject common properties (e.g., themes, wrappers) across multiple widgets.
  • Multiple Configurations: Apply multiple
    @Preview
    annotations to a single target to generate multiple preview instances. Alternatively, extend
    MultiPreview
    to encapsulate common multi-preview configurations.
  • 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).
使用Flutter Widget Previewer实时渲染组件,与完整应用环境隔离。
  • 目标元素:
    @Preview
    注解应用于顶层函数、类中的静态方法,或无必填参数且返回
    Widget
    WidgetBuilder
    的公共组件构造函数/工厂方法。
  • 导入: 务必导入
    package:flutter/widget_previews.dart
    以获取预览注解。
  • 自定义注解: 继承
    Preview
    类创建自定义注解,为多个组件注入通用属性(如主题、包装器)。
  • 多配置: 为单个目标应用多个
    @Preview
    注解以生成多个预览实例。或者继承
    MultiPreview
    封装通用多预览配置。
  • 运行时转换: 在自定义
    Preview
    MultiPreview
    类中重写
    transform()
    方法,以在运行时动态修改预览配置(例如基于动态值生成名称,这在
    const
    上下文无法实现)。

Handling Limitations

限制处理

Adhere to the following constraints when authoring previewable widgets, as the Widget Previewer runs in a web environment:
  • No Native APIs: Do not use native plugins or APIs from
    dart:io
    or
    dart:ffi
    . Widgets with transitive dependencies on
    dart:io
    will throw exceptions upon invocation, and
    dart:ffi
    dependencies will fail to load. Use conditional imports to mock or bypass these in preview mode.
  • 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
    ).
  • Public Callbacks: Ensure all callback arguments provided to preview annotations are public and constant to satisfy code generation requirements.
  • 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.
编写可预览组件时需遵守以下约束,因为Widget Previewer运行在Web环境中:
  • 禁止原生API: 不要使用原生插件或
    dart:io
    dart:ffi
    中的API。依赖
    dart:io
    的组件在调用时会抛出异常,依赖
    dart:ffi
    的组件将无法加载。使用条件导入在预览模式下模拟或绕过这些依赖。
  • 资源路径: 通过
    dart:ui
    fromAsset
    API加载资源时,使用基于包的路径(例如
    packages/my_package_name/assets/my_image.png
    而非
    assets/my_image.png
    )。
  • 公共回调: 确保提供给预览注解的所有回调参数是公共且常量,以满足代码生成要求。
  • 约束设置: 如果你的组件无约束,使用
    @Preview
    注解中的
    size
    参数设置显式约束,因为预览器默认将其约束为约视口的一半大小。

Workflows

工作流程

Creating a Widget Preview

创建组件预览

Copy and track this checklist when implementing a new widget preview:
  • Import
    package:flutter/widget_previews.dart
    .
  • Identify a valid target (top-level function, static method, or parameter-less public constructor).
  • Apply the
    @Preview
    annotation to the target.
  • Configure preview parameters (
    name
    ,
    group
    ,
    size
    ,
    theme
    ,
    brightness
    , etc.) as needed.
  • If applying the same configuration to multiple widgets, extract the configuration into a custom class extending
    Preview
    .
实现新组件预览时,遵循以下检查清单:
  • 导入
    package:flutter/widget_previews.dart
  • 确定有效目标(顶层函数、静态方法或无参数公共构造函数)。
  • 为目标应用
    @Preview
    注解。
  • 根据需要配置预览参数(
    name
    group
    size
    theme
    brightness
    等)。
  • 如果要为多个组件应用相同配置,将配置提取为继承
    Preview
    的自定义类。

Interacting with Previews

与预览交互

Follow the appropriate conditional workflow to launch and interact with the Widget Previewer:
If using a supported IDE (Android Studio, IntelliJ, VS Code with Flutter 3.38+):
  1. Launch the IDE. The Widget Previewer starts automatically.
  2. Open the "Flutter Widget Preview" tab in the sidebar.
  3. Toggle "Filter previews by selected file" at the bottom left if you want to view previews outside the currently active file.
If using the Command Line:
  1. Navigate to the Flutter project's root directory.
  2. Run
    flutter widget-preview start
    .
  3. View the automatically opened Chrome environment.
Feedback Loop: Preview Iteration
  1. Modify the widget code or preview configuration.
  2. Observe the automatic update in the Widget Previewer.
  3. If global state (e.g., static initializers) was modified: Click the global hot restart button at the bottom right.
  4. If only the local widget state needs resetting: Click the individual hot restart button on the specific preview card.
  5. Review errors in the IDE/CLI console -> fix -> repeat.
遵循相应的条件工作流程启动并与Widget Previewer交互:
如果使用支持的IDE(Android Studio、IntelliJ、Flutter 3.38+版本的VS Code):
  1. 启动IDE,Widget Previewer会自动启动。
  2. 打开侧边栏中的“Flutter Widget Preview”标签页。
  3. 如果要查看当前活动文件之外的预览,点击左下角的“按所选文件过滤预览”开关。
如果使用命令行:
  1. 导航到Flutter项目的根目录。
  2. 运行
    flutter widget-preview start
  3. 查看自动打开的Chrome环境。
反馈循环:预览迭代
  1. 修改组件代码或预览配置。
  2. 在Widget Previewer中观察自动更新。
  3. 如果修改了全局状态(如静态初始化器):点击右下角的全局热重启按钮。
  4. 如果仅需重置本地组件状态:点击特定预览卡片上的单独热重启按钮。
  5. 在IDE/CLI控制台中查看错误 -> 修复 -> 重复上述步骤。

Examples

示例

Basic Preview

基础预览

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

(name: 'My Sample Text', group: 'Typography')
Widget mySampleText() {
  return const Text('Hello, World!');
}
dart
import 'package:flutter/widget_previews.dart';
import 'package:flutter/material.dart';

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

Custom Preview with Runtime Transformation

带运行时转换的自定义预览

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

final class TransformativePreview extends Preview {
  const TransformativePreview({
    super.name,
    super.group,
  });

  PreviewThemeData _themeBuilder() {
    return PreviewThemeData(
      materialLight: ThemeData.light(),
      materialDark: ThemeData.dark(),
    );
  }

  
  Preview transform() {
    final originalPreview = super.transform();
    final builder = originalPreview.toBuilder();
    
    builder
      ..name = 'Transformed - ${originalPreview.name}'
      ..theme = _themeBuilder;

    return builder.toPreview();
  }
}

(name: 'Custom Themed Button')
Widget myButton() => const ElevatedButton(onPressed: null, child: Text('Click'));
dart
import 'package:flutter/widget_previews.dart';
import 'package:flutter/material.dart';

final class TransformativePreview extends Preview {
  const TransformativePreview({
    super.name,
    super.group,
  });

  PreviewThemeData _themeBuilder() {
    return PreviewThemeData(
      materialLight: ThemeData.light(),
      materialDark: ThemeData.dark(),
    );
  }

  
  Preview transform() {
    final originalPreview = super.transform();
    final builder = originalPreview.toBuilder();
    
    builder
      ..name = 'Transformed - ${originalPreview.name}'
      ..theme = _themeBuilder;

    return builder.toPreview();
  }
}

(name: 'Custom Themed Button')
Widget myButton() => const ElevatedButton(onPressed: null, child: Text('Click'));

MultiPreview Implementation

MultiPreview 实现

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

/// Creates light and dark mode previews automatically.
final class MultiBrightnessPreview extends MultiPreview {
  const MultiBrightnessPreview({required this.name});

  final String name;

  
  List<Preview> get previews => const [
        Preview(brightness: Brightness.light),
        Preview(brightness: Brightness.dark),
      ];

  
  List<Preview> transform() {
    final previews = super.transform();
    return previews.map((preview) {
      final builder = preview.toBuilder()
        ..group = 'Brightness'
        ..name = '$name - ${preview.brightness!.name}';
      return builder.toPreview();
    }).toList();
  }
}

(name: 'Primary Card')
Widget cardPreview() => const Card(child: Padding(padding: EdgeInsets.all(8.0), child: Text('Content')));
dart
import 'package:flutter/widget_previews.dart';
import 'package:flutter/material.dart';

/// 自动创建亮色和暗色模式预览。
final class MultiBrightnessPreview extends MultiPreview {
  const MultiBrightnessPreview({required this.name});

  final String name;

  
  List<Preview> get previews => const [
        Preview(brightness: Brightness.light),
        Preview(brightness: Brightness.dark),
      ];

  
  List<Preview> transform() {
    final previews = super.transform();
    return previews.map((preview) {
      final builder = preview.toBuilder()
        ..group = 'Brightness'
        ..name = '$name - ${preview.brightness!.name}';
      return builder.toPreview();
    }).toList();
  }
}

(name: 'Primary Card')
Widget cardPreview() => const Card(child: Padding(padding: EdgeInsets.all(8.0), child: Text('Content')));