关于 flutter-add-widget-preview
此技能指导开发者使用 Flutter Widget Previewer 隔离渲染 Flutter 组件,并配置尺寸、主题、本地化等预览环境。它涵盖注解、自定义预览及运行限制。
主要功能
- 01为组件添加 @Preview 注解
- 02配置尺寸、主题与本地化
- 03创建自定义及多配置预览
- 04处理 Web 预览环境限制
适用场景
- 01在应用上下文之外检查组件
- 02比较不同主题或字体缩放
- 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的fromAssetAPI 加载资源时,请使用基于 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):
- 启动 IDE。Widget Previewer 会自动启动。
- 在侧边栏中打开“Flutter Widget Preview”标签页。
- 如果想查看当前活动文件以外的预览,可在左下角切换“按所选文件筛选预览”。
如果使用命令行:
- 切换到 Flutter 项目的根目录。
- 运行
flutter widget-preview start。 - 查看自动打开的 Chrome 环境。
反馈循环:迭代预览
- 修改 Widget 代码或预览配置。
- 在 Widget Previewer 中查看自动更新。
- 如果修改了全局状态(例如静态初始化器):点击右下角的全局热重启按钮。
- 如果只需重置局部 Widget 状态:点击相应预览卡片上的单独热重启按钮。
- 在 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