flutter dart
npx skills add dhruvanbhalara/skills --skill flutter-add-widget-preview

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 returning Widget
    • Static methods within a class returning Widget
    • Public widget constructors/factories with no required arguments
  • Import: Always import package:flutter/widget_previews.dart.
  • Multiple Configurations: Apply multiple @Preview annotations to a single target for multiple preview instances (e.g., light/dark mode).
  • Naming: Use name and group parameters for organized preview panels.
  • Sizing: Apply explicit constraints using the size parameter if the widget is unconstrained — the previewer defaults to approximately half the viewport.

Handling Limitations

The Widget Previewer runs in a web environment. Adhere to these constraints:

Limitation Impact Workaround
No dart:io File system, sockets unavailable Use conditional imports to mock
No dart:ffi Native code won’t execute Stub native calls in preview mode
Asset paths dart:ui fromAsset requires package paths Use packages/my_package/assets/...
Callbacks Must be public and constant No closures in annotation params
Unconstrained widgets May render incorrectly Set size parameter in @Preview

IDE and CLI Integration

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 to view previews outside the active file.

Command Line

flutter widget-preview start

Opens a Chrome environment with live previews.

Feedback Loop

  1. Modify the widget code or preview configuration.
  2. Observe the automatic update in the Widget Previewer.
  3. If global state was modified → click global hot restart (bottom right).
  4. If only local widget state needs resetting → click individual hot restart on the preview card.
  5. Review errors in the IDE/CLI console → fix → repeat.

Custom Annotations

Extending Preview

Create reusable preview configurations by extending the Preview class:

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

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

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

  @override
  Preview transform() {
    final originalPreview = super.transform();
    final builder = originalPreview.toBuilder()
      ..name = 'Themed - ${originalPreview.name}'
      ..theme = _themeBuilder;
    return builder.toPreview();
  }
}

@ThemedPreview(name: 'Primary Button')
Widget primaryButton() => const ElevatedButton(onPressed: null, child: Text('Click'));

MultiPreview for Brightness Variants

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() {
    return super.transform().map((preview) {
      final builder = preview.toBuilder()
        ..group = 'Brightness'
        ..name = '$name - ${preview.brightness!.name}';
      return builder.toPreview();
    }).toList();
  }
}

@MultiBrightnessPreview(name: 'User Card')
Widget userCard() => const Card(
  child: Padding(padding: EdgeInsets.all(16), child: Text('John Doe')),
);

Workflow: Adding a Widget Preview

Task Progress

  • [ ] Step 1: Import package:flutter/widget_previews.dart.
  • [ ] Step 2: Identify valid target (top-level function, static method, or no-arg constructor).
  • [ ] Step 3: Apply @Preview annotation with name, group, size params.
  • [ ] Step 4: If config is reused across widgets → extract into custom Preview subclass.
  • [ ] Step 5: Launch previewer (IDE tab or flutter widget-preview start).
  • [ ] Step 6: Iterate — modify widget → observe auto-update → fix errors → repeat.

Examples

Basic Preview

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

@Preview(name: 'Greeting Text', group: 'Typography')
Widget greetingText() {
  return const Text('Hello, World!', style: TextStyle(fontSize: 24));
}

Preview with Size Constraints

@Preview(
  name: 'Login Form',
  group: 'Forms',
  size: Size(400, 600),
)
Widget loginFormPreview() {
  return const MaterialApp(home: LoginForm());
}

Preview on a Constructor

@Preview(name: 'Default Avatar')
class UserAvatar extends StatelessWidget {
  const UserAvatar({super.key});

  @override
  Widget build(BuildContext context) {
    return const CircleAvatar(radius: 40, child: Icon(Icons.person));
  }
}