Widgetbook 3 (Release Candidate): Migration Guide

Widgetbook 3.0.0-rc.2 is out 🎉! This release candidate marks one step closer towards the stable version of Widgetbook 3. It's like getting the burgers out of the freezer but not putting them on the grill yet.

A lot of breaking changes were made compared to the beta version of Widgetbook 3. These changes were necessary to improve the DX and the integration with Widgetbook Cloud.

Highlights

  1. No more code generation for addons or app builders, only for use cases.
  2. Almost zero-dependencies, after removing dependency on provider, go_router, flutter_bloc and freezed.
  3. Better addons and knobs deep linking on the web.
  4. New addons API that make your appBuilder much cleaner.
  5. Performance optimization by minimizing rebuilds.
  6. Some bug fixes, yet a lot more to come.

How to Migrate

Annotations

This release candidate comes with code generation support for use cases only. So the following changes were made:

  1. @WidgetbookUseCase has been renamed to @UseCase.

  2. @WidgetbookApp has been replaced with @App that takes no parameters. The new @App annotation generates a file containing a single variable called directories.

    ParameterAlternative
    nameNone
    foldersExpandedNone
    widgetsExpandedNone
    constructorNone
    devicesDeviceFrameAddon
    framesDeviceFrameAddon
    themeTypeThemeAddon
  3. Removed Annotations

    AnnotationAlternative
    @WidgetbookLocalesLocalizationAddon
    @WidgetbookLocalizationDelegatesLocalizationAddon
    @WidgetbookThemeThemeAddon
    @WidgetbookAppBuilderWidgetbook's appBuilder parameter

Configuration

Since the Widgetbook app is no longer generated by @App, instead, it generates a widgetbook.directories.g.dart file with the directories variable. You will need to manually configure your app as follows:

In v3.0.0-rc.1, the generated file was named widgetbook.g.dart, but this was changed in v3.0.0-rc.2 to widgetbook.directories.g.dart, to avoid conflict with other builders (e.g. json_serializable).

// widgetbook.dart
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;

import 'widgetbook.directories.g.dart';

void main() {
  runApp(const WidgetbookApp());
}

@widgetbook.App()
class WidgetbookApp extends StatelessWidget {
  const WidgetbookApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Widgetbook.material(
      // Imported from `widgetbook.directories.g.dart`
      directories: directories,
      // ...
    );
  }
}

Knobs

Some knobs have been renamed:

Old NameNew Name
sliderdouble.slider
nullableSliderdoubleOrNull.slider
numberdouble.input
nullableNumberdoubleOrNull.input
textstring
nullableTextstringOrNull
optionslist
nullableBooleanbooleanOrNull

Addons

Most addons used to have an annotation for them and they were added through code generation. After code generation for addons was removed, you should add them manually to your Widgetbook app as follows, with taking their order into consideration (Use the order mentioned below):

@override
Widget build(BuildContext context) {
  return Widgetbook.material(
    // ...
    addons: [
      // Add addons here...
    ],
  );
}

If you were using addons the manual way, you will find out that the addons constructors no longer take a setting parameter, instead, they take the required data directly.

1. ThemeAddon

The new ThemeAddon replaces the @WidgetbookTheme annotation. It comes with two variants (MaterialThemeAddon and CupertinoThemeAddon), that can be used as follows:

ThemeAddon<AppThemeData>(
  themes: [
    WidgetbookTheme(
      name: 'Light',
      data: yourCustomLightTheme,
    ),
    WidgetbookTheme(
      name: 'Dark',
      data: yourCustomDarkTheme,
    ),
  ],
  themeBuilder: (context, theme, child) {
    return AppTheme(
      data: theme,
      child: child,
    );
  },
)
MaterialThemeAddon(
  themes: [
    WidgetbookTheme(
      name: 'Light',
      data: yourMaterialLightTheme,
    ),
    WidgetbookTheme(
      name: 'Dark',
      data: yourMaterialDarkTheme,
    ),
  ],
)
CupertinoThemeAddon(
  themes: [
    WidgetbookTheme(
      name: 'Light',
      data: yourCupertinoLightTheme,
    ),
    WidgetbookTheme(
      name: 'Dark',
      data: yourCupertinoDarkTheme,
    ),
  ],
)

2. TextScaleAddon

The new TextScaleAddon replaces the @WidgetbookApp annotation's textScaleFactors parameter. It can be used as follows:

TextScaleAddon(
  scales: [1.0, 2.0],
)

3. LocalizationAddon

The new LocalizationAddon replaces the @WidgetbookLocales and the WidgetbookLocalizationDelegates annotations. It that can be used as follows:

LocalizationAddon(
  locales: [
    const Locale('en', 'US'),
  ],
  localizationsDelegates: [
    DefaultWidgetsLocalizations.delegate,
    DefaultMaterialLocalizations.delegate,
  ],
)

4. DeviceFrameAddon (formerly FrameAddon)

The new TextScaleAddon replaces the @WidgetbookApp annotation's devices parameter. It can be used as follows:

DeviceFrameAddon(
  devices: [
    Devices.ios.iPhoneSE,
    Devices.ios.iPhone13,
  ],
)

AppBuilder

Previously in the beta version of Widgetbook 3, you needed to configure the addons in the appBuilder as follows, but thanks to the new Addons API, the following code is no longer needed and can be safely REMOVED.

// [WARNING] Widgetbook 3 beta code
@WidgetbookAppBuilder
Widget customAppBuilder(BuildContext context, Widget child) {
  final builder = Builder(
    builder: (context) {
      return MaterialApp(
        theme: context.materialTheme,
        locale: context.localization!.activeLocale,
        supportedLocales: context.localization!.locales,
        localizationsDelegates: context.localization!.localizationsDelegates,
        debugShowCheckedModeBanner: false,
        home: Scaffold(
          body: MediaQuery(
            data: MediaQuery.of(context).copyWith(
              textScaleFactor: context.textScale,
            ),
            child: child,
          ),
        ),
      );
    },
  );

  final frameBuilder = context.frameBuilder!;
  return frameBuilder(
    context,
    builder,
  );
}

But if you were using the app builder to do more things than just configuring the addons, then you can use the appBuilder parameter in the Widgetbook widget as follows:

@override
Widget build(BuildContext context) {
  return Widgetbook.material(
    // ...
    appBuilder: (context, child) {
      return AwesomePackage(
        child: MaterialApp(
          debugShowCheckedModeBanner: false,
          home: child,
        )
      );
    }
  );
}