Skip to content
This repository has been archived by the owner on Aug 14, 2023. It is now read-only.

Commit

Permalink
feat: add loading indicator to character sprites (#579)
Browse files Browse the repository at this point in the history
  • Loading branch information
omartinma authored May 12, 2021
1 parent 5e0ea59 commit 87c9d69
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class AnimatedAndroid extends AnimatedSprite {
const AnimatedAndroid({Key? key})
: super(
key: key,
loadingIndicatorColor: PhotoboothColors.green,
sprites: const Sprites(
asset: 'android_spritesheet.png',
size: Size(450, 658),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class AnimatedDash extends AnimatedSprite {
const AnimatedDash({Key? key})
: super(
key: key,
loadingIndicatorColor: PhotoboothColors.blue,
sprites: const Sprites(
asset: 'dash_spritesheet.png',
size: Size(650, 587),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class AnimatedDino extends AnimatedSprite {
const AnimatedDino({Key? key})
: super(
key: key,
loadingIndicatorColor: PhotoboothColors.orange,
sprites: const Sprites(
asset: 'dino_spritesheet.png',
size: Size(648, 757),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class AnimatedSparky extends AnimatedSprite {
const AnimatedSparky({Key? key})
: super(
key: key,
loadingIndicatorColor: PhotoboothColors.red,
sprites: const Sprites(
asset: 'sparky_spritesheet.png',
size: Size(730, 588),
Expand Down
1 change: 1 addition & 0 deletions lib/share/widgets/animated_photo_indicator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class AnimatedPhotoIndicator extends StatelessWidget {
frames: 51,
stepTime: 0.05,
),
showLoadingIndicator: false,
),
);
}
Expand Down
2 changes: 2 additions & 0 deletions lib/share/widgets/animated_photobooth_photo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class AnimatedPhotoboothPhotoLandscape extends StatelessWidget {
frames: 19,
stepTime: 2 / 19,
),
showLoadingIndicator: false,
);
static const aspectRatio = PhotoboothAspectRatio.landscape;
static const left = 129.0;
Expand Down Expand Up @@ -149,6 +150,7 @@ class AnimatedPhotoboothPhotoPortrait extends StatelessWidget {
frames: 38,
stepTime: 0.05,
),
showLoadingIndicator: false,
);
static const aspectRatio = PhotoboothAspectRatio.portrait;
static const left = 93.0;
Expand Down
30 changes: 1 addition & 29 deletions lib/stickers/widgets/stickers_tabs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class StickerChoice extends StatelessWidget {
int? frame,
bool wasSynchronouslyLoaded,
) {
return AnimatedCrossFade(
return AppAnimatedCrossFade(
firstChild: SizedBox.fromSize(
size: const Size(20, 20),
child: const AppCircularProgressIndicator(strokeWidth: 2),
Expand All @@ -176,34 +176,6 @@ class StickerChoice extends StatelessWidget {
crossFadeState: frame == null
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
duration: const Duration(seconds: 1),
alignment: Alignment.center,
firstCurve: Curves.easeOut,
secondCurve: Curves.easeOut,
sizeCurve: Curves.easeOut,
layoutBuilder: (
Widget topChild,
Key topChildKey,
Widget bottomChild,
Key bottomChildKey,
) {
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Align(
alignment: Alignment.center,
key: bottomChildKey,
child: bottomChild,
),
Align(
alignment: Alignment.center,
key: topChildKey,
child: topChild,
),
],
);
},
);
},
);
Expand Down
31 changes: 25 additions & 6 deletions packages/photobooth_ui/lib/src/widgets/animated_sprite.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:flame/sprite.dart';
import 'package:flame/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:photobooth_ui/photobooth_ui.dart';

/// {@template sprites}
/// Object which contains meta data for a collection of sprites.
Expand Down Expand Up @@ -53,6 +54,8 @@ class AnimatedSprite extends StatefulWidget {
Key? key,
required this.sprites,
this.mode = AnimationMode.loop,
this.showLoadingIndicator = true,
this.loadingIndicatorColor = PhotoboothColors.orange,
}) : super(key: key);

/// The collection of sprites which will be animated.
Expand All @@ -61,6 +64,12 @@ class AnimatedSprite extends StatefulWidget {
/// The mode of animation (`trigger`, `loop` or `oneTime`).
final AnimationMode mode;

/// Where should display a loading indicator while loading the sprite
final bool showLoadingIndicator;

/// Color for loading indicator
final Color loadingIndicatorColor;

@override
_AnimatedSpriteState createState() => _AnimatedSpriteState();
}
Expand Down Expand Up @@ -118,16 +127,26 @@ class _AnimatedSpriteState extends State<AnimatedSprite> {

@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
child: AnimatedOpacity(
opacity: _status.isLoaded ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
return AppAnimatedCrossFade(
firstChild: widget.showLoadingIndicator
? SizedBox.fromSize(
size: const Size(20, 20),
child: AppCircularProgressIndicator(
strokeWidth: 2,
color: widget.loadingIndicatorColor,
),
)
: const SizedBox(),
secondChild: Container(
width: double.infinity,
height: double.infinity,
child: _status.isLoaded
? SpriteAnimationWidget(animation: _animation, playing: _isPlaying)
: const SizedBox(),
),
crossFadeState: _status.isLoaded
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';

/// {@template app_animated_cross_fade}
/// Abstraction of AnimatedCrossFade to override the layout centering
/// the widgets inside
/// {@endtemplate}
class AppAnimatedCrossFade extends StatelessWidget {
/// {@macro app_animated_cross_fade}
const AppAnimatedCrossFade({
Key? key,
required this.firstChild,
required this.secondChild,
required this.crossFadeState,
}) : super(key: key);

/// First [Widget] to display
final Widget firstChild;

/// Second [Widget] to display
final Widget secondChild;

/// Specifies when to display [firstChild] or [secondChild]
final CrossFadeState crossFadeState;

@override
Widget build(BuildContext context) {
return AnimatedCrossFade(
firstChild: firstChild,
secondChild: secondChild,
crossFadeState: crossFadeState,
duration: const Duration(seconds: 1),
layoutBuilder: (
Widget topChild,
Key topChildKey,
Widget bottomChild,
Key bottomChildKey,
) {
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Align(
alignment: Alignment.center,
key: bottomChildKey,
child: bottomChild,
),
Align(
alignment: Alignment.center,
key: topChildKey,
child: topChild,
),
],
);
},
);
}
}
1 change: 1 addition & 0 deletions packages/photobooth_ui/lib/src/widgets/widgets.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export 'animated_fade_in.dart';
export 'animated_sprite.dart';
export 'app_animated_cross_fade.dart';
export 'app_circular_progress_indicator.dart';
export 'app_dialog.dart';
export 'app_page_view.dart';
Expand Down
16 changes: 14 additions & 2 deletions packages/photobooth_ui/test/src/widgets/animated_sprite_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,23 @@ void main() async {
Flame.images = images;
});

testWidgets('renders SizedBox when loading asset', (tester) async {
testWidgets('renders AppCircularProgressIndicator when loading asset',
(tester) async {
await tester.pumpWidget(AnimatedSprite(
sprites: Sprites(asset: 'test.png', size: Size(1, 1), frames: 1),
));
expect(find.byType(AppCircularProgressIndicator), findsOneWidget);
});

testWidgets(
'does not render AppCircularProgressIndicator'
' when loading asset and showLoadingIndicator is false',
(tester) async {
await tester.pumpWidget(AnimatedSprite(
sprites: Sprites(asset: 'test.png', size: Size(1, 1), frames: 1),
showLoadingIndicator: false,
));
expect(find.byType(SizedBox), findsOneWidget);
expect(find.byType(AppCircularProgressIndicator), findsNothing);
});

testWidgets('renders SpriteAnimationWidget when asset is loaded (loop)',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:photobooth_ui/photobooth_ui.dart';

void main() {
group('AppAnimatedCrossFade', () {
testWidgets('renders both children', (tester) async {
await tester.pumpWidget(
AppAnimatedCrossFade(
firstChild: SizedBox(key: Key('first')),
secondChild: SizedBox(key: Key('second')),
crossFadeState: CrossFadeState.showFirst),
);
expect(find.byKey(Key('first')), findsOneWidget);
expect(find.byKey(Key('second')), findsOneWidget);
});
});
}

0 comments on commit 87c9d69

Please sign in to comment.