top of page
shubhangisingh453

Navigation and Routing in Flutter: A Guide to Building Dynamic UIs

Updated: Mar 13, 2023






Flutter is an open-source mobile application development framework that enables the creation of natively compiled applications for mobile, web, and desktop platforms using a single codebase. One of the essential features of any mobile application is the ability to navigate through the app's various screens or pages and switch between them seamlessly. In this guide, we will explore how to implement navigation and routing in Flutter.


Navigation in Flutter


Navigation refers to the process of moving from one screen to another in a Flutter app. The Flutter framework provides several built-in widgets to facilitate navigation in an app, such as Navigator, MaterialPageRoute, and PageRouteBuilder. These widgets work together to create a seamless navigation experience for users.


Using the Navigator widget


The Navigator widget is a stateful widget that manages a stack of Route objects and provides methods to push and pop routes onto and off the stack. The Navigator widget is typically placed at the root of the app's widget tree and is used to navigate between different screens in the app.

Here is an example of using the Navigator widget to navigate between two screens:


class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.red,
        title: Text('Coding age First Screen'),
    leading:ClipOval(
    child: Image.network(
    'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR4NNNQD8Hsj0EywGnRRXhVsasaOVfXAVvxXM-FxQZnLA&s',
    width: 100,
    height: 100,
    ),
      ),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Go to Second Screen'),

          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SecondScreen()),
            );
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.red,
        title: Text('Coding age Second Screen'),
        leading:ClipOval(
          child: Image.network(
            'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR4NNNQD8Hsj0EywGnRRXhVsasaOVfXAVvxXM-FxQZnLA&s',
            width: 100,
            height: 100,
          ),
        ),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Go back to First Screen'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}




In this example, we define two screens: FirstScreen and SecondScreen. FirstScreen has a button that, when pressed, navigates to SecondScreen using the Navigator.push() method. SecondScreen has a button that, when pressed, navigates back to FirstScreen using the Navigator.pop() method.


Using named routes


In larger apps with many screens, it can become difficult to manage navigation using the Navigator.push() and Navigator.pop() methods. To address this issue, Flutter provides the ability to use named routes to navigate between screens.

Here is an example of using named routes to navigate between two screens:




class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => FirstScreen(),
        '/second': (context) => SecondScreen(),
      },
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go to Second Screen'),
          onPressed: () {
            Navigator.pushNamed(context, '/second');
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
        title: Text('Second Screen'),
    ),
    body: Center(
    child: RaisedButton(
    child: Text('Go back to First Screen'),
    onPressed: () {         
    Navigator.pop(context); 
          },   
      ),  
    ),
  );

In this example, we define twoscreens: `FirstScreen` and `SecondScreen`. We also define named routes for each screen using the `routes` parameter in the `MaterialApp` widget. When the button in `FirstScreen` is pressed, we navigate to `SecondScreen` using the `Navigator.pushNamed()` method with the route name '/second'. When the button in `SecondScreen` is pressed, we navigate back to `FirstScreen` using the `Navigator.pop()` method.


Routing in Flutter


Routing refers to the process of matching a URL to a specific screen or page in a Flutter app. Routing is useful for web and desktop applications, where users can use a URL to navigate to a specific page in the app. Flutter provides several built-in widgets to facilitate routing in an app, such as `MaterialApp`, `MaterialPageRoute`, and`PageRouteBuilder`.

Using the MaterialApp widget


The `MaterialApp` widget is a top-level widget that provides navigation and routing capabilities for an app. When a user navigates to a URL, the `MaterialApp` widget matches the URL to a specific screen or page in the app. Here is an example of using the `MaterialApp` widget to route between two screens:


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => HomeScreen(),
        '/about': (context) => AboutScreen(),
      },
      onUnknownRoute: (settings) => MaterialPageRoute(
        builder: (context) => ErrorScreen(),
      ),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.red,
        title: Text('Home Screen'),
        leading:ClipOval(
          child: Image.network(
            'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR4NNNQD8Hsj0EywGnRRXhVsasaOVfXAVvxXM-FxQZnLA&s',
            width: 100,
            height: 100,
          ),
        ),
      ),
      body: Center(
        child: ElevatedButton(
          style: ButtonStyle(
            backgroundColor: MaterialStateProperty.all<Color>(Colors.redAccent),
          ),
          child: Text('Go to About Screen'),
          onPressed: () {
            Navigator.pushNamed(context, '/about');
          },
        ),
      ),
    );
  }
}

class AboutScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('About Screen'),
        backgroundColor: Colors.red,
        leading:ClipOval(
          child: Image.network(
            'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR4NNNQD8Hsj0EywGnRRXhVsasaOVfXAVvxXM-FxQZnLA&s',
            width: 100,
            height: 100,
          ),
        ),
      ),
      body: Center(
        child: ElevatedButton(
          style: ButtonStyle(
            backgroundColor: MaterialStateProperty.all<Color>(Colors.redAccent),
          ),
          child: Text('Go back to Home Screen'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

class ErrorScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.red,
        title: Text('Error Screen'),
        leading:ClipOval(
          child: Image.network(
            'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR4NNNQD8Hsj0EywGnRRXhVsasaOVfXAVvxXM-FxQZnLA&s',
            width: 100,
            height: 100,
          ),
        ),
      ),
      body: Center(
        child: Text('Page not found.'),
      ),
    );
  }
}

Output:




In this example, we define three screens: HomeScreen, AboutScreen, and ErrorScreen. We also define routes for each screen using the routes parameter in the MaterialApp widget. When the user navigates to the URL '/', we display HomeScreen. When the user navigates to the URL '/about', we display AboutScreen. If the user navigates to an unknown URL, we display ErrorScreen using the onUnknownRoute parameter in the MaterialApp widget.


Using the onGenerateRoute callback


In some cases, you may want to perform additional logic when routing to a specific screen or page. For example, you may want to load data from a server before displaying a screen. In these cases, you can use the onGenerateRoute parameter in the MaterialApp widget to perform custom logic when routing to a screen.

The onGenerateRoute parameter of the MaterialApp widget can be used to perform custom logic when routing to a screen in Flutter.

Here's how you can use onGenerateRoute:


Step 1 - Define a named route for the screen that you want to navigate to. For example:

static const String secondScreenRoute = '/secondScreen';

Step 2- In the MaterialApp widget, set the onGenerateRoute parameter to a function that returns a MaterialPageRoute. This function should check the RouteSettings.name property to determine which screen to navigate to and return a MaterialPageRoute with the appropriate widget. For example:


MaterialApp(
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case secondScreenRoute:
return MaterialPageRoute(
builder: (BuildContext context) => SecondScreen(),
settings: settings,
);
// Add more cases for additional screens here
default:
// If there is no such named route, throw an error.
throw Exception('Invalid route: ${settings.name}');
}
},
// Other MaterialApp properties
)

In this example, the onGenerateRoute parameter is set to a function that checks the RouteSettings.name property to determine which screen to navigate to. If the RouteSettings.name is equal to the secondScreenRoute constant defined earlier, a MaterialPageRoute with the SecondScreen widget is returned. Note that if there is no such named route, an Exception is thrown.

Step 3- To navigate to the screen using the named route, use the Navigator.pushNamed method:


Navigator.pushNamed(context, secondScreenRoute);

Using onGenerateRoute allows you to perform custom logic when navigating to a screen, such as loading data from a server or performing authentication checks. It also allows you to define named routes in a central location, making it easier to manage your app's navigation.


Thanks for reading, and happy coding!


bottom of page