From 28a3c15fe27b35e204590063243cbc2771d535d3 Mon Sep 17 00:00:00 2001 From: Jorge Coca Date: Sat, 7 May 2022 15:21:21 -0500 Subject: [PATCH 1/3] refactor: improve performance of loading screen and load I/O Pinball asset (#394) --- assets/images/loading_game/io_pinball.png | Bin 0 -> 7958 bytes .../cubit/assets_manager_cubit.dart | 36 +++-- .../cubit/assets_manager_state.dart | 11 +- .../views/assets_loading_page.dart | 8 +- lib/game/game_assets.dart | 3 +- lib/game/view/pinball_game_page.dart | 52 +++---- lib/gen/assets.gen.dart | 10 ++ lib/l10n/arb/app_en.arb | 4 - pubspec.yaml | 1 + test/app/view/app_test.dart | 1 + .../cubit/assets_manager_cubit_test.dart | 35 ----- .../cubit/assets_manager_state_test.dart | 5 +- test/game/view/pinball_game_page_test.dart | 129 +++++------------- web/index.html | 4 + 14 files changed, 102 insertions(+), 197 deletions(-) create mode 100644 assets/images/loading_game/io_pinball.png delete mode 100644 test/assets_manager/cubit/assets_manager_cubit_test.dart diff --git a/assets/images/loading_game/io_pinball.png b/assets/images/loading_game/io_pinball.png new file mode 100644 index 0000000000000000000000000000000000000000..c8d9fadc37b883aec0428a31d9cc72ee3d7b21fd GIT binary patch literal 7958 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 VX5l8+d@@YaRyR { - /// {@macro assets_manager_cubit} - AssetsManagerCubit(List loadables) - : super( - AssetsManagerState.initial( - loadables: loadables, - ), - ); + AssetsManagerCubit(this._game, this._player) + : super(const AssetsManagerState.initial()); + + final PinballGame _game; + final PinballPlayer _player; - /// Loads the assets Future load() async { + /// Assigning loadables is a very expensive operation. With this purposeful + /// delay here, which is a bit random in duration but enough to let the UI + /// do its job without adding too much delay for the user, we are letting + /// the UI paint first, and then we start loading the assets. + await Future.delayed(const Duration(milliseconds: 300)); + emit( + state.copyWith( + loadables: [ + _game.preFetchLeaderboard(), + ..._game.preLoadAssets(), + ..._player.load(), + ...BonusAnimation.loadAssets(), + ...SelectedCharacter.loadAssets(), + ], + ), + ); final all = state.loadables.map((loadable) async { await loadable; emit(state.copyWith(loaded: [...state.loaded, loadable])); }).toList(); - await Future.wait(all); } } diff --git a/lib/assets_manager/cubit/assets_manager_state.dart b/lib/assets_manager/cubit/assets_manager_state.dart index 8ef1e874..4847adc6 100644 --- a/lib/assets_manager/cubit/assets_manager_state.dart +++ b/lib/assets_manager/cubit/assets_manager_state.dart @@ -11,9 +11,8 @@ class AssetsManagerState extends Equatable { }); /// {@macro assets_manager_state} - const AssetsManagerState.initial({ - required List loadables, - }) : this(loadables: loadables, loaded: const []); + const AssetsManagerState.initial() + : this(loadables: const [], loaded: const []); /// List of futures to load final List loadables; @@ -22,7 +21,11 @@ class AssetsManagerState extends Equatable { final List loaded; /// Returns a value between 0 and 1 to indicate the loading progress - double get progress => loaded.length / loadables.length; + double get progress => + loadables.isEmpty ? 0 : loaded.length / loadables.length; + + /// Only returns false if all the assets have been loaded + bool get isLoading => progress != 1; /// Returns a copy of this instance with the given parameters /// updated diff --git a/lib/assets_manager/views/assets_loading_page.dart b/lib/assets_manager/views/assets_loading_page.dart index ddb76803..4e75a3a5 100644 --- a/lib/assets_manager/views/assets_loading_page.dart +++ b/lib/assets_manager/views/assets_loading_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:pinball/assets_manager/assets_manager.dart'; +import 'package:pinball/gen/gen.dart'; import 'package:pinball/l10n/l10n.dart'; import 'package:pinball_ui/pinball_ui.dart'; @@ -20,10 +21,9 @@ class AssetsLoadingPage extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Text( - l10n.ioPinball, - style: headline1!.copyWith(fontSize: 80), - textAlign: TextAlign.center, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Assets.images.loadingGame.ioPinball.image(), ), const SizedBox(height: 40), AnimatedEllipsisText( diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index ac324417..866564b4 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -1,3 +1,4 @@ +import 'package:flame/extensions.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart' as components; import 'package:pinball_theme/pinball_theme.dart' hide Assets; @@ -5,7 +6,7 @@ import 'package:pinball_theme/pinball_theme.dart' hide Assets; /// Add methods to help loading and caching game assets. extension PinballGameAssetsX on PinballGame { /// Returns a list of assets to be loaded - List preLoadAssets() { + List> preLoadAssets() { const dashTheme = DashTheme(); const sparkyTheme = SparkyTheme(); const androidTheme = AndroidTheme(); diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index bb190668..dc36c74c 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -21,12 +21,6 @@ class PinballGamePage extends StatelessWidget { final bool isDebugMode; - static Route route({bool isDebugMode = kDebugMode}) { - return MaterialPageRoute( - builder: (_) => PinballGamePage(isDebugMode: isDebugMode), - ); - } - @override Widget build(BuildContext context) { final characterThemeBloc = context.read(); @@ -48,53 +42,39 @@ class PinballGamePage extends StatelessWidget { l10n: context.l10n, gameBloc: gameBloc, ); - - final loadables = [ - game.preFetchLeaderboard(), - ...game.preLoadAssets(), - ...player.load(), - ...BonusAnimation.loadAssets(), - ...SelectedCharacter.loadAssets(), - ]; - - return BlocProvider( - create: (_) => AssetsManagerCubit(loadables)..load(), - child: PinballGameView(game: game), + return Container( + decoration: const CrtBackground(), + child: Scaffold( + backgroundColor: PinballColors.transparent, + body: BlocProvider( + create: (_) => AssetsManagerCubit(game, player)..load(), + child: PinballGameView(game), + ), + ), ); } } class PinballGameView extends StatelessWidget { - const PinballGameView({ - Key? key, - required this.game, - }) : super(key: key); + const PinballGameView(this.game, {Key? key}) : super(key: key); final PinballGame game; @override Widget build(BuildContext context) { - final isLoading = context.select( - (AssetsManagerCubit bloc) => bloc.state.progress != 1, - ); - return Container( - decoration: const CrtBackground(), - child: Scaffold( - backgroundColor: PinballColors.transparent, - body: isLoading + return BlocBuilder( + builder: (context, state) { + return state.isLoading ? const AssetsLoadingPage() - : PinballGameLoadedView(game: game), - ), + : PinballGameLoadedView(game); + }, ); } } @visibleForTesting class PinballGameLoadedView extends StatelessWidget { - const PinballGameLoadedView({ - Key? key, - required this.game, - }) : super(key: key); + const PinballGameLoadedView(this.game, {Key? key}) : super(key: key); final PinballGame game; diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 33d2bbd1..f0b6fdeb 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -15,6 +15,8 @@ class $AssetsImagesGen { $AssetsImagesComponentsGen get components => const $AssetsImagesComponentsGen(); $AssetsImagesLinkBoxGen get linkBox => const $AssetsImagesLinkBoxGen(); + $AssetsImagesLoadingGameGen get loadingGame => + const $AssetsImagesLoadingGameGen(); $AssetsImagesScoreGen get score => const $AssetsImagesScoreGen(); } @@ -62,6 +64,14 @@ class $AssetsImagesLinkBoxGen { const AssetGenImage('assets/images/link_box/info_icon.png'); } +class $AssetsImagesLoadingGameGen { + const $AssetsImagesLoadingGameGen(); + + /// File path: assets/images/loading_game/io_pinball.png + AssetGenImage get ioPinball => + const AssetGenImage('assets/images/loading_game/io_pinball.png'); +} + class $AssetsImagesScoreGen { const $AssetsImagesScoreGen(); diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 839da492..2d30d03a 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -148,10 +148,6 @@ "@loading": { "description": "Text shown to indicate loading times" }, - "ioPinball": "I/O Pinball", - "@ioPinball": { - "description": "I/O Pinball - Name of the game" - }, "enter": "Enter", "@enter": { "description": "Text shown on the mobile controls enter button" diff --git a/pubspec.yaml b/pubspec.yaml index 1a025d4a..32a412a0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,7 @@ flutter: - assets/images/bonus_animation/ - assets/images/score/ - assets/images/link_box/ + - assets/images/loading_game/ flutter_gen: line_length: 80 diff --git a/test/app/view/app_test.dart b/test/app/view/app_test.dart index 4f04a89d..0f34b66e 100644 --- a/test/app/view/app_test.dart +++ b/test/app/view/app_test.dart @@ -35,6 +35,7 @@ void main() { pinballPlayer: pinballPlayer, ), ); + await tester.pump(const Duration(milliseconds: 400)); expect(find.byType(PinballGamePage), findsOneWidget); }); }); diff --git a/test/assets_manager/cubit/assets_manager_cubit_test.dart b/test/assets_manager/cubit/assets_manager_cubit_test.dart deleted file mode 100644 index 27d9cedb..00000000 --- a/test/assets_manager/cubit/assets_manager_cubit_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'dart:async'; - -import 'package:bloc_test/bloc_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/assets_manager/assets_manager.dart'; - -void main() { - group('AssetsManagerCubit', () { - final completer1 = Completer(); - final completer2 = Completer(); - - final future1 = completer1.future; - final future2 = completer2.future; - - blocTest( - 'emits the loaded on the order that they load', - build: () => AssetsManagerCubit([future1, future2]), - act: (cubit) { - cubit.load(); - completer2.complete(); - completer1.complete(); - }, - expect: () => [ - AssetsManagerState( - loadables: [future1, future2], - loaded: [future2], - ), - AssetsManagerState( - loadables: [future1, future2], - loaded: [future2, future1], - ), - ], - ); - }); -} diff --git a/test/assets_manager/cubit/assets_manager_state_test.dart b/test/assets_manager/cubit/assets_manager_state_test.dart index 4882f880..41e94add 100644 --- a/test/assets_manager/cubit/assets_manager_state_test.dart +++ b/test/assets_manager/cubit/assets_manager_state_test.dart @@ -13,12 +13,11 @@ void main() { }); test('has the correct initial state', () { - final future = Future.value(); expect( - AssetsManagerState.initial(loadables: [future]), + AssetsManagerState.initial(), equals( AssetsManagerState( - loadables: [future], + loadables: const [], loaded: const [], ), ), diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index fbcf8c76..8c6d3042 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -82,14 +82,26 @@ void main() { ); }); - testWidgets('renders PinballGameView', (tester) async { - await tester.pumpApp( - PinballGamePage(), - characterThemeCubit: characterThemeCubit, - gameBloc: gameBloc, - ); + group('renders PinballGameView', () { + testWidgets('with debug mode turned on', (tester) async { + await tester.pumpApp( + PinballGamePage(), + characterThemeCubit: characterThemeCubit, + gameBloc: gameBloc, + ); + + expect(find.byType(PinballGameView), findsOneWidget); + }); - expect(find.byType(PinballGameView), findsOneWidget); + testWidgets('with debug mode turned off', (tester) async { + await tester.pumpApp( + PinballGamePage(isDebugMode: false), + characterThemeCubit: characterThemeCubit, + gameBloc: gameBloc, + ); + + expect(find.byType(PinballGameView), findsOneWidget); + }); }); testWidgets( @@ -106,9 +118,7 @@ void main() { initialState: initialAssetsState, ); await tester.pumpApp( - PinballGameView( - game: game, - ), + PinballGameView(game), assetsManagerCubit: assetsManagerCubit, characterThemeCubit: characterThemeCubit, ); @@ -138,9 +148,7 @@ void main() { ); await tester.pumpApp( - PinballGameView( - game: game, - ), + PinballGameView(game), assetsManagerCubit: assetsManagerCubit, characterThemeCubit: characterThemeCubit, gameBloc: gameBloc, @@ -151,61 +159,6 @@ void main() { expect(find.byType(PinballGameLoadedView), findsOneWidget); }); - - group('route', () { - Future pumpRoute({ - required WidgetTester tester, - required bool isDebugMode, - }) async { - await tester.pumpApp( - Scaffold( - body: Builder( - builder: (context) { - return ElevatedButton( - onPressed: () { - Navigator.of(context).push( - PinballGamePage.route( - isDebugMode: isDebugMode, - ), - ); - }, - child: const Text('Tap me'), - ); - }, - ), - ), - characterThemeCubit: characterThemeCubit, - gameBloc: gameBloc, - ); - - await tester.tap(find.text('Tap me')); - - // We can't use pumpAndSettle here because the page renders a Flame game - // which is an infinity animation, so it will timeout - await tester.pump(); // Runs the button action - await tester.pump(); // Runs the navigation - } - - testWidgets('route creates the correct non debug game', (tester) async { - await pumpRoute(tester: tester, isDebugMode: false); - expect( - find.byWidgetPredicate( - (w) => w is PinballGameView && w.game is! DebugPinballGame, - ), - findsOneWidget, - ); - }); - - testWidgets('route creates the correct debug game', (tester) async { - await pumpRoute(tester: tester, isDebugMode: true); - expect( - find.byWidgetPredicate( - (w) => w is PinballGameView && w.game is DebugPinballGame, - ), - findsOneWidget, - ); - }); - }); }); group('PinballGameView', () { @@ -230,7 +183,7 @@ void main() { testWidgets('renders game', (tester) async { await tester.pumpApp( - PinballGameView(game: game), + PinballGameView(game), gameBloc: gameBloc, startGameBloc: startGameBloc, ); @@ -258,7 +211,7 @@ void main() { ); await tester.pumpApp( - PinballGameView(game: game), + PinballGameView(game), gameBloc: gameBloc, startGameBloc: startGameBloc, ); @@ -276,7 +229,6 @@ void main() { final gameState = GameState.initial().copyWith( status: GameStatus.gameOver, ); - whenListen( startGameBloc, Stream.value(startGameState), @@ -287,17 +239,12 @@ void main() { Stream.value(gameState), initialState: gameState, ); - await tester.pumpApp( - PinballGameView(game: game), + Material(child: PinballGameView(game)), gameBloc: gameBloc, startGameBloc: startGameBloc, ); - - expect( - find.byType(GameHud), - findsNothing, - ); + expect(find.byType(GameHud), findsNothing); }); testWidgets('keep focus on game when mouse hovers over it', (tester) async { @@ -307,7 +254,6 @@ void main() { final gameState = GameState.initial().copyWith( status: GameStatus.gameOver, ); - whenListen( startGameBloc, Stream.value(startGameState), @@ -319,28 +265,24 @@ void main() { initialState: gameState, ); await tester.pumpApp( - PinballGameView(game: game), + Material(child: PinballGameView(game)), gameBloc: gameBloc, startGameBloc: startGameBloc, ); - game.focusNode.unfocus(); await tester.pump(); - expect(game.focusNode.hasFocus, isFalse); - final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); await gesture.addPointer(location: Offset.zero); addTearDown(gesture.removePointer); await gesture.moveTo((game.size / 2).toOffset()); await tester.pump(); - expect(game.focusNode.hasFocus, isTrue); }); testWidgets('mobile controls when the overlay is added', (tester) async { await tester.pumpApp( - PinballGameView(game: game), + PinballGameView(game), gameBloc: gameBloc, startGameBloc: startGameBloc, ); @@ -357,23 +299,17 @@ void main() { final gameState = GameState.initial().copyWith( status: GameStatus.gameOver, ); - whenListen( gameBloc, Stream.value(gameState), initialState: gameState, ); - await tester.pumpApp( - PinballGameView(game: game), + Material(child: PinballGameView(game)), gameBloc: gameBloc, startGameBloc: startGameBloc, ); - - expect( - find.image(Assets.images.linkBox.infoIcon), - findsOneWidget, - ); + expect(find.image(Assets.images.linkBox.infoIcon), findsOneWidget); }); testWidgets('opens MoreInformationDialog when tapped', (tester) async { @@ -386,16 +322,13 @@ void main() { initialState: gameState, ); await tester.pumpApp( - PinballGameView(game: game), + Material(child: PinballGameView(game)), gameBloc: gameBloc, startGameBloc: startGameBloc, ); await tester.tap(find.byType(IconButton)); await tester.pump(); - expect( - find.byType(MoreInformationDialog), - findsOneWidget, - ); + expect(find.byType(MoreInformationDialog), findsOneWidget); }); }); }); diff --git a/web/index.html b/web/index.html index f60ae7ce..30eb9080 100644 --- a/web/index.html +++ b/web/index.html @@ -76,6 +76,10 @@ application. For more information, see: https://developers.google.com/web/fundamentals/primers/service-workers -->