Look at Experimental Flutter Widget Preview
New way of iterative UI development
With Flutter 3.35.0 and higher, you can now preview your widgets in the browser. You can now preview individual widgets and make changes to them with hot reload. Jetpack Compose and SwiftUI have had this feature for a while, but Flutter now is catching up.
You do not need to spin up the whole app and hot reload your way to change your widget’s code. Instead, you can make changes directly in the preview and see the results instantly.
If you have merged logic and UI, it is time to clean it up, because it can lead to more maintainable code and iterative development soon.
Flutter widget preview is still experimental, so it can contain bugs and many breaking changes can be introduced in the future. So be careful when using it in production environments.
How to Create a Preview
Take your implementation of the widget you want to preview and wrap it with the @Preview
annotation.
import 'package:flutter/widget_previews.dart';
import 'package:flutter/material.dart';
@Preview(name: 'My Widget Preview', size: Size(150, 100))
Widget examplePreview() => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('Write your email'), TextField()],
);
Afterwards, you can run the preview in your browser by running the following command:
flutter widget-preview start
The browser will open, and you will be able to see your widget in action.
If you make changes to the files where the preview is defined, the preview will automatically reload and show the changes.
How to Make More Complex Widgets
You might ask, how to integrate it with my localizations, theme and some of the widget dependencies, which you have in the project.
The Preview
comes in with multiple parameters, which you can use to configure your preview.
Here is an example:
// custom localizations
PreviewLocalizationsData previewLocalizations() =>
const PreviewLocalizationsData(
locale: Locale('en', 'US'),
supportedLocales: [Locale('en', 'US')],
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
);
// custom theme data
PreviewThemeData basePreviewTheme() => PreviewThemeData(
materialDark: ThemeData.dark(),
materialLight: ThemeData.light(),
// also cupertino equivalents are available
);
// wrapper must take in child widget and return a widget
Widget scaffoldWrapper(Widget child) => Scaffold(body: Center(child: child));
// actual preview
@Preview(
name: 'Widget UI Preview',
size: Size(640, 1136),
localizations: previewLocalizations,
theme: basePreviewTheme,
textFactorScale: 1.0,
brightness: Brightness.light,
wrapper: scaffoldWrapper,
)
Widget myWidgetPreview => MyWidget();
As it was mentioned, the Preview
widget can take more parameters, and here is a rundown of them:
name
: The name of the preview, which will be displayed in the UI.size
: The size of the preview, which can be useful for testing responsive layouts.localizations
: A function that returns the localizations data for the preview.theme
: A function that returns the theme data for the preview.textFactorScale
: A scaling factor for the text in the preview.brightness
: The brightness mode for the preview (light or dark).wrapper
: A function that wraps the preview widget in a scaffold or other layout.
The preview mostly expects static functions, which the preview creator can call and use during the preview creation process.
Afterwards, the preview enables you to switch themes, zoom in and interact with the viewer.
If you have more complex widgets, you might need to create a custom wrapper to handle all additional dependencies and providers.
Limitations
Since it is an experimental implementation running in the browser, some limitations apply:
- all dependencies must be publicly available and const
- native plugins and dart:io dependencies do not work, because we are in the browser
- assets paths must be declared as whole paths and not as local paths
It is experimental feature, so many of these do not need to apply in the future. Since, Flutter aims to decouple material/cupertino designs from to packages, e.g. the material/cupertino themes might be removed in the future.
Personal Opinion and Tips
Actual Experience
If you have fully independent widgets from some logic, native implementations and these kinds of things, you can freely pass mocked data and use the preview to test different scenarios without any issues.
It means that your widgets do not have calls on some state management, which needs further dependencies. Navigation is handled outside of the widgets.
Or in other words, only pure Flutter is used within your UI or packages which offer pure Flutter implementations.
For example, if your app follows MVVM or MVI architecture, you are ready to use the preview out of the box.
I had to rebuild the widget a couple of times to make it work properly, but I eventually got it right.
Do not forget to add size constraints, wrap the widget into a Scaffold or a material widget, and it will work as expected.
To Make it Compelling Option to Use
- IDE support for preview (will be developed)
- I see that a generator for multiple preview variants can emerge
- overriding
@Preview
to create custom preview configurations - more attributes in the preview as background, padding, etc.
- predefined sizes and configurations for the most common devices
Conclusion
SwiftUI and Jetpack Compose already enjoy previews of their UI during their development.
Flutter’s implementation is still experimental, but the base functionality is there, and it shows promise for future enhancements.
Cannot wait to see how it evolves!
Reference
For official documentation, check out the flutter.dev.
Socials
Thanks for reading this article!
For more content like this, follow me here or on X or LinkedIn.