PersistentBottomNavBar icon indicating copy to clipboard operation
PersistentBottomNavBar copied to clipboard

ModalRoute.of(context).settings.arguements always null

Open evanholt1 opened this issue 3 years ago • 5 comments

in my route, i route from StoreItemsScreen -> StoreItemScreen. both screens are under the "home" tab of the persistent navigation bar. I am using named routes. StoreItemScreen needs to accept an arguement to work, yet ModalRoute.of(context).settings.arguements is always null no matter what i do. i have simplified the code a bit, but still kept all the important bits. Why is ModalRoute.of(context).settings.arguements always null? and how can i pass data to this named screen?

Routes.dart

final Map<String, Widget Function(BuildContext)> AppRoutes = {
  "/storeItems": (_) => StoreItemsScreen(),
  "/storeItem": (context) => StoreItemScreen(),
};

navigation to store item screen Navigator.of(context).pushNamed('/storeItem', arguments: {"item": "test"});

StoreItemScreen

class StoreItemScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
      final args = ModalRoute.of(context)!.settings.arguements;
      print(args); // **--> always null**
      return Scaffold(
        body: Container(),
      );
  }

NavBarItems

 List<PersistentBottomNavBarItem> getNavBarItems(BuildContext context) {
    return [
      PersistentBottomNavBarItem(
        icon: Icon(Icons.person),
        title: (AppLocalizations.of(context)!.profile),
        routeAndNavigatorSettings:
            RouteAndNavigatorSettings(initialRoute: '/profile', defaultTitle: 'profile', routes: AppRoutes),
      ),
      PersistentBottomNavBarItem(
        icon: Icon(Icons.home),
        title: (AppLocalizations.of(context)!.home),
        routeAndNavigatorSettings:
            RouteAndNavigatorSettings(initialRoute: '/home', defaultTitle: 'home', routes: AppRoutes),
      ),
      PersistentBottomNavBarItem(
        icon: Icon(Icons.receipt_outlined),
        title: (AppLocalizations.of(context)!.my_orders),
        routeAndNavigatorSettings:
            RouteAndNavigatorSettings(initialRoute: '/orders', defaultTitle: 'orders', routes: AppRoutes),
      ),
    ];

evanholt1 avatar Jul 31 '21 11:07 evanholt1

I'm struggling to figure out how this is meant to work also, I've set up a replicable example in a repo here.

Here's how I've been attempting to set it up:

main.dart

import 'package:flutter/material.dart';
import 'package:persistent_bottom_nav_bar/persistent-tab-view.dart';
import 'package:flutter_nav_bar_routing/screens/screen_a.dart';
import 'package:flutter_nav_bar_routing/screens/screen_b.dart';

class MyApp extends StatelessWidget {
  @override

  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Persistent Bottom Nav Bar example project',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: AppView(),
    );
  }
}

class AppView extends StatefulWidget {
  @override
  _AppViewState createState() => _AppViewState();
}

class _AppViewState extends State<AppView> {
  final PersistentTabController _controller =
  PersistentTabController(initialIndex: 0);
  String? module;

  @override
  Widget build(BuildContext context) {
    return PersistentTabView(
      context,
      controller: _controller,
      decoration: NavBarDecoration(boxShadow: [BoxShadow()]),
      screens: _buildScreens(),
      items: _navBarsItems(),
      navBarStyle: NavBarStyle.style14,

    );
  }

  List<Widget> _buildScreens() {
    return [
      ScreenA(),
      Container(),
    ];
  }

  List<PersistentBottomNavBarItem> _navBarsItems() {
    return [
      PersistentBottomNavBarItem(
        title: 'Home',
        icon: Icon(Icons.home),
          routeAndNavigatorSettings: RouteAndNavigatorSettings(
            initialRoute: '/page/one',
            routes: {
              '/page/one': (context) => ScreenA(),
              '/page/two': (context) => ScreenB(),
            },
          )
      ),
      PersistentBottomNavBarItem(
          title: 'Help',
          icon: Icon(Icons.help)
      ),
    ];
  }
}

void main() async {
  runApp(new MyApp());
}

screen_a.dart

import 'package:flutter/material.dart';
import 'package:flutter_nav_bar_routing/page_arguments.dart';

class ScreenA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Navigation with params'),
      ),
      body: Center(
        child:  ElevatedButton(
          child: Text('Click me!'),
          onPressed: () {
            Navigator.pushNamed(
              context,
              '/page/two',
              arguments: PageArguments(
                  id: 1,
                  title: "Example Title"
              ),
            );
          },
        ),
      ),
    );
  }
}

screen_b.dart

import 'package:flutter/material.dart';

class ScreenB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final arguments = ModalRoute.of(context)!.settings.arguments;

    print("Route name is:");
    print(ModalRoute.of(context)!.settings.name);
    // The result of this is `/page/one` ? 

    print("Route arguments are:");
    print(arguments);
    // The result of this is `null`. 

    return Scaffold(
      appBar: AppBar(
        title: Text('Parameter example'),
      ),
      body: Center(
        child: Text("Parameter example"),
      ),
    );
  }
}

page_arguments.dart

class PageArguments{
  final int id;
  final String title;
  PageArguments({required this.id, required this.title});
}

Any input would be great, as at this stage I suspect I'm going about it the wrong way, but I haven't been able to find any examples of this implemented.

seanmcn avatar Aug 02 '21 15:08 seanmcn

@Seanmcn this seems like the idea I'm trying to get across, yes. the arguments are always Null, and the Name is wrong. this means i am not currently able to use a named route AND pass arguments around, i have to either use named routes without passing arguments, or not use named routes.

The original idea really was that i wanted to use a very clean-looking way to pass things around with named routes i found in an answer somewhere, where i write in "routes":

routes: {
              '/page/one': (context) => ScreenA(),
              '/page/two': (context) => ScreenB(ModalRoute.of(context).settings.arugments), // here
            },

which would have made things more easier and consistent to use with named routes, but as modalRoute has this issue, this is impossible to do.

evanholt1 avatar Aug 04 '21 09:08 evanholt1

@evanholt1 i have the same problem to pass arguments to another screen, do you how can we resolve this problem? could you fix that?

pishguy avatar Aug 14 '21 04:08 pishguy

@pishguy Sorry, i was not able to find a way to solve this. The way i ended up going for was using "pushNewScreenWithRouteSettings", using they key from the AppRoutes map i defined. example:

AppRoutes.dart

final Map<String, Widget Function(BuildContext)> AppRoutes = {
    "/storeItem" => (context) => StoreItemScreen(),
}

In a button which leads to StoreItemScreen

InkWell(
      onTap: () => pushNewScreenWithRouteSettings(context,
          screen: AppRoutes["/storeItem"]!(context),
          settings: RouteSettings(name: '/storeItem', arguments:  age )),
         ........
         ......
    )

and in StoreItemScreen

  @override
  Widget build(BuildContext context) {
    final int age= ModalRoute.of(context)!.settings.arguments as int;
    .....
    ....

This was not ideal, but the only way i could get things done on time.

evanholt1 avatar Aug 15 '21 17:08 evanholt1

@evanholt1 thanks so much

i can resolve this problem by this solution:

PersistentBottomNavBarItem(
    title: 'Home',
    icon: const Icon(Icons.home),
    routeAndNavigatorSettings: RouteAndNavigatorSettings(
        initialRoute: '/page/one',
        onGenerateRoute: (RouteSettings settings) {
          switch (settings.name) {
            case '/page/one':
              return PageTransition(
                child: ScreenA(),
                type: PageTransitionType.fade,
                duration: const Duration(milliseconds: 50),
                reverseDuration: const Duration(milliseconds: 50),
                settings: settings,
              );
            case '/page/two':
              return PageTransition(
                child: ScreenB(),
                type: PageTransitionType.fade,
                duration: const Duration(milliseconds: 50),
                reverseDuration: const Duration(milliseconds: 50),
                settings: settings,
              );
            default:
              throw Exception('Invalid route: ${settings.name}');
          }
        })),

pishguy avatar Aug 15 '21 18:08 pishguy