forked from firka/flutter
[ Widget Preview ] Display an error widget when an exception is thrown while defining the widget tree (#166005)
Example exception thrown trying to invoke `Directory.current` in a widget constructor: 
This commit is contained in:
@@ -519,10 +519,12 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
|
||||
}) {
|
||||
final List<AssetsEntry> assets = rootManifest.assets.map(transformAssetsEntry).toList();
|
||||
|
||||
final List<Font> fonts =
|
||||
rootManifest.fonts.map((Font font) {
|
||||
return Font(font.familyName, font.fontAssets.map(transformFontAsset).toList());
|
||||
}).toList();
|
||||
final List<Font> fonts = <Font>[
|
||||
...widgetPreviewManifest.fonts,
|
||||
...rootManifest.fonts.map((Font font) {
|
||||
return Font(font.familyName, font.fontAssets.map(transformFontAsset).toList());
|
||||
}),
|
||||
];
|
||||
|
||||
final List<Uri> shaders = rootManifest.shaders.map(transformAssetUri).toList();
|
||||
|
||||
@@ -576,6 +578,7 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
|
||||
'--directory',
|
||||
widgetPreviewScaffoldProject.directory.path,
|
||||
'flutter_lints',
|
||||
'stack_trace',
|
||||
],
|
||||
context: PubContext.pubAdd,
|
||||
command: pubAdd,
|
||||
|
||||
@@ -103,6 +103,10 @@ class PreviewCodeGenerator {
|
||||
preview.wrapperLibraryUri,
|
||||
).call(<Expression>[previewWidget]);
|
||||
}
|
||||
previewWidget =
|
||||
Method((MethodBuilder previewBuilder) {
|
||||
previewBuilder.body = previewWidget.code;
|
||||
}).closure;
|
||||
previewExpressions.add(
|
||||
refer(
|
||||
'WidgetPreview',
|
||||
@@ -115,7 +119,7 @@ class PreviewCodeGenerator {
|
||||
key: PreviewDetails.kTextScaleFactor,
|
||||
property: preview.textScaleFactor,
|
||||
),
|
||||
'child': previewWidget,
|
||||
'builder': previewWidget,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,10 +26,7 @@ class _WidgetPreviewIconButton extends StatelessWidget {
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: onPressed,
|
||||
icon: Icon(
|
||||
color: Colors.white,
|
||||
icon,
|
||||
),
|
||||
icon: Icon(color: Colors.white, icon),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -42,9 +39,11 @@ class ZoomControls extends StatelessWidget {
|
||||
const ZoomControls({
|
||||
super.key,
|
||||
required TransformationController transformationController,
|
||||
required this.enabled,
|
||||
}) : _transformationController = transformationController;
|
||||
|
||||
final TransformationController _transformationController;
|
||||
final bool enabled;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -53,23 +52,19 @@ class ZoomControls extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
_WidgetPreviewIconButton(
|
||||
tooltip: 'Zoom in',
|
||||
onPressed: _zoomIn,
|
||||
onPressed: enabled ? _zoomIn : null,
|
||||
icon: Icons.zoom_in,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
_WidgetPreviewIconButton(
|
||||
tooltip: 'Zoom out',
|
||||
onPressed: _zoomOut,
|
||||
onPressed: enabled ? _zoomOut : null,
|
||||
icon: Icons.zoom_out,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
_WidgetPreviewIconButton(
|
||||
tooltip: 'Reset zoom',
|
||||
onPressed: _reset,
|
||||
onPressed: enabled ? _reset : null,
|
||||
icon: Icons.refresh,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Returns a [TextStyle] with [FontFeature.proportionalFigures] applied to
|
||||
/// fix blurry text.
|
||||
TextStyle fixBlurryText(TextStyle style) {
|
||||
return style.copyWith(
|
||||
fontFeatures: [const FontFeature.proportionalFigures()],
|
||||
);
|
||||
}
|
||||
|
||||
/// A basic vertical spacer.
|
||||
class VerticalSpacer extends StatelessWidget {
|
||||
/// Creates a basic vertical spacer.
|
||||
@@ -11,8 +19,6 @@ class VerticalSpacer extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const SizedBox(
|
||||
height: 10,
|
||||
);
|
||||
return const SizedBox(height: 10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ import 'package:flutter/widgets.dart';
|
||||
/// previews.
|
||||
// TODO(bkonyi): link to actual documentation when available.
|
||||
class WidgetPreview {
|
||||
/// Wraps [child] in a [WidgetPreview] instance that applies some set of
|
||||
/// Wraps [builder] in a [WidgetPreview] instance that applies some set of
|
||||
/// properties.
|
||||
const WidgetPreview({
|
||||
required this.child,
|
||||
required this.builder,
|
||||
this.name,
|
||||
this.width,
|
||||
this.height,
|
||||
@@ -30,22 +30,22 @@ class WidgetPreview {
|
||||
/// If not provided, no name will be associated with the preview.
|
||||
final String? name;
|
||||
|
||||
/// The [Widget] to be rendered in the preview.
|
||||
final Widget child;
|
||||
/// A callback to build the [Widget] to be rendered in the preview.
|
||||
final Widget Function() builder;
|
||||
|
||||
/// Artificial width constraint to be applied to the [child].
|
||||
/// Artificial width constraint to be applied to the [Widget] returned by [builder].
|
||||
///
|
||||
/// If not provided, the previewed widget will attempt to set its own width
|
||||
/// constraints and may result in an unbounded constraint error.
|
||||
final double? width;
|
||||
|
||||
/// Artificial height constraint to be applied to the [child].
|
||||
/// Artificial height constraint to be applied to the [Widget] returned by [builder].
|
||||
///
|
||||
/// If not provided, the previewed widget will attempt to set its own height
|
||||
/// constraints and may result in an unbounded constraint error.
|
||||
final double? height;
|
||||
|
||||
/// Applies font scaling to text within the [child].
|
||||
/// Applies font scaling to text within the [Widget] returned by [builder].
|
||||
///
|
||||
/// If not provided, the default text scaling factor provided by [MediaQuery]
|
||||
/// will be used.
|
||||
|
||||
@@ -2,21 +2,117 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
||||
import 'controls.dart';
|
||||
import 'generated_preview.dart';
|
||||
import 'utils.dart';
|
||||
import 'widget_preview.dart';
|
||||
|
||||
/// Displayed when an unhandled exception is thrown when initializing the widget
|
||||
/// tree for a preview (i.e., before the build phase).
|
||||
///
|
||||
/// Provides users with details about the thrown exception, including the exception
|
||||
/// contents and a scrollable stack trace.
|
||||
class _WidgetPreviewErrorWidget extends StatelessWidget {
|
||||
_WidgetPreviewErrorWidget({
|
||||
required this.error,
|
||||
required StackTrace stackTrace,
|
||||
required this.size,
|
||||
}) : trace = Trace.from(stackTrace).terse;
|
||||
|
||||
/// The [Object] that was thrown, resulting in an unhandled exception.
|
||||
final Object error;
|
||||
|
||||
/// The stack trace identifying where [error] was thrown from.
|
||||
final Trace trace;
|
||||
|
||||
/// The size of the error widget.
|
||||
final Size size;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final TextStyle boldStyle = fixBlurryText(
|
||||
TextStyle(fontWeight: FontWeight.bold),
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
height: size.height,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: 'Failed to initialize widget tree: ',
|
||||
style: boldStyle,
|
||||
),
|
||||
// TODO(bkonyi): use monospace font
|
||||
TextSpan(text: error.toString()),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text('Stacktrace:', style: boldStyle),
|
||||
// TODO(bkonyi): use monospace font
|
||||
SelectableText.rich(
|
||||
TextSpan(children: _formatFrames(trace.frames)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<TextSpan> _formatFrames(List<Frame> frames) {
|
||||
// Figure out the longest path so we know how much to pad.
|
||||
final int longest = frames
|
||||
.map((frame) => frame.location.length)
|
||||
.fold(0, math.max);
|
||||
|
||||
final TextStyle linkTextStyle = fixBlurryText(
|
||||
TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
// TODO(bkonyi): this color scheme is from DevTools and should be responsive
|
||||
// to changes in the previewer theme.
|
||||
color: const Color(0xFF1976D2),
|
||||
),
|
||||
);
|
||||
|
||||
// Print out the stack trace nicely formatted.
|
||||
return frames.map<TextSpan>((frame) {
|
||||
if (frame is UnparsedFrame) return TextSpan(text: '$frame\n');
|
||||
return TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: frame.location,
|
||||
style: linkTextStyle,
|
||||
recognizer:
|
||||
TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
// TODO(bkonyi): notify IDEs to navigate to the source location via DTD.
|
||||
},
|
||||
),
|
||||
TextSpan(text: ' ' * (longest - frame.location.length)),
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(text: '${frame.member}\n'),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
class WidgetPreviewWidget extends StatefulWidget {
|
||||
const WidgetPreviewWidget({
|
||||
super.key,
|
||||
required this.preview,
|
||||
});
|
||||
const WidgetPreviewWidget({super.key, required this.preview});
|
||||
|
||||
final WidgetPreview preview;
|
||||
|
||||
@@ -43,19 +139,31 @@ class _WidgetPreviewWidgetState extends State<WidgetPreviewWidget> {
|
||||
maxHeight: previewerConstraints.maxHeight / 2.0,
|
||||
);
|
||||
|
||||
Widget preview = _WidgetPreviewWrapper(
|
||||
bool errorThrownDuringTreeConstruction = false;
|
||||
Widget preview;
|
||||
// Catch any unhandled exceptions and display an error widget instead of taking
|
||||
// down the entire preview environment.
|
||||
try {
|
||||
preview = widget.preview.builder();
|
||||
} on Object catch (error, stackTrace) {
|
||||
errorThrownDuringTreeConstruction = true;
|
||||
preview = _WidgetPreviewErrorWidget(
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
size: maxSizeConstraints.biggest,
|
||||
);
|
||||
}
|
||||
|
||||
preview = _WidgetPreviewWrapper(
|
||||
previewerConstraints: maxSizeConstraints,
|
||||
child: SizedBox(
|
||||
width: widget.preview.width,
|
||||
height: widget.preview.height,
|
||||
child: widget.preview.child,
|
||||
child: preview,
|
||||
),
|
||||
);
|
||||
|
||||
preview = MediaQuery(
|
||||
data: _buildMediaQueryOverride(),
|
||||
child: preview,
|
||||
);
|
||||
preview = MediaQuery(data: _buildMediaQueryOverride(), child: preview);
|
||||
|
||||
preview = Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -63,9 +171,8 @@ class _WidgetPreviewWidgetState extends State<WidgetPreviewWidget> {
|
||||
if (widget.preview.name != null) ...[
|
||||
Text(
|
||||
widget.preview.name!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w300,
|
||||
style: fixBlurryText(
|
||||
TextStyle(fontSize: 16, fontWeight: FontWeight.w300),
|
||||
),
|
||||
),
|
||||
const VerticalSpacer(),
|
||||
@@ -80,6 +187,9 @@ class _WidgetPreviewWidgetState extends State<WidgetPreviewWidget> {
|
||||
children: [
|
||||
ZoomControls(
|
||||
transformationController: transformationController,
|
||||
// If an unhandled exception was caught and we're displaying an error
|
||||
// widget, these controls should be disabled.
|
||||
enabled: !errorThrownDuringTreeConstruction,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -90,10 +200,7 @@ class _WidgetPreviewWidgetState extends State<WidgetPreviewWidget> {
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16.0,
|
||||
horizontal: 16.0,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
|
||||
child: preview,
|
||||
),
|
||||
),
|
||||
@@ -109,13 +216,13 @@ class _WidgetPreviewWidgetState extends State<WidgetPreviewWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
var size = Size(widget.preview.width ?? mediaQueryData.size.width,
|
||||
widget.preview.height ?? mediaQueryData.size.height);
|
||||
var size = Size(
|
||||
widget.preview.width ?? mediaQueryData.size.width,
|
||||
widget.preview.height ?? mediaQueryData.size.height,
|
||||
);
|
||||
|
||||
if (widget.preview.width != null || widget.preview.height != null) {
|
||||
mediaQueryData = mediaQueryData.copyWith(
|
||||
size: size,
|
||||
);
|
||||
mediaQueryData = mediaQueryData.copyWith(size: size);
|
||||
}
|
||||
|
||||
return mediaQueryData;
|
||||
@@ -137,8 +244,11 @@ class WidgetPreviewerWindowConstraints extends InheritedWidget {
|
||||
final BoxConstraints constraints;
|
||||
|
||||
static BoxConstraints getRootConstraints(BuildContext context) {
|
||||
final result = context
|
||||
.dependOnInheritedWidgetOfExactType<WidgetPreviewerWindowConstraints>();
|
||||
final result =
|
||||
context
|
||||
.dependOnInheritedWidgetOfExactType<
|
||||
WidgetPreviewerWindowConstraints
|
||||
>();
|
||||
assert(
|
||||
result != null,
|
||||
'No WidgetPreviewerWindowConstraints founds in context',
|
||||
@@ -207,8 +317,8 @@ class _WidgetPreviewWrapperBox extends RenderShiftedBox {
|
||||
_WidgetPreviewWrapperBox({
|
||||
required RenderBox? child,
|
||||
required BoxConstraints previewerConstraints,
|
||||
}) : _previewerConstraints = previewerConstraints,
|
||||
super(child);
|
||||
}) : _previewerConstraints = previewerConstraints,
|
||||
super(child);
|
||||
|
||||
BoxConstraints _constraintOverride = const BoxConstraints();
|
||||
BoxConstraints _previewerConstraints;
|
||||
@@ -222,10 +332,7 @@ class _WidgetPreviewWrapperBox extends RenderShiftedBox {
|
||||
}
|
||||
|
||||
@override
|
||||
void layout(
|
||||
Constraints constraints, {
|
||||
bool parentUsesSize = false,
|
||||
}) {
|
||||
void layout(Constraints constraints, {bool parentUsesSize = false}) {
|
||||
if (child != null && constraints is BoxConstraints) {
|
||||
double minInstrinsicHeight;
|
||||
try {
|
||||
@@ -241,14 +348,12 @@ class _WidgetPreviewWrapperBox extends RenderShiftedBox {
|
||||
// the previewer. In this case, apply finite constraints (e.g., the
|
||||
// constraints for the root of the previewer). Otherwise, use the
|
||||
// widget's actual constraints.
|
||||
_constraintOverride = minInstrinsicHeight == 0
|
||||
? _previewerConstraints
|
||||
: const BoxConstraints();
|
||||
_constraintOverride =
|
||||
minInstrinsicHeight == 0
|
||||
? _previewerConstraints
|
||||
: const BoxConstraints();
|
||||
}
|
||||
super.layout(
|
||||
constraints,
|
||||
parentUsesSize: parentUsesSize,
|
||||
);
|
||||
super.layout(constraints, parentUsesSize: parentUsesSize);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -259,10 +364,7 @@ class _WidgetPreviewWrapperBox extends RenderShiftedBox {
|
||||
return;
|
||||
}
|
||||
final updatedConstraints = _constraintOverride.enforce(constraints);
|
||||
child.layout(
|
||||
updatedConstraints,
|
||||
parentUsesSize: true,
|
||||
);
|
||||
child.layout(updatedConstraints, parentUsesSize: true);
|
||||
size = constraints.constrain(child.size);
|
||||
}
|
||||
}
|
||||
@@ -308,9 +410,7 @@ class PreviewAssetBundle extends PlatformAssetBundle {
|
||||
/// the preview scaffold project which prevents us from being able to use hot
|
||||
/// restart to iterate on this file.
|
||||
Future<void> mainImpl() async {
|
||||
runApp(
|
||||
_WidgetPreviewScaffold(),
|
||||
);
|
||||
runApp(_WidgetPreviewScaffold());
|
||||
}
|
||||
|
||||
class _WidgetPreviewScaffold extends StatelessWidget {
|
||||
@@ -321,7 +421,7 @@ class _WidgetPreviewScaffold extends StatelessWidget {
|
||||
final List<WidgetPreview> previewList = previews();
|
||||
Widget previewView;
|
||||
if (previewList.isEmpty) {
|
||||
previewView = const Column(
|
||||
previewView = Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Center(
|
||||
@@ -329,9 +429,9 @@ class _WidgetPreviewScaffold extends StatelessWidget {
|
||||
// with Widget Previews.
|
||||
child: Text(
|
||||
'No previews available',
|
||||
style: TextStyle(color: Colors.white),
|
||||
style: fixBlurryText(TextStyle(color: Colors.white)),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
@@ -343,7 +443,8 @@ class _WidgetPreviewScaffold extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
for (final WidgetPreview preview in previewList) WidgetPreviewWidget(preview: preview),
|
||||
for (final WidgetPreview preview in previewList)
|
||||
WidgetPreviewWidget(preview: preview),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -59,6 +59,8 @@ void main() {
|
||||
'can create a pubspec.yaml for the preview scaffold including root project assets',
|
||||
() {
|
||||
final FlutterManifest root = rootProject.manifest;
|
||||
final FlutterManifest emptyPreviewManifest =
|
||||
rootProject.widgetPreviewScaffoldProject.manifest;
|
||||
final FlutterManifest updated = command.buildPubspec(
|
||||
rootManifest: rootProject.manifest,
|
||||
widgetPreviewManifest: rootProject.widgetPreviewScaffoldProject.manifest,
|
||||
@@ -73,9 +75,28 @@ void main() {
|
||||
expect(updatedEntry, WidgetPreviewStartCommand.transformAssetsEntry(rootEntry));
|
||||
}
|
||||
|
||||
expect(updated.fonts.length, root.fonts.length);
|
||||
for (int i = 0; i < root.fonts.length; ++i) {
|
||||
final Font rootFont = root.fonts[i];
|
||||
final int emptyPreviewFontCount = emptyPreviewManifest.fonts.length;
|
||||
final int expectedFontCount = root.fonts.length + emptyPreviewFontCount;
|
||||
expect(updated.fonts.length, expectedFontCount);
|
||||
|
||||
// Verify that the updated preview scaffold pubspec includes fonts needed by
|
||||
// the previewer.
|
||||
for (int i = 0; i < emptyPreviewFontCount; ++i) {
|
||||
final Font defaultPreviewerFont = emptyPreviewManifest.fonts[i];
|
||||
final Font updatedFont = updated.fonts[i];
|
||||
expect(updatedFont.familyName, defaultPreviewerFont.familyName);
|
||||
expect(updatedFont.fontAssets.length, defaultPreviewerFont.fontAssets.length);
|
||||
for (int j = 0; j < defaultPreviewerFont.fontAssets.length; ++j) {
|
||||
final FontAsset rootFontAsset = defaultPreviewerFont.fontAssets[j];
|
||||
final FontAsset updatedFontAsset = updatedFont.fontAssets[j];
|
||||
expect(updatedFontAsset.descriptor, rootFontAsset.descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify fonts from the root project are included in the updated preview
|
||||
// scaffold pubspec.
|
||||
for (int i = emptyPreviewFontCount; i < expectedFontCount; ++i) {
|
||||
final Font rootFont = root.fonts[i - emptyPreviewFontCount];
|
||||
final Font updatedFont = updated.fonts[i];
|
||||
expect(updatedFont.familyName, rootFont.familyName);
|
||||
expect(updatedFont.fontAssets.length, rootFont.fontAssets.length);
|
||||
|
||||
@@ -195,7 +195,7 @@ Widget preview() => Text('Foo');''';
|
||||
|
||||
const String expectedGeneratedFileContents = '''
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'widget_preview.dart' as _i1;import 'package:flutter_project/foo.dart' as _i2;List<_i1.WidgetPreview> previews() => [_i1.WidgetPreview(name: 'preview', child: _i2.preview(), )];''';
|
||||
import 'widget_preview.dart' as _i1;import 'package:flutter_project/foo.dart' as _i2;List<_i1.WidgetPreview> previews() => [_i1.WidgetPreview(name: 'preview', builder: () => _i2.preview(), )];''';
|
||||
|
||||
testUsingContext(
|
||||
'start finds existing previews and injects them into ${PreviewCodeGenerator.generatedPreviewFilePath}',
|
||||
|
||||
@@ -73,7 +73,7 @@ void main() {
|
||||
// The generated file is unfortunately unformatted.
|
||||
const String expectedGeneratedPreviewFileContents = '''
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'widget_preview.dart' as _i1;import 'foo.dart' as _i2;import 'src/bar.dart' as _i3;import 'wrapper.dart' as _i4;import 'package:flutter/widgets.dart' as _i5;List<_i1.WidgetPreview> previews() => [_i1.WidgetPreview(child: _i2.preview()), _i1.WidgetPreview(child: _i3.barPreview1()), _i1.WidgetPreview(child: _i3.barPreview2()), _i1.WidgetPreview(name: Foo, height: 456.0, width: 123.0, textScaleFactor: 50.0, child: _i4.wrapper(_i5.Builder(builder: _i3.barPreview3())), ), ];''';
|
||||
import 'widget_preview.dart' as _i1;import 'foo.dart' as _i2;import 'src/bar.dart' as _i3;import 'wrapper.dart' as _i4;import 'package:flutter/widgets.dart' as _i5;List<_i1.WidgetPreview> previews() => [_i1.WidgetPreview(builder: () => _i2.preview()), _i1.WidgetPreview(builder: () => _i3.barPreview1()), _i1.WidgetPreview(builder: () => _i3.barPreview2()), _i1.WidgetPreview(name: Foo, height: 456.0, width: 123.0, textScaleFactor: 50.0, builder: () => _i4.wrapper(_i5.Builder(builder: _i3.barPreview3())), ), ];''';
|
||||
expect(generatedPreviewFile.readAsStringSync(), expectedGeneratedPreviewFileContents);
|
||||
|
||||
// Regenerate the generated file with no previews.
|
||||
|
||||
Reference in New Issue
Block a user