Skip to content

Conversation

@GP4cK
Copy link
Contributor

@GP4cK GP4cK commented Mar 10, 2023

Continuing the work of #3269
Fixes flutter/flutter#111909

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the relevant style guides and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use dart format.)
  • I signed the CLA.
  • The title of the PR starts with the name of the package surrounded by square brackets, e.g. [shared_preferences]
  • I listed at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.
  • I updated CHANGELOG.md to add a description of the change, following repository CHANGELOG style.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@GP4cK GP4cK requested a review from chunhtai as a code owner March 10, 2023 17:00
@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat (don't just cc him here, he won't see it! He's on Discord!).

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@GP4cK
Copy link
Contributor Author

GP4cK commented Mar 10, 2023

As mentioned in the other PR, please let me know how you want me to refactor the GoRouterShellGenerator to avoid duplicated code and I'll write a test based on it.
Cheers.

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it will be better if we can refactor GoRouterGenerator to prevent code duplication. I imagine we will add more shellroute subclass in the future


extension $FamilyRouteExtension on FamilyRoute {
static FamilyRoute _fromState(GoRouterState state) => FamilyRoute(
static FamilyRoute _fromState(GoRouterState state) => const FamilyRoute();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feel a bit unnecessary? maybe we don't need to generate extension for shellRoute at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change got reverted with the changes on example/lib/main.dart but if you're talking more generally about the ShellRouteExtension._fromState, it is passed as the factory of ShellRouteData.$route to create $appRoutes.

@GP4cK
Copy link
Contributor Author

GP4cK commented Mar 11, 2023

Yes, it will be better if we can refactor GoRouterGenerator to prevent code duplication. I imagine we will add more shellroute subclass in the future

I've refactored the generator in 76b9b81, let me know what you think.

For the tests, I'm not sure what is the most pertinent. It seems most of the code is already covered by packages/go_router_builder/test/builder_test.dart or packages/go_router_builder/example/test/ensure_build_test.dart. Let me know what use case you want to test.
Thanks!

Copy link

@dorklein dorklein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look good

Copy link

@alanchan-dev alanchan-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks good.

@@ -0,0 +1,134 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a simple test for this example? Just make sure things will compile and the shellRoute's widget is built

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added test in fb449ff

);
}

@TypedShellRoute<MyShellRouteData>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may have missed something, how would one specified navigator key for typedshellRoute and also the parent navigator key for typedGoRoute

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what @johnpryan had in mind was to add a static final or getter called $navigatorKey to the class extending ShellRouteData and GoRouteData and this will be grabbed by the _decodeKey and added as the navigatorKey / parentNavigatorKey (BTW I fixed a small bug here: 6eab717)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static final or getter

It seems a bit not obvious. Why not just let it be a parameter into the
@TypedShellRoute<MyShellRouteData> ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it's not obvious. I assumed that you two had agreed on the approach beforehand and I just picked up the work where it was left off.
So if we go ahead with adding:

  • navigatorKey to TypedShellRoute
  • parentNavigatorKey to TypedGoRoute

I will first need to make a PR on go_router (since that's where these classes are defined) and then raise the minimum version of go_router in go_router_builder.

Could you confirm that it's ok?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that is ok. Sorry about the miscommunication, the original PR was still in its initial state, so there may be some issue that hasn't been discovered yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, I also should have double checked.
Here is the PR on go_router for the first step: #3471

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @chunhtai, actually passing the keys as parameters of TypedShellRoute / TypedGoRoute doesn't work because the keys need to be const and GlobalKey<NavigatorState>() is not...
So is it ok to go ahead with this PR as it is now and I'll make another one to revert #3471?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain a bit why it needs to be const? I can't find the places the enforce the requirement

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chunhtai
Copy link
Contributor

I just learned from @johnpryan that annotation has to be const. Let me think a bit on what else we can do here...

@johnpryan
Copy link
Contributor

Yes, annotations must have a const constructor, so all of the fields must be final and all of the parameters must be const too. Since Keys in Flutter aren't const, I'm not sure how we're going to support navigatorKey or parentNavigatorKey.

@kaankoken
Copy link

Is that a fair assessment of where this is at, at the moment?

That's correct. Right now I'm waiting for @chunhtai or @johnpryan 's advice on how to manage the navigatorKey and the parentNavigatorKey.

We cannot pass them to TypedShellRoute / TypedGoRoute because GlobalKey<NavigatorState>() is not a const. So either we can either:

  1. declare them as a static member called $navigatorKey in the typed route and the generator will pick them up (but it's not obvious for users of this library)
  2. use the workaround described in [go_router_builder] Add parentNavigatorKey to generated routes. flutter#119294 (comment) that clones each routes after the code generation to add the keys before passing them to GoRouter.

I will give a try to this PR since I built around everything with Typed. Thank you for the workaround.

@chunhtai
Copy link
Contributor

@GP4cK

After some thought, I think we can have a getter in the GoRouteData and ShellRouteData

for example:

abstract class GoRouteData extends RouteData {
 ...
 GlobalKey<NavigatorKey> get $parentNavigatorKey => null;
 ...
}

abstract class ShellRouteData extends RouteData {
 ...
 GlobalKey<NavigatorKey> get $navigatorKey => null;
 ...
}

and user can override and provide their navigator key in the subclass if they want. I was hoping this would be more clear than using the static field. At least people can see the getter in the abstract class and notice such thing exists.

WDYT @johnpryan @GP4cK

@GP4cK
Copy link
Contributor Author

GP4cK commented Mar 31, 2023

I'm good with that and it's not too far from the initial implementation.

EDIT: actually the problem is that if the keys are non-static getters, I don't know how to invoke the getter or get the name of the variable returned by it in the generated code.

For example, in this code:

final GlobalKey<NavigatorState> goRouteKey = GlobalKey<NavigatorState>();

class GoRouteWithKey extends GoRouteData {
  const GoRouteWithKey();

  @override
  GlobalKey<NavigatorState>? get navigatorKey => goRouteKey;
}

In the _decodeKey() method, we can find the navigatorKey with

classElement.fields
  .where(whereKeyName)
  .where((FieldElement element) {
    final DartType type = element.type;
    if (type is! ParameterizedType) {
      return false;
    }
    final List<DartType> typeArguments = type.typeArguments;
    if (typeArguments.length != 1) {
      return false;
    }
    final DartType typeArgument = typeArguments.single;
    if (typeArgument.getDisplayString(withNullability: false) ==
        'NavigatorState') {
      return true;
    }
    return false;
  })

But once we have the ref to the navigatorKey, how to know that the return value is goRouteKey?

@kaankoken
Copy link

kaankoken commented Apr 2, 2023

I have a question regarding @TypedShellRoute. Let's say I have two screens: SplashView & HomeView. The first screen shown is SplashView, generated by @TypedGoRouter, after the native splash screen. After SplashView, it will directly transition to HomeView which is generated by @TypedShellGoRouter.

Apparently, to run this flow, I should put HomeView as a childRoute element. If I do not, it does not generate the code.

Code
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); 
final GlobalKey<NavigatorState> _shellNavigatorKey = GlobalKey<NavigatorState>();

final router = GoRouter(
  navigatorKey: _navigatorKey,
  initialLocation: AppRoutes.SPLASH,
  observers: [NavigatorObservers()],
  routes: $appRoutes,
);

/// Splash Route

@immutable
@TypedGoRoute<SplashViewRoute>(path: AppRoutes.SPLASH, routes: [])
class SplashViewRoute extends GoRouteData {
  const SplashViewRoute();

  @override
  Widget build(BuildContext _, GoRouterState state) => const SplashView();
}

///

/// Home Route
@TypedShellRoute(
  routes: [
    TypedGoRoute<SubViewRoute>(path: AppRoutes.HOME),
  ],
)
class HomeViewRoute extends ShellRouteData {
  const HomeViewRoute();

  @override
  Widget builder(BuildContext context, GoRouterState state, Widget navigator) => const SizedBox.shrink();

  @override
  GlobalKey<NavigatorState>? get navigatorKey => _shellNavigatorKey;
}

class SubViewRoute extends GoRouteData {
  const SubViewRoute();

  @override
  Widget build(BuildContext _, GoRouterState state) => const SizedBox.shrink();

  @override
  GlobalKey<NavigatorState>? get navigatorKey => _shellNavigatorKey;
}

If I turned HomeView to @TypedGoRoute, I can navigate without declaring it as a childRoute.
Am I doing something wrong? Should not I at least navigate to SubView?

I am getting this error when I tried to generate a code:


The @TypedGoRoute annotation must have a type parameter that matches the annotated element.
package:routes/routes.dart:38:7
   ╷
38 │ class HomeViewRoute extends ShellRouteData 

edit: pubspec.yaml

environment:
  sdk: ">=2.19.5 <3.0.0"
  flutter: 3.7.9

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  # Routing
  go_router: 6.5.2

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: ^2.0.0

  # Code Generation
  analyzer: 5.10.0
  build_runner: 2.3.3
  build_verify: 3.1.0

  # Builders
  # TODO: do not forget to change this to upstream when its merged
  go_router_builder:
    git:
      url: [email protected]:GP4cK/packages.git
      path: packages/go_router_builder
      ref: feature/shell-route-go-router-builder

flutter:
  uses-material-design: true

@GP4cK @chunhtai

@GP4cK
Copy link
Contributor Author

GP4cK commented Apr 3, 2023

I have a question [...]

@kaankoken, I copy/pasted your code in the example, ran the generator and it did generate some code. The only line I modified was

/// Home Route
-@TypedShellRoute(
+@TypedShellRoute<HomeViewRoute>(

Also, I see that you're overriding the navigatorKey but with the current state of this PR, this will do nothing. You should replace it like this:

-@override
-GlobalKey<NavigatorState>? get navigatorKey => _shellNavigatorKey;
+static final GlobalKey<NavigatorState> $navigatorKey = _shellNavigatorKey;

@chunhtai
Copy link
Contributor

chunhtai commented Apr 3, 2023

yeah I was confused myself how GoRouteData.$route was called. it looks like using static field may be cleanest way to approach this. Sorry for all the detours, let's go with the original proposal.

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work, I left some comments, but overall looks great.

Can you add a section in readme to talk about shellroute and navigatorkey?

Comment on lines +328 to +332
final String navigatorKeyParameterName =
_isShellRoute ? 'navigatorKey' : 'parentNavigatorKey';
final String navigatorKey = _key == null || _key!.isEmpty
? ''
: '$navigatorKeyParameterName: $_key,';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just add an example in the example folder, and add a test for that example to make sure everything works correctly. We also have a test to ensure the .g.dart is always the latest code. So if someone breaks the route_config.dart either the example test will fail or the .g.dart test will fail.

provider: 6.0.5

dev_dependencies:
build_runner: ^2.0.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this need to be a 2.3.0?

Copy link
Contributor Author

@GP4cK GP4cK Apr 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I change it to build_runner: 2.0.0 and run flutter clean && flutter pub get, I get this error:

Because every version of go_router_builder from path depends on analyzer >=4.4.0 <6.0.0 and build_runner >=2.0.0 <2.0.6 depends on analyzer ^1.4.0, go_router_builder from path is incompatible
  with build_runner >=2.0.0 <2.0.6.

Similar issue with 2.1.0. However 2.2.0 works fine. Shall I change to ^2.2.0? This is just the pubspec in the example folder so I don't think it makes much of a difference.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, this is fine then

@@ -0,0 +1,169 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add a test for this? just to make sure the code can actually compile

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a simple test here: Test shell_route_with_keys_example

Widget build(BuildContext context, GoRouterState state) {
// ...
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add a link to the examples

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the link here: Add link to example
It will work when the PR is merged.

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@chunhtai chunhtai added autosubmit Merge PR when tree becomes green via auto submit App and removed needs tests labels Apr 5, 2023
@auto-submit auto-submit bot merged commit 1d1fe12 into flutter:main Apr 5, 2023
@GP4cK GP4cK deleted the feature/shell-route-go-router-builder branch April 6, 2023 00:07
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Apr 6, 2023
InterfaceElement classElement, {
required String keyName,
}) {
bool whereStatic(FieldElement element) => element.isStatic;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why create one-off functions for the tiny ones – but keep the big function inline?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong reason I believe.
Do you want me to make a PR to make everything inline?
Do I need to create an issue for this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issue needed. just a cleanup PR. I'd put everything inline

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I grouped everything under one .where #3698

nploi pushed a commit to nploi/packages that referenced this pull request Jul 16, 2023
…ter#3439)

[go_router_builder] Add ShellRoute support to go_router_builder
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App p: go_router_builder

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[go_router_builder] Add go_router v5 support to go_router_builder

9 participants