Michael Davies
Michael Davies

Reputation: 1672

Named route navigation error: type 'MaterialPageRoute<dynamic>' is not a subtype of type 'Route<String>'

I'm trying to navigate to another page using a named route:

... RaisedButton(
            child: Text('Get image'),
            onPressed: () => Navigator.pushNamed<String>(
                context, '/second'),...

but get the error

> I/flutter (12439): ══╡ EXCEPTION CAUGHT BY GESTURE
> ╞═══════════════════════════════════════════════════════════════════
>     I/flutter (12439): The following assertion was thrown while handling a gesture:
>     I/flutter (12439): type 'MaterialPageRoute<dynamic>' is not a subtype of type 'Route<String>'
>     I/flutter (12439): Either the assertion indicates an error in the framework itself, or we should provide substantially
>     I/flutter (12439): more information in this error message to help you determine and fix the underlying cause.
>     I/flutter (12439): In either case, please report this assertion by filing a bug on GitHub:
>     I/flutter (12439):   https://github.com/flutter/flutter/issues/new?template=BUG.md
>     I/flutter (12439): When the exception was thrown, this was the stack:
>     I/flutter (12439): #0      NavigatorState._routeNamed package:flutter/…/widgets/navigator.dart:1426
>     I/flutter (12439): #1      NavigatorState.pushNamed package:flutter/…/widgets/navigator.dart:1473
>     I/flutter (12439): #2      Navigator.pushNamed package:flutter/…/widgets/navigator.dart:740
>     I/flutter (12439): #3      _CompleterState.build.<anonymous closure> package:my_package/completer.dart:205

The route is defined in the homepage:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'XXXX',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Completer(),
      routes: {
        '/second': (BuildContext context) => SecondPage(),
      },
      onUnknownRoute: (RouteSettings settings) {
        return MaterialPageRoute(
          builder: (BuildContext context) => Completer(),
        );
      },
    );
  }
}

I couldn't find anything on this error message, so I seem to have generated an error that no-one else has seen before.

I'm slowly hacking away at the code to simplify it to the bare minimum, to work out what I've done wrong, but any pointers would be appreciated.

Upvotes: 37

Views: 25257

Answers (5)

S6rra
S6rra

Reputation: 61

In my case, I made it work by changing <String> to <dynamic> , so it looked like this:

Navigator.pushNamed<dynamic>(context, '/second'),

Upvotes: 2

Found out that Navigator.pushNamed(context, '/second') as String works fine with named routes, this would be useful when returning data from a second named page and using it a bit more strictly typed (as everything should be)

Upvotes: 10

TimMutlow
TimMutlow

Reputation: 305

It does make sense to have the type hints, it helps with debugging and lets the IDE flag when the type is not what you expect. (Big fan of strong typing where possible)

And you CAN do this with your named routes - Hooray!

You've implemented your route handling with the routes attribute of the MaterialApp but as you have found there is nowhere to add a return type of the route you are creating.

routes: {
  '/second': (BuildContext context) => SecondPage(),
}

This means any instance passed to it must have return type dynamic and throws an error for anything else.

onGenerateRoute

In order to specify the return type, it takes a little more code and the use of the onGenerateRoute property of MaterialApp:

MaterialApp(
  // ... All the other properties
  // You can still have the simple 'routes' version at the same time
  // so don't need to implement all routes in long-form below
  routes: <String, WidgetBuilder>{
    '/first': (BuildContext context) => FirstPage();
  },
  onGenerateRoute: (RouteSettings settings) {
    final String routeName = settings.name;
    final Map<String, dynamic> args = settings.arguments;  // Get any arguments passed to the route

    switch (routeName) {
      case '/second':
        // Set the Route return value here to correspond with pushNamed<String> for example
        return MaterialPageRoute<String>(
            builder: (BuildContext context) => SecondPage(arg1: args["arg1"]);
            settings: settings,
        );
    }
)

This will now handle the pushNamed<String>('/second') - And There Was Much Rejoicing!

Upvotes: 13

live-love
live-love

Reputation: 52534

Change this:

     MyType result = await Navigator.pushNamed(
                context,
                MyPage.routeName,
                arguments: PageArguments(myArg);

to:

     var result = await Navigator.pushNamed(
                context,
                MyPage.routeName,
                arguments: PageArguments(myArg);

Upvotes: 20

Michael Davies
Michael Davies

Reputation: 1672

Blindingly obvious in hindsight.

I had added the String type-hint in Navigator.pushNamed<String>() as I thought I could receive a typed value back from the call. Removing that and receiving it into a var solved the problem.

Leaving this up for anyone else who gets themselves into the same state.

Upvotes: 60

Related Questions