JaspervanRiet / duck_router

A Flutter router with intents
MIT License
4 stars 3 forks source link

Error when going from login page to sign up page #42

Closed rlee1990 closed 1 day ago

rlee1990 commented 1 day ago

I receive the below error when going from login page to sign up page:

Here is how I navigate:

DuckRouter.of(context)
                                .navigate(to: SignupLocation(), root: true);
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building HeroControllerScope:
'package:flutter/src/widgets/navigator.dart': Failed assertion: line 3925 pos 18: '!keyReservation.contains(key)': is not true.

Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.yml

The relevant error-causing widget was:
    MaterialApp MaterialApp:file:///Users/rickeylee/Desktop/supabase_social_jawn_test/lib/main.dart:358:32

When the exception was thrown, this was the stack:
#2      NavigatorState._debugCheckDuplicatedPageKeys.<anonymous closure> (package:flutter/src/widgets/navigator.dart:3925:18)
navigator.dart:3925
#3      NavigatorState._debugCheckDuplicatedPageKeys (package:flutter/src/widgets/navigator.dart:3930:6)
navigator.dart:3930
#4      NavigatorState._updatePages.<anonymous closure> (package:flutter/src/widgets/navigator.dart:3993:7)
navigator.dart:3993
#5      NavigatorState._updatePages (package:flutter/src/widgets/navigator.dart:3996:6)
navigator.dart:3996
#6      NavigatorState.didUpdateWidget (package:flutter/src/widgets/navigator.dart:3911:7)
navigator.dart:3911
#7      StatefulElement.update (package:flutter/src/widgets/framework.dart:5789:55)
framework.dart:5789
#8      Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
framework.dart:3941
#9      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
framework.dart:5642
#10     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
framework.dart:5333
#11     ProxyElement.update (package:flutter/src/widgets/framework.dart:5946:5)
framework.dart:5946
#12     Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
framework.dart:3941
#13     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
framework.dart:5642
#14     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5780:11)
framework.dart:5780
#15     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
framework.dart:5333
#16     StatefulElement.update (package:flutter/src/widgets/framework.dart:5803:5)
framework.dart:5803
#17     Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
framework.dart:3941
#18     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
framework.dart:5642
#19     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
framework.dart:5333
#20     ProxyElement.update (package:flutter/src/widgets/framework.dart:5946:5)
framework.dart:5946
#21     Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
framework.dart:3941
#22     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
framework.dart:5642
#23     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
framework.dart:5333
#24     StatelessElement.update (package:flutter/src/widgets/framework.dart:5693:5)
framework.dart:5693
#25     Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
framework.dart:3941
#26     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
framework.dart:5642
#27     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
framework.dart:5333
#28     ProxyElement.update (package:flutter/src/widgets/framework.dart:5946:5)
framework.dart:5946
#29     Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
framework.dart:3941
#30     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
framework.dart:5642
#31     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
framework.dart:5333
#32     ProxyElement.update (package:flutter/src/widgets/framework.dart:5946:5)
framework.dart:5946
#33     Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
framework.dart:3941
#34     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
framework.dart:5642
#35     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5780:11)
framework.dart:5780
#36     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
framework.dart:5333
#37     BuildScope._tryRebuild (package:flutter/src/widgets/framework.dart:2693:15)
framework.dart:2693
#38     BuildScope._flushDirtyElements (package:flutter/src/widgets/framework.dart:2752:11)
framework.dart:2752
#39     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:3048:18)
framework.dart:3048
#40     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:1162:21)
binding.dart:1162
#41     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:468:5)
binding.dart:468
#42     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1397:15)
binding.dart:1397
#43     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1318:9)
binding.dart:1318
#44     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1176:5)
binding.dart:1176
#45     _invoke (dart:ui/hooks.dart:312:13)
hooks.dart:312
#46     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:419:5)
platform_dispatcher.dart:419
#47     _drawFrame (dart:ui/hooks.dart:283:31)
hooks.dart:283
(elided 2 frames from class _AssertionError)

════════════════════════════════════════════════════════════════════════════════

My Route:

final socialRouter = DuckRouter(
  initialLocation: LoadingLocation(),
  onNavigate: (destination) {},
  interceptors: [
    AuthInterceptor(),
  ],
);

class HomeLocation extends Location {
  @override
  String get path => 'home';

  @override
  LocationBuilder? get builder => (context) => const Home();
}

class ChatLocation extends Location {
  @override
  String get path => 'mesages';

  @override
  LocationBuilder? get builder => (context) => const Chat();
}

class NotificationLocation extends Location {
  @override
  String get path => 'notifications';

  @override
  LocationBuilder? get builder => (context) => const Notifications();
}

class HelpLocation extends Location {
  @override
  String get path => 'help';

  @override
  LocationBuilder? get builder => (context) => const Help();
}

class SearchLocation extends Location {
  final String? query;

  const SearchLocation(this.query);
  @override
  String get path => query != null ? 'search/?query=$query' : 'search';

  @override
  LocationBuilder? get builder => (context) => SearchPage(
        query: query,
      );
}

class ProfileLocation extends Location {
  final String id;

  const ProfileLocation({required this.id});

  @override
  String get path => 'profile/$id';

  @override
  LocationBuilder? get builder => (context) => ProfileView(id: id);
}

class EditProfileLocation extends Location {
  final String id;

  const EditProfileLocation({required this.id});

  @override
  String get path => 'edit/$id';

  @override
  LocationBuilder? get builder => (context) => EditProfile(id: id);
}

class FollowerFollowingLocation extends Location {
  final String id;

  const FollowerFollowingLocation({required this.id});

  @override
  String get path => 'follow/$id';

  @override
  LocationBuilder? get builder => (context) => FollowerFollowingView(id: id);
}

class PostFullLocation extends Location {
  final String postId;
  final bool isProfileFeed;
  final String? commentId;
  final bool isBookmark;
  const PostFullLocation({
    required this.postId,
    required this.isProfileFeed,
    this.isBookmark = false,
    this.commentId,
  });

  @override
  String get path => 'post/$postId';

  @override
  LocationBuilder? get builder => (context) => PostFullView(
        isProfileFeed: isProfileFeed,
        postId: postId,
        commentId: commentId,
        isBookmark: isBookmark,
      );
}

class ReactionFullViewLocation extends Location {
  final String parentId;
  final bool isComment;
  const ReactionFullViewLocation({
    required this.parentId,
    required this.isComment,
  });

  @override
  String get path => 'reactions/$parentId';

  @override
  LocationBuilder? get builder => (context) =>
      ReactionListFullScreen(isComment: isComment, parentId: parentId);
}

class PostFileFullLocation extends Location {
  final int index;
  final List<PostFile> files;

  const PostFileFullLocation({required this.index, required this.files});

  @override
  String get path => 'post/files/$index';

  @override
  LocationBuilder? get builder =>
      (context) => CarouselFullScreen(index: index, files: files);
}

class MessagesLocation extends Location {
  final String channelId;
  final String? messageId;

  const MessagesLocation({required this.channelId, this.messageId});

  @override
  String get path => channelId;

  @override
  LocationBuilder? get builder => (context) => MessagesView(
        channelId: channelId,
        messageId: messageId,
      );
}

class MessagesDetailLocation extends Location {
  final String channelId;

  const MessagesDetailLocation({required this.channelId});

  @override
  String get path => 'details/$channelId';

  @override
  LocationBuilder? get builder =>
      (context) => ChatDetails(channelId: channelId);
}

class AddMembersLocation extends Location {
  final String channelId;

  const AddMembersLocation({required this.channelId});

  @override
  String get path => 'add-members/$channelId';

  @override
  LocationBuilder? get builder => (context) => AddMembers(channelId: channelId);
}

class ChatMediaLocation extends Location {
  final String channelId;

  const ChatMediaLocation({required this.channelId});

  @override
  String get path => 'media/$channelId';

  @override
  LocationBuilder? get builder => (context) => ChatMedia(channelId: channelId);
}

class ChatMemberLocation extends Location {
  final String channelId;

  const ChatMemberLocation({required this.channelId});

  @override
  String get path => 'members/$channelId';

  @override
  LocationBuilder? get builder => (context) => MemberList(channelId: channelId);
}

class ChatMemberRequestLocation extends Location {
  final String channelId;

  const ChatMemberRequestLocation({required this.channelId});

  @override
  String get path => 'request/$channelId';

  @override
  LocationBuilder? get builder =>
      (context) => MemberRequest(channelId: channelId);
}

class ChatPinnedMessageLocation extends Location {
  final String channelId;

  const ChatPinnedMessageLocation({required this.channelId});

  @override
  String get path => 'pinned/$channelId';

  @override
  LocationBuilder? get builder =>
      (context) => PinnedMessages(channelId: channelId);
}

class SettingsLocation extends Location {
  @override
  String get path => 'settings';

  @override
  LocationBuilder? get builder => (context) => Settings();
}

class PrivacyLocation extends Location {
  @override
  String get path => 'privacy';

  @override
  LocationBuilder? get builder => (context) => Privacy();
}

class BookmarkLocation extends Location {
  @override
  String get path => 'bookmarks';

  @override
  LocationBuilder? get builder => (context) => Bookmarks();
}

class BlockListLocation extends Location {
  @override
  String get path => 'block-list';

  @override
  LocationBuilder? get builder => (context) => BlockedList();
}

class VerificationLocation extends Location {
  @override
  String get path => 'verification';

  @override
  LocationBuilder? get builder => (context) => VerificationHome();
}

class ChatSearchLocation extends Location {
  @override
  String get path => 'search';

  @override
  LocationBuilder? get builder => (context) => ChatSearchList();
}

class CreateChatLocation extends Location {
  @override
  String get path => 'create-chat';

  @override
  LocationBuilder? get builder => (context) => CreateChat();
}

class CameraLocation extends Location {
  final bool? fromStory;
  final bool isProfilePic;
  final bool allowVideo;

  const CameraLocation(
      {this.fromStory, required this.isProfilePic, required this.allowVideo});

  @override
  String get path => 'camera';

  @override
  LocationBuilder? get builder => (context) =>
      CameraView(allowVideo: allowVideo, isProfilePic: isProfilePic);
}

class CreatePostLocation extends Location {
  final bool fromProfile;
  final String? userId;
  final String? username;
  final String? communityId;
  final bool shared;
  final bool edit;
  final PostView? post;

  const CreatePostLocation(
      {required this.fromProfile,
      this.userId,
      this.username,
      this.communityId,
      this.shared = false,
      this.edit = false,
      this.post});

  @override
  String get path => 'create-post';

  @override
  LocationBuilder? get builder => (context) => CreatePost(
        fromProfile: fromProfile,
        userId: userId,
        username: username,
        communityId: communityId,
        shared: shared,
        edit: edit,
        post: post,
      );
}

class LoginLocation extends Location {
  @override
  String get path => 'login';

  @override
  LocationBuilder? get builder => (context) => Login();
}

class SignupLocation extends Location {
  @override
  String get path => 'signup';

  @override
  LocationBuilder? get builder => (context) => Signup();
}

class EmailVerificationLocation extends Location {
  final String email;

  const EmailVerificationLocation({required this.email});

  @override
  String get path => 'email-verify/$email';

  @override
  LocationBuilder? get builder => (context) => EmailVerification(email: email);
}

class DialogPage<T> extends DuckPage<T> {
  const DialogPage({
    required super.name,
    required this.builder,
    super.key,
    super.arguments,
    super.restorationId,
  }) : super.custom();

  final WidgetBuilder builder;

  @override
  Route<T> createRoute(BuildContext context) => DialogRoute<T>(
        context: context,
        settings: this,
        builder: (context) => Dialog(
          child: builder(context),
        ),
      );
}

class NavigationLocation extends StatefulLocation {
  @override
  StatefulLocationBuilder get childBuilder =>
      (context, shell) => TabbarViewScaffold(shell: shell);

  @override
  List<Location> get children => [
        HomeLocation(),
        ChatLocation(),
        NotificationLocation(),
        SearchLocation(null),
      ];

  @override
  String get path => '/';
}

class LoadingLocation extends Location {
  @override
  String get path => 'loading';

  @override
  LocationBuilder? get builder => (context) => const StartUpView();
}

class AuthInterceptor extends LocationInterceptor {
  @override
  Location? execute(Location to, Location? from) {
    log('To: ${to.path}, from: ${from?.path}');
    if (to is! LoginLocation ||
        to is! SignupLocation ||
        to is! EmailVerificationLocation ||
        to is! LoadingLocation) {
      if (serviceLocator<SupabaseClient>().auth.currentSession == null) {
        return LoginLocation();
      }
    }
    return null;
  }
}
rlee1990 commented 1 day ago

I can navigate just fine when I am already signed in.

rlee1990 commented 1 day ago

Not sure if the issue is the AuthInterceptor or that I have a loading page.

rlee1990 commented 1 day ago

I can confirm the issue is with the AuthInterceptor and will look into this.