From 1e62b3424578150718514aa762f184485dba024a Mon Sep 17 00:00:00 2001 From: Nandaprian Rajasekaran Date: Wed, 24 Jul 2024 21:31:19 +0800 Subject: [PATCH] feat: Loading builder for Route (#3113) This change is mainly to make it easier for specifying loading screen along when routing between component using the ``RouterComponent``. Currently the change is only made on ``Route``, that now have optional ``loadingBuilder`` to Route constructor which would expect a ``Component``, which would be added as the loading page/screen while ``onLoad`` of the ``builder`` (the main routing component) to be completed. --------- Co-authored-by: Lukas Klingsbo --- packages/flame/lib/src/components/route.dart | 25 +++++++++- .../flame/test/components/route_test.dart | 48 +++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/packages/flame/lib/src/components/route.dart b/packages/flame/lib/src/components/route.dart index 6319bcdcfb3..472a39b1081 100644 --- a/packages/flame/lib/src/components/route.dart +++ b/packages/flame/lib/src/components/route.dart @@ -24,9 +24,11 @@ class Route extends PositionComponent with ParentIsA, HasTimeScale { Route( Component Function()? builder, { + Component Function()? loadingBuilder, this.transparent = false, this.maintainState = true, }) : _builder = builder, + _loadingBuilder = loadingBuilder, _renderEffect = Decorator(); /// If true, then the route below this one will continue to be rendered when @@ -53,6 +55,10 @@ class Route extends PositionComponent /// in which case the user must override the [build] method. final Component Function()? _builder; + /// The function that will build the loading page component, which is shown + /// when this route first becomes active, but hasn't fully loaded yet. + final Component Function()? _loadingBuilder; + /// This method is invoked when the route is pushed on top of the /// [RouterComponent]'s stack. /// @@ -118,17 +124,32 @@ class Route extends PositionComponent /// also be added as a child component. Component? _page; + /// The loadingPage that was built and is now owned by this route. The + /// [_loadingPage] will also be added as a child component. + Component? _loadingPage; + /// Additional visual effect that may be applied to the page during rendering. final Decorator _renderEffect; /// Invoked by the [RouterComponent] when this route is pushed to the top - /// of the navigation stack. + /// of the navigation stack @internal void didPush(Route? previousRoute) { - _page ??= build()..addToParent(this); + _page ??= build(); + (_loadingBuilder != null) ? _addLoadingPage() : _page!.addToParent(this); onPush(previousRoute); } + /// Adds the [_loadingPage] to the parent and invoked by [didPush] , when + /// [_loadingBuilder] is specified + Future _addLoadingPage() async { + _loadingPage ??= _loadingBuilder!()..addToParent(this); + await _loadingPage!.loaded; + await add(_page!); + await _page!.loaded; + _loadingPage!.removeFromParent(); + } + /// Invoked by the [RouterComponent] when this route is popped off the top /// of the navigation stack. /// If [maintainState] is false, the page component rendered by this route diff --git a/packages/flame/test/components/route_test.dart b/packages/flame/test/components/route_test.dart index 5933b88c526..18fee009c84 100644 --- a/packages/flame/test/components/route_test.dart +++ b/packages/flame/test/components/route_test.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:ui'; import 'package:flame/components.dart'; @@ -474,6 +475,43 @@ void main() { ); }, ); + testWithFlameGame('Route with loading', (game) async { + final loadingComponent = PositionComponent(size: Vector2.all(100)); + final pageComponent = _HeavyComponent()..size = Vector2.all(100); + final router = RouterComponent( + initialRoute: 'new', + routes: { + 'start': Route( + Component.new, + ), + 'new': Route( + () { + return pageComponent; + }, + loadingBuilder: () { + return loadingComponent; + }, + ), + }, + ); + game.add(router); + await game.ready(); + expect( + pageComponent.isMounted, + isFalse, + ); + expect(loadingComponent.isMounted, isTrue); + pageComponent.completer.complete(); + await game.ready(); + expect( + pageComponent.isMounted, + isTrue, + ); + expect( + loadingComponent.isRemoved, + isTrue, + ); + }); }); } @@ -527,3 +565,13 @@ class _ColoredComponent extends PositionComponent { canvas.drawRect(size.toRect(), _paint); } } + +class _HeavyComponent extends PositionComponent { + Duration dummyTime = const Duration(seconds: 3); + Completer completer = Completer(); + @override + FutureOr onLoad() async { + await completer.future; + return super.onLoad(); + } +}