Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart';
final anchorOptions = Anchor.values.map((e) => e.name).toList();

Widget spriteAnimationWidgetBuilder(DashbookContext ctx) {
return Container(
return SizedBox(
width: ctx.numberProperty('container width', 400),
height: ctx.numberProperty('container height', 200),
child: SpriteAnimationWidget.asset(
Expand All @@ -32,3 +32,30 @@ Widget spriteAnimationWidgetBuilder(DashbookContext ctx) {
),
);
}

Widget spriteAnimationWithSizeWidgetBuilder(DashbookContext ctx) {
return SpriteAnimationWidget.asset(
size: Size(
ctx.numberProperty('width', 400),
ctx.numberProperty('height', 200),
),
path: 'bomb_ptero.png',
data: SpriteAnimationData.sequenced(
amount: 4,
stepTime: 0.2,
textureSize: Vector2(48, 32),
),
playing: ctx.boolProperty('playing', true),
anchor: Anchor.valueOf(
ctx.listProperty('anchor', 'center', anchorOptions),
),
paint:
paintList[paintChoices.indexOf(
ctx.listProperty(
'paint',
'none',
paintChoices,
),
)],
);
}
25 changes: 25 additions & 0 deletions examples/lib/stories/widgets/sprite_widget_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,28 @@ Widget spriteWidgetBuilder(DashbookContext ctx) {
),
);
}

Widget spriteWidgetWithSizeBuilder(DashbookContext ctx) {
return DecoratedBox(
decoration: BoxDecoration(border: Border.all(color: Colors.amber)),
child: SpriteWidget.asset(
size: Size(
ctx.numberProperty('width', 400),
ctx.numberProperty('height', 200),
),
path: 'shield.png',
angle: pi / 180 * ctx.numberProperty('angle (deg)', 0),
anchor: Anchor.valueOf(
ctx.listProperty('anchor', 'center', anchorOptions),
),
paint:
paintList[paintChoices.indexOf(
ctx.listProperty(
'paint',
'none',
paintChoices,
),
)],
),
);
}
16 changes: 16 additions & 0 deletions examples/lib/stories/widgets/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ void addWidgetsStories(Dashbook dashbook) {
on the pen icon.
''',
)
..add(
'Sprite Widget (with size)',
spriteWidgetWithSizeBuilder,
codeLink: baseLink('widgets/sprite_widget_example.dart'),
info: '''
Similar as the default example, but using a fixed size directly in the widget.
''',
)
..add(
'Sprite Widget (section of image)',
partialSpriteWidgetBuilder,
Expand All @@ -71,6 +79,14 @@ void addWidgetsStories(Dashbook dashbook) {
the settings on the pen icon.
''',
)
..add(
'Sprite Animation Widget (with size)',
spriteAnimationWithSizeWidgetBuilder,
codeLink: baseLink('widgets/sprite_animation_widget_example.dart'),
info: '''
Similar as the default example, but using a fixed size directly in the widget.
''',
)
..add(
'CustomPainterComponent',
customPainterBuilder,
Expand Down
14 changes: 13 additions & 1 deletion packages/flame/lib/src/widgets/animation_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class SpriteAnimationWidget extends StatefulWidget {
/// When omitted the default paint from the [Sprite] class will be used.
final Paint? paint;

/// The size of the widget. If null, the widget will fill the available space.
final Size? size;

const SpriteAnimationWidget({
required SpriteAnimation animation,
required SpriteAnimationTicker animationTicker,
Expand All @@ -43,6 +46,7 @@ class SpriteAnimationWidget extends StatefulWidget {
this.loadingBuilder,
this.onComplete,
this.paint,
this.size,
super.key,
}) : _animationFuture = animation,
_animationTicker = animationTicker;
Expand All @@ -63,6 +67,7 @@ class SpriteAnimationWidget extends StatefulWidget {
this.loadingBuilder,
this.onComplete,
this.paint,
this.size,
String? package,
super.key,
}) : _animationFuture = SpriteAnimation.load(
Expand Down Expand Up @@ -132,13 +137,20 @@ class _SpriteAnimationWidgetState extends State<SpriteAnimationWidget> {
final ticker = _animationTicker ?? spriteAnimation.createTicker();
ticker.completed.then((_) => widget.onComplete?.call());

return InternalSpriteAnimationWidget(
final internalWidget = InternalSpriteAnimationWidget(
animation: spriteAnimation,
animationTicker: ticker,
anchor: widget.anchor,
playing: widget.playing,
paint: widget.paint,
);
if (widget.size != null) {
return SizedBox.fromSize(
size: widget.size,
child: internalWidget,
);
}
return internalWidget;
},
errorBuilder: widget.errorBuilder,
loadingBuilder: widget.loadingBuilder,
Expand Down
14 changes: 13 additions & 1 deletion packages/flame/lib/src/widgets/sprite_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ class SpriteWidget extends StatefulWidget {
/// If the Sprite should be rasterized or not.
final bool rasterize;

/// The size of the widget. If null, the widget will fill the available space.
final Size? size;

final FutureOr<Sprite> _spriteFuture;

/// renders the [sprite] as a Widget.
Expand All @@ -45,6 +48,7 @@ class SpriteWidget extends StatefulWidget {
this.loadingBuilder,
this.paint,
this.rasterize = false,
this.size,
super.key,
}) : _spriteFuture = sprite;

Expand All @@ -65,6 +69,7 @@ class SpriteWidget extends StatefulWidget {
this.loadingBuilder,
this.paint,
this.rasterize = false,
this.size,
String? package,
super.key,
}) : _spriteFuture = Sprite.load(
Expand Down Expand Up @@ -141,12 +146,19 @@ class _SpriteWidgetState extends State<SpriteWidget> {
return BaseFutureBuilder<Sprite>(
future: _spriteFuture,
builder: (_, sprite) {
return InternalSpriteWidget(
final internalWidget = InternalSpriteWidget(
sprite: sprite,
anchor: widget.anchor,
angle: widget.angle,
paint: widget.paint,
);
if (widget.size != null) {
return SizedBox.fromSize(
size: widget.size,
child: internalWidget,
);
}
return internalWidget;
},
errorBuilder: widget.errorBuilder,
loadingBuilder: widget.loadingBuilder,
Expand Down
91 changes: 91 additions & 0 deletions packages/flame/test/widgets/sprite_animation_widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -528,5 +528,96 @@ Future<void> main() async {
});
});
});

group('size parameter', () {
testWidgets('when null, does not wrap in SizedBox', (tester) async {
final sprite1 = Sprite(image);
final sprite2 = Sprite(image);
final spriteAnimation = SpriteAnimation.spriteList(
[sprite1, sprite2],
stepTime: 0.1,
);

await tester.pumpWidget(
SpriteAnimationWidget(
animation: spriteAnimation,
animationTicker: spriteAnimation.createTicker(),
),
);

// Check that InternalSpriteAnimationWidget is not wrapped in a SizedBox
final internalWidgetFinder = find.byType(InternalSpriteAnimationWidget);
final sizedBoxAncestor = find.ancestor(
of: internalWidgetFinder,
matching: find.byType(SizedBox),
);
expect(sizedBoxAncestor, findsNothing);
});

testWidgets('when provided, wraps in SizedBox with correct size', (
tester,
) async {
final sprite1 = Sprite(image);
final sprite2 = Sprite(image);
final spriteAnimation = SpriteAnimation.spriteList(
[sprite1, sprite2],
stepTime: 0.1,
);
const customSize = Size(100, 200);

await tester.pumpWidget(
SpriteAnimationWidget(
animation: spriteAnimation,
animationTicker: spriteAnimation.createTicker(),
size: customSize,
),
);

// Find SizedBox that is an ancestor of InternalSpriteAnimationWidget
final internalWidgetFinder = find.byType(InternalSpriteAnimationWidget);
final sizedBoxFinder = find.ancestor(
of: internalWidgetFinder,
matching: find.byType(SizedBox),
);
expect(sizedBoxFinder, findsOneWidget);

final sizedBox = tester.widget<SizedBox>(sizedBoxFinder);
expect(sizedBox.width, customSize.width);
expect(sizedBox.height, customSize.height);
});

testWidgets('asset constructor respects size parameter', (tester) async {
const imagePath = 'test_path_size_animation';
Flame.images.add(imagePath, image);
const customSize = Size(150, 250);
final spriteAnimationData = SpriteAnimationData.sequenced(
amount: 2,
stepTime: 1,
textureSize: Vector2(10, 10),
);

await tester.pumpWidget(
SpriteAnimationWidget.asset(
path: imagePath,
data: spriteAnimationData,
size: customSize,
),
);

await tester.pump();

// Find SizedBox that is an ancestor of InternalSpriteAnimationWidget
final internalWidgetFinder = find.byType(InternalSpriteAnimationWidget);
final sizedBoxFinder = find.ancestor(
of: internalWidgetFinder,
matching: find.byType(SizedBox),
);
expect(sizedBoxFinder, findsOneWidget);

final sizedBox = tester.widget<SizedBox>(sizedBoxFinder);
expect(sizedBox.width, customSize.width);
expect(sizedBox.height, customSize.height);
});
});
});
}
71 changes: 71 additions & 0 deletions packages/flame/test/widgets/sprite_widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,76 @@ Future<void> main() async {
expect(internalSpriteWidgetFinder.sprite.srcPosition, Vector2(10, 10));
});
});

group('size parameter', () {
testWidgets('when null, does not wrap in SizedBox', (tester) async {
final sprite = Sprite(image);

await tester.pumpWidget(SpriteWidget(sprite: sprite));
await tester.pump();

// Check that InternalSpriteWidget is not wrapped in a SizedBox
final internalWidgetFinder = find.byType(InternalSpriteWidget);
final sizedBoxAncestor = find.ancestor(
of: internalWidgetFinder,
matching: find.byType(SizedBox),
);
expect(sizedBoxAncestor, findsNothing);
});

testWidgets('when provided, wraps in SizedBox with correct size', (
tester,
) async {
final sprite = Sprite(image);
const customSize = Size(100, 200);

await tester.pumpWidget(
SpriteWidget(
sprite: sprite,
size: customSize,
),
);
await tester.pump();

// Find SizedBox that is an ancestor of InternalSpriteWidget
final internalWidgetFinder = find.byType(InternalSpriteWidget);
final sizedBoxFinder = find.ancestor(
of: internalWidgetFinder,
matching: find.byType(SizedBox),
);
expect(sizedBoxFinder, findsOneWidget);

final sizedBox = tester.widget<SizedBox>(sizedBoxFinder);
expect(sizedBox.width, customSize.width);
expect(sizedBox.height, customSize.height);
});

testWidgets('asset constructor respects size parameter', (tester) async {
const imagePath = 'test_path_size';
Flame.images.add(imagePath, image);
const customSize = Size(150, 250);

await tester.pumpWidget(
SpriteWidget.asset(
path: imagePath,
size: customSize,
),
);

await tester.pump();

// Find SizedBox that is an ancestor of InternalSpriteWidget
final internalWidgetFinder = find.byType(InternalSpriteWidget);
final sizedBoxFinder = find.ancestor(
of: internalWidgetFinder,
matching: find.byType(SizedBox),
);
expect(sizedBoxFinder, findsOneWidget);

final sizedBox = tester.widget<SizedBox>(sizedBoxFinder);
expect(sizedBox.width, customSize.width);
expect(sizedBox.height, customSize.height);
});
});
});
}
Loading