csells / go_router

The purpose of the go_router for Flutter is to use declarative routes to reduce complexity, regardless of the platform you're targeting (mobile, web, desktop), handling deep linking from Android, iOS and the web while still allowing an easy-to-use developer experience.
https://gorouter.dev
441 stars 97 forks source link

too many routes for location #269

Closed ebelevics closed 2 years ago

ebelevics commented 2 years ago

Here is small example that causes this issue. I have three routes /todos, /todos/:id, /todos/filters. Why when I go to "/todos/filters" it drops me this exception? P.S. When I think more about it -> it's because "filters" is expected as :id? Shouldn't router use parameter in subroutes as last resort, giving higher priority to static addresses first?

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  App({Key? key}) : super(key: key);

  static const title = 'GoRouter Example: Declarative Routes';

  @override
  Widget build(BuildContext context) => MaterialApp.router(
        routeInformationParser: _router.routeInformationParser,
        routerDelegate: _router.routerDelegate,
        title: title,
      );

  final _router = GoRouter(
    initialLocation: "/todos",
    routes: [
      GoRoute(
        path: '/todos',
        builder: (context, state) => const Page1Screen(),
        routes: [
          GoRoute(
            path: ':id',
            builder: (context, state) => const Page2Screen(),
          ),
          GoRoute(
            path: 'filters',
            builder: (context, state) => const Page3Screen(),
          ),
        ],
      ),
    ],
  );
}

class Page1Screen extends StatelessWidget {
  const Page1Screen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text(App.title)),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () => context.push('/todos/12'),
                child: const Text('Go to details'),
              ),
              ElevatedButton(
                onPressed: () => context.push('/todos/filters'),
                child: const Text('Go to filters'),
              ),
            ],
          ),
        ),
      );
}

class Page2Screen extends StatelessWidget {
  const Page2Screen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text(App.title)),
        body: const Center(
          child: Text("details"),
        ),
      );
}

class Page3Screen extends StatelessWidget {
  const Page3Screen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text(App.title)),
        body: const Center(
          child: Text("filters"),
        ),
      );
}
csells commented 2 years ago

The reason you're having trouble is that /todos/filter is matching the regular expression for :id and filter. If you'd like to make the pattern for :id more restrictive, you shouldn't have that problem, e.g. :id(/d+). You can see the path_to_regexp docs for more details (GR uses path_to_regexp to implement it's path parsing).