mcpField
F
flutter-add-widget-preview

flutter-add-widget-preview

github.com/flutter开发3.1k37.2k 次安装2 天前更新
代码仓库

关于 flutter-add-widget-preview

此技能指导开发者使用 Flutter Widget Previewer 隔离渲染 Flutter 组件,并配置尺寸、主题、本地化等预览环境。它涵盖注解、自定义预览及运行限制。

主要功能

  1. 01为组件添加 @Preview 注解
  2. 02配置尺寸、主题与本地化
  3. 03创建自定义及多配置预览
  4. 04处理 Web 预览环境限制

适用场景

  1. 01在应用上下文之外检查组件
  2. 02比较不同主题或字体缩放
  3. 03为多个组件复用预览配置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/flutter/agent-plugins --skill flutter-add-widget-preview

安装后在 Claude Code 中输入/flutter-add-widget-preview确认已加载。

详细介绍

Flutter Widget 预览

目录

预览指南

使用 Flutter Widget Previewer 实时渲染 Widget,并将其与完整的应用上下文隔离。

  • 目标元素: 将 @Preview 注解应用于顶层函数、类中的静态方法,或无需必填参数且返回 Widget 或 WidgetBuilder 的公共 Widget 构造函数/工厂方法。
  • 导入: 始终导入 package:flutter/widget_previews.dart,以使用预览注解。
  • 自定义注解: 扩展 Preview 类,创建可为多个 Widget 注入常用属性(例如自定义主题、包装器和尺寸)的自定义注解。
  • 多个配置: 对单个目标应用多个 @Preview 注解,以生成多个预览实例。也可以扩展 MultiPreview,封装常用的多预览配置。
  • 运行时转换: 在自定义 Preview 或 MultiPreview 类中重写 transform() 方法,以便在运行时动态修改预览配置(例如根据参数动态生成名称,而这在 const 构造函数中无法实现)。

预览参数

@Preview 注解支持以下参数,用于定制预览环境:

  • name:预览实例的描述性名称。
  • group:用于在预览器中将相关预览归为一组的名称。
  • size:使用 Size 对象设置人工约束。
  • textScaleFactor:自定义字体缩放因子。
  • wrapper:一个函数,用于将预览的 Widget 包装在特定的 Widget 树中(例如注入状态或继承 Widget)。
  • theme:一个返回 PreviewThemeData 子类实例的函数,用于提供自定义主题,并支持按顺序叠加主题。
  • brightness:初始主题的 Brightness(Brightness.light 或 Brightness.dark)。
  • localizations:用于应用本地化配置的函数。

处理限制

由于 Widget Previewer 在 Web 环境中运行,编写可预览的 Widget 时请遵守以下限制:

  • 不使用原生 API: 不要使用原生插件或 dart:io、dart:ffi 中的 API。直接或间接依赖 dart:io 或 dart:ffi 的 Widget 在调用时会抛出异常。请使用条件导入或包装器,在预览模式下模拟或绕过这些依赖。
  • 资源路径: 通过 dart:ui 的 fromAsset API 加载资源时,请使用基于 package 的路径(例如 packages/my_package_name/assets/my_image.png,而非 assets/my_image.png)。
  • 公共回调: 确保提供给预览注解的所有回调参数均为公共且常量,以满足代码生成要求。
  • 约束: 如果 Widget 没有约束,请在 @Preview 注解中使用 size 参数显式设置约束,因为无约束的 Widget 默认约为视口的一半大小。
  • 构建缓存: 预览器会将构建缓存到项目根目录下的 .widget_preview/ 文件夹中。

工作流

创建 Widget 预览

实现新的 Widget 预览时,请复制并跟踪此清单:

  • 导入 package:flutter/widget_previews.dart。
  • 确认目标有效(顶层函数、静态方法或无参数的公共构造函数)。
  • 对目标应用 @Preview 注解。
  • 按需配置预览参数(name、group、size、textScaleFactor、wrapper、theme、brightness、localizations)。
  • 如果要将相同配置应用于多个 Widget,请将配置提取到一个扩展 Preview 或 MultiPreview 的自定义类中。
与预览交互

按照相应的条件工作流启动并使用 Widget Previewer:

如果使用受支持的 IDE(Android Studio、IntelliJ、Flutter 3.47+ 版 VS Code):

  1. 启动 IDE。Widget Previewer 会自动启动。
  2. 在侧边栏中打开“Flutter Widget Preview”标签页。
  3. 如果想查看当前活动文件以外的预览,可在左下角切换“按所选文件筛选预览”。

如果使用命令行:

  1. 切换到 Flutter 项目的根目录。
  2. 运行 flutter widget-preview start。
  3. 查看自动打开的 Chrome 环境。

反馈循环:迭代预览

  1. 修改 Widget 代码或预览配置。
  2. 在 Widget Previewer 中查看自动更新。
  3. 如果修改了全局状态(例如静态初始化器):点击右下角的全局热重启按钮。
  4. 如果只需重置局部 Widget 状态:点击相应预览卡片上的单独热重启按钮。
  5. 在 IDE/CLI 控制台中查看错误 → 修复 → 重复上述步骤。

示例

基本预览
import 'package:flutter/widget_previews.dart';
import 'package:flutter/material.dart';

@Preview(name: 'Submit Button', group: 'Form Controls')
Widget submitButtonPreview() => const ElevatedButton(
      onPressed: null,
      child: Text('Submit'),
    );
使用 PreviewThemeData 和变换的自定义预览
import 'package:flutter/widget_previews.dart';
import 'package:flutter/material.dart';

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

  PreviewThemeData _themeBuilder() =>
      CustomThemeData(brightness: brightness);

  @override
  Preview transform() {
    final originalPreview = super.transform();
    final themeVariant = switch (brightness) {
      null => 'Responsive',
      final b => b.name,
    };

    final builder = originalPreview.toBuilder();
    final baseName = originalPreview.name ?? 'Preview';
    builder
      ..name = '$baseName [$themeVariant]'
      ..theme = _themeBuilder;

    return builder.build();
  }
}

final class CustomThemeData extends PreviewThemeData {
  const CustomThemeData({this.brightness});

  final Brightness? brightness;

  @override
  Widget apply(BuildContext context, Widget child) {
    return Theme(
      data: ThemeData.from(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.teal,
          brightness: brightness ??
              MediaQuery.maybePlatformBrightnessOf(context) ??
              Brightness.light,
        ),
      ),
      child: child,
    );
  }
}

@ThemedPreview(name: 'Themed Action Button', brightness: Brightness.dark)
Widget themedButtonPreview() => const ElevatedButton(
      onPressed: null,
      child: Text('Action'),
    );
MultiPreview 实现
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;

  @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'
        ..name = '$name - ${preview.brightness!.name}';
      return builder.toPreview();
    }).toList();
  }
}

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

评论

支持 Markdown