-1

I've been working on a Flutter app for a few days now.

I've implemented a NavigationRail for my app. It works pretty well, excepted for one thing.

When I tap on a NavigationRailDestination, it calls the function updateRoute(index) and goes to the required route.

My problem is that, the selectedIndex property udpates, but then, when the new route is loaded, the selectedIndex goes back to 0.

Here is my code :

class SidebarDrawer extends StatefulWidget {

  final VoidCallback callbackOnScreenChange;

  SidebarDrawer({@required this.callbackOnScreenChange});

  @override
  _SidebarDrawerState createState() => _SidebarDrawerState();
}

class _SidebarDrawerState extends State<SidebarDrawer> {

  int index = 0;
  
  @override
  Widget build(BuildContext context) => NavigationRail(
        extended: isExtended,
        onDestinationSelected: (index) => updateRoute(index),
        selectedIndex: index,
        leading: IconButton(
          icon: Icon(Icons.reorder),
          onPressed: () => setState(() => isExtended = !isExtended),
        ),
        destinations: <NavigationRailDestination>[
          <Some NavigationRailDestination>
        ],
      );

  void updateRoute(index) {
  
    setState(() => this.index = index);
    
    switch (index) {
      case 0:
        return navigateToScreen(
            context, '/home', widget.callbackOnScreenChange);
      case 1:
        return navigateToScreen(
            context, '/orderManager', widget.callbackOnScreenChange);
      case 2:
        return navigateToScreen(
            context, '/manualControl', widget.callbackOnScreenChange);
      default:
        return navigateToScreen(
            context, '/home', widget.callbackOnScreenChange);
    }
  }
  
  

The function navigateToScreen() :

/// Navigate to given widget
void navigateToScreen(BuildContext context, String route,
    VoidCallback callbackOnScreenChange) async {
  // Call callback on screen change
  if (callbackOnScreenChange != null) {
    callbackOnScreenChange();
  }

  // Check the current route
  String currentRoute = ModalRoute.of(context).settings.name;

  // If the current route is not the same as the new route change the screen
  if (currentRoute != route) {
    await Navigator.pushNamed(context, route);
  }
}

I am new to Flutter, I am struggling a bit to understand how does the state works and updates.

I would really appreciate if you could help me understand it please !

1 Answers1

0

Ok, I've managed to correct it.

In my parent widget (MainScaffold), I pass an index value to my child widget (containing the NavigationRail).

Then in my NavigationRail Widget, instead of having the property : selectedIndex: index I set it to selectedIndex: widget.selectedDrawerIndex.

Then on each screen, when I render the main scaffold, I set the required selectedDrawerIndex value.

Here is the code from the sidebar Widget :

class SidebarDrawer extends StatefulWidget {
  // Index is used to know which icon is selected
  final int selectedDrawerIndex;
  final VoidCallback callbackOnScreenChange;

  SidebarDrawer({@required this.selectedDrawerIndex, @required this.callbackOnScreenChange});

  @override
  _SidebarDrawerState createState() => _SidebarDrawerState();
}

class _SidebarDrawerState extends State<SidebarDrawer> {

  @override
  Widget build(BuildContext context) => NavigationRail(
        selectedIndex: widget.selectedDrawerIndex,
        leading: IconButton(
          icon: Icon(Icons.reorder),
          onPressed: () => setState(() => isExtended = !isExtended),
        ),
        // When a destination is selected, the routes are updated
        onDestinationSelected: (selectedDrawerIndex) => updateRoute(selectedDrawerIndex),
        destinations: <NavigationRailDestination>[
          [...some navigation rail destinations...]
        ],
      );

  void updateRoute(selectedDrawerIndex) {
    // Navigate to a specific screen when a destination is clicked
    switch (selectedDrawerIndex) {
      case 0:
        return navigateToScreen(
            context, '/home', widget.callbackOnScreenChange);
      case 1:
        return navigateToScreen(
            context, '/orderManager', widget.callbackOnScreenChange);
      case 2:
        return navigateToScreen(
            context, '/manualControl', 
      default:
        return navigateToScreen(
            context, '/home', widget.callbackOnScreenChange);
    }
  }
}

And then in the main scaffold:

class MainScaffold extends StatelessWidget {
  final int selectedDrawerIndex;
  final Widget body;
  // Callback when a screen change is requested
  // This is the current way to make sure a screen calls its dispose method when the screen changes
  final VoidCallback callbackOnScreenChange;

  MainScaffold(
      {@required this.selectedDrawerIndex,
      @required this.body,
      this.callbackOnScreenChange});
      
  [...some more code...]
}

Then I just need to pass the selectedDrawerIndex as parameter when using the MainScaffold Widget.

It works well but I am willing to improve so if you think about a better solution, do not hesitate to post it.