Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      This week in AI updates: Mistral’s new Le Chat features, ChatGPT updates, and more (September 5, 2025)

      September 6, 2025

      Designing For TV: Principles, Patterns And Practical Guidance (Part 2)

      September 5, 2025

      Neo4j introduces new graph architecture that allows operational and analytics workloads to be run together

      September 5, 2025

      Beyond the benchmarks: Understanding the coding personalities of different LLMs

      September 5, 2025

      Hitachi Energy Pledges $1B to Strengthen US Grid, Build Largest Transformer Plant in Virginia

      September 5, 2025

      How to debug a web app with Playwright MCP and GitHub Copilot

      September 5, 2025

      Between Strategy and Story: Thierry Chopain’s Creative Path

      September 5, 2025

      What You Need to Know About CSS Color Interpolation

      September 5, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Why browsers throttle JavaScript timers (and what to do about it)

      September 6, 2025
      Recent

      Why browsers throttle JavaScript timers (and what to do about it)

      September 6, 2025

      How to create Google Gemini AI component in Total.js Flow

      September 6, 2025

      Drupal 11’s AI Features: What They Actually Mean for Your Team

      September 5, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Harnessing GitOps on Linux for Seamless, Git-First Infrastructure Management

      September 6, 2025
      Recent

      Harnessing GitOps on Linux for Seamless, Git-First Infrastructure Management

      September 6, 2025

      How DevOps Teams Are Redefining Reliability with NixOS and OSTree-Powered Linux

      September 5, 2025

      Distribution Release: Linux Mint 22.2

      September 4, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Get Started With GoRouter in Flutter

    How to Get Started With GoRouter in Flutter

    September 4, 2025

    Navigating between screens in Flutter is crucial for any app. And while the built-in Navigator API provides functionality, it can get complex for larger projects.

    This is where go_router shines, offering a more declarative, URL-based, and feature-rich navigation system. This article delves deep into every detail of go_router, guiding you from setup to advanced features like redirection and nested routes.

    go_router is a flexible and lightweight routing library for Flutter that simplifies the navigation process and provides a clean API for managing routes, passing parameters, and handling redirects. It’s designed to be easy to use while offering advanced features for more complex navigation requirements.

    Navigation plays a crucial role in crafting seamless user experiences. While the built-in Navigator 2.0 offers versatility, it can become complex in larger projects. Here’s where go_router steps in and helps simplify the process significantly.

    Table of Contents:

    1. Prerequisites

    2. What We’ll Build

    3. Installation

    4. How to Define Routes

    5. How to Create the Router

    6. How to Navigate Between Screens

    7. How to Pass Parameters

    8. Sub-Routes and ShellRoute

    9. Redirection and Guards

    10. How to Set Up a Real Flutter Project using GoRouter

    11. Project Structure:

      • 1. main.dart

      • 2. Model

      • 3. Controller

      • 4. Config

      • 5. Screens

      • 6. Widgets

    12. A Few Screenshots:

    13. Conclusion

    14. References

    Prerequisites

    To follow along with this article and build the example application, you’ll need:

    • Flutter SDK: Ensure you have Flutter installed and configured on your development machine. You can find installation instructions on the official Flutter website.

    • Basic Flutter knowledge: Familiarity with Flutter widgets, state management (even basic setState), and general app development concepts will be helpful.

    • Dart language basics: A good understanding of Dart syntax, classes, and functions is essential.

    • An IDE: Visual Studio Code or Android Studio with the Flutter and Dart plugins installed.

    What We’ll Build

    By the end of this article, we will have built a minimalistic shopping application that demonstrates the core functionalities of go_router. This application will have the following features:

    1. A Product Listing screen displaying a grid of products.

    2. A Product Details screen showing detailed information about a selected product.

    3. A Product Purchase screen that confirms a product purchase.

    4. Navigation between these screens using go_router, including passing data via query and path parameters.

    5. Route Redirection and Exit Guards for enhanced navigation control.

    Installation

    To begin, add go_router to your pubspec.yaml file:

    <span class="hljs-attr">dependencies:</span>
      <span class="hljs-attr">go_router:</span> <span class="hljs-string">^13.0.0</span>
    

    This adds the go_router package as a dependency to your project, allowing you to use its functionalities.

    Import it in your Dart files:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:go_router/go_router.dart'</span>;
    

    This statement makes all the classes and functions provided by the go_router package available for use in your Dart file.

    How to Define Routes

    Create a list of GoRoute objects, each defining a route:

    <span class="hljs-keyword">final</span> routes = [
      GoRoute(
        path: <span class="hljs-string">'/'</span>,
        builder: (context, state) => <span class="hljs-keyword">const</span> HomeScreen(),
      ),
      GoRoute(
        path: <span class="hljs-string">'/products/:id'</span>,
        builder: (context, state) => ProductDetailsScreen(productId: state.params[<span class="hljs-string">'id'</span>]!),
      ),
      <span class="hljs-comment">// ... more routes</span>
    ];
    

    Here’s what’s going on in this code:

    • final routes = [...]: This declares a final list named routes which will hold all our route configurations.

    • GoRoute(...): This is the core class for defining a route. Each GoRoute object represents a distinct path in your application.

    • path: '/': The path property defines the URL path for this route. In this case, / represents the root or home screen of the application.

    • builder: (context, state) => const HomeScreen(): The builder property is a function that returns the widget to be displayed when this route is active. context provides the build context, and state gives access to route-specific information like parameters. Here, it builds a HomeScreen widget.

    • path: '/products/:id': This route defines a dynamic path. The :id part is a path parameter, meaning that whatever value is in that position in the URL will be captured as a parameter.

    • builder: (context, state) => ProductDetailsScreen(productId: state.params['id']!): When this route is activated, it builds a ProductDetailsScreen. state.params['id']! accesses the value of the id path parameter. The ! asserts that id will not be null.

    How to Create the Router

    Instantiate a GoRouter object, passing the routes and integrating it with your app’s MaterialApp:

    MaterialApp.router(
      routeInformationParser: GoRouter.of(context).routeInformationParser,
      routerDelegate: GoRouter(routes: routes),
      <span class="hljs-comment">// ... other MaterialApp properties</span>
    )
    

    Here’s what’s going on in this code:

    • MaterialApp.router(...): This is a special constructor for MaterialApp that integrates with a router delegate, like GoRouter.

    • routerConfig: router, or routeInformationParser: GoRouter.of(context).routeInformationParser, routerDelegate: GoRouter(routes: routes),: These properties are crucial for go_router to manage navigation.

      1. routeInformationParser: Responsible for parsing the route information from the platform (for example, URL in a web browser) into a data structure that the router can understand.

      2. routerDelegate: Responsible for building and managing the navigation stack based on the parsed route information.

    • GoRouter(routes: routes): This creates an instance of GoRouter, passing the list of GoRoute objects we defined earlier.

    How to Navigate Between Screens

    You can navigate programmatically using GoRouter.of(context).go():

    GoRouter.of(context).go(<span class="hljs-string">'/products/123'</span>);
    

    Here’s what this code is doing:

    • GoRouter.of(context): This static method retrieves the nearest GoRouter instance from the widget tree.

    • .go('/products/123'): This method navigates to the specified URL path. This will replace the current route in the navigation stack.

    You can also navigate using named routes like this:

    GoRouter.of(context).goNamed(<span class="hljs-string">'productDetails'</span>, params: {<span class="hljs-string">'id'</span>: <span class="hljs-number">123</span>});
    

    In this code:

    • .goNamed('productDetails', ...): This method navigates to a route identified by its name property (which needs to be defined in the GoRoute configuration).

    • params: {'id': 123}: This map provides values for any path parameters defined in the named route.

    How to Pass Parameters

    In most real-world applications, we don’t just navigate between screens – we also need to pass information. For example:

    • From a product list to a product details page, you’ll want to pass the product’s ID.

    • From a checkout screen, you may need to pass the product description or price.

    With go_router, you can pass parameters in two main ways:

    1. Query Parameters: Added to the URL after a ?. Useful for optional data or filters (for example, /products?id=123).

    2. Path Parameters: Embedded directly in the route path. Best for required values (for example, /products/123).

    Let’s explore both.

    1. Passing Query Parameters

    Query parameters are flexible key-value pairs attached to the URL. These are typically used for non-essential or optional information, such as filters, search queries, or IDs.

    Example: tapping a product card to open its details screen.

    GestureDetector( 
      onTap: () => context.goNamed(
        ProductDetailsScreen.routeName, 
        queryParameters: {<span class="hljs-string">'id'</span>: product.id}, 
      ), 
      child: SingleProduct(product: product), 
    );
    

    What’s happening here?

    • context.goNamed(...): Navigates to a route using its name (defined in your routes config).

    • queryParameters: {'id': product.id}: Appends the product ID to the URL like this:

        /product-details?id=abc123
      

    On the destination screen, you retrieve the parameter like this:

    GoRoute(
      path: ProductDetailsScreen.routeName,
      name: ProductDetailsScreen.routeName,
      builder: (context, state) {
        <span class="hljs-keyword">return</span> ProductDetailsScreen(
          productId: state.uri.queryParameters[<span class="hljs-string">'id'</span>] ?? <span class="hljs-string">""</span>,
        );
      },
    )
    
    • state.uri.queryParameters['id']: Extracts the id value from the URL.

    • ?? "": Provides a default empty string if the parameter is missing.

    Use query parameters when:

    • The parameter is optional.

    • You want to allow multiple parameters without changing the base route.

    • The data doesn’t fundamentally change the structure of the route.

    2. Passing Path Parameters

    Path parameters are part of the route itself and are usually required. Without them, the route doesn’t make sense.

    Example: a purchase flow where the product description is required.

    Navigate to the route:

    context.goNamed(
      <span class="hljs-string">'pay-now'</span>,
      pathParameters: {
        <span class="hljs-string">'description'</span>: product.description,
      },
    );
    

    Define the route:

    GoRoute(
      path: <span class="hljs-string">'product-purchase/:description'</span>,
      name: ProductPurchaseScreen.routeName,
      builder: (context, state) {
        <span class="hljs-keyword">return</span> ProductPurchaseScreen(
          description: state.pathParameters[<span class="hljs-string">'description'</span>]!,
        );
      },
    )
    

    What’s happening here?

    • path: 'product-purchase/:description': The :description part defines a dynamic segment.

    • pathParameters: {'description': product.description}: Replaces :description with the actual value. The URL will look like:

        /product-purchase/AwesomeProduct
      
    • state.pathParameters['description']!: Retrieves the parameter inside the screen.

    Use path parameters when:

    • The value is required (for example, ID, username, slug).

    • The route should not exist without it.

    Sub-routes and ShellRoute

    As your app grows, you’ll need to organize routes in a hierarchy or keep persistent UI elements like a bottom navigation bar. go_router makes this possible with Sub-routes and ShellRoute.

    1. Sub-routes

    Sub-routes allow you to nest routes under a parent. This keeps related routes grouped together.

    Example: Profile and its settings page.

    GoRoute(
      path: <span class="hljs-string">'/profile'</span>,
      builder: (context, state) => ProfileScreen(),
      routes: [
        GoRoute(
          path: <span class="hljs-string">'settings'</span>,
          builder: (context, state) => SettingsScreen(),
        ),
      ],
    ),
    
    • /profile: Opens ProfileScreen.

    • /profile/settings: Opens SettingsScreen.

    Use sub-routes to keep related screens organized under one parent route.

    2. ShellRoute

    ShellRoute is used when you need a persistent UI wrapper (like a BottomNavigationBar or Drawer) that stays visible while switching between child routes.

    Example: A bottom navigation layout.

    ShellRoute(
      builder: (context, state, child) {
        <span class="hljs-keyword">return</span> MainScaffold(child: child); <span class="hljs-comment">// contains BottomNavigationBar</span>
      },
      routes: [
        GoRoute(
          path: <span class="hljs-string">'/home'</span>,
          builder: (context, state) => HomeScreen(),
        ),
        GoRoute(
          path: <span class="hljs-string">'/profile'</span>,
          builder: (context, state) => ProfileScreen(),
        ),
      ],
    ),
    

    ShellRoute: Wraps a persistent widget (MainScaffold). child: Dynamically changes depending on which route is active.

    Use ShellRoute when:

    • You need tabs or bottom navigation.

    • You want a layout to remain while only the inner content changes.

    Redirection and Guards

    In many apps, navigation isn’t just about moving between pages. It’s also about controlling who can access what and when. For example:

    • Redirecting a logged-out user to the login screen.

    • Preventing non-admins from entering admin routes.

    go_router provides two main tools here: redirects and guards.

    1. Redirection

    A redirect automatically reroutes users if a condition is not met.

    Example: redirecting old URLs or enforcing login.

    GoRoute(
      path: <span class="hljs-string">'/old-path'</span>,
      redirect: (state) => <span class="hljs-string">'/new-path'</span>,
    ),
    
    GoRoute(
      path: <span class="hljs-string">'/dashboard'</span>,
      builder: (context, state) => DashboardScreen(),
      redirect: (context, state) {
        <span class="hljs-keyword">final</span> isLoggedIn = AuthService.isLoggedIn();
        <span class="hljs-keyword">return</span> isLoggedIn ? <span class="hljs-keyword">null</span> : <span class="hljs-string">'/login'</span>;
      },
    ),
    
    • /old-path: Always redirects to /new-path.

    • /dashboard: Redirects to /login if the user is not logged in.

    2. Guards

    Guards are like “checks” placed on routes. They decide if a user can access a route or not.

    Example: restricting access to admins only.

    GoRoute(
      path: <span class="hljs-string">'/admin'</span>,
      builder: (context, state) => AdminScreen(),
      redirect: (context, state) {
        <span class="hljs-keyword">final</span> isAdmin = AuthService.isAdmin();
        <span class="hljs-keyword">return</span> isAdmin ? <span class="hljs-keyword">null</span> : <span class="hljs-string">'/not-authorized'</span>;
      },
    ),
    

    If isAdmin is true, the user can enter /admin. Otherwise, they’re redirected to /not-authorized.

    Use redirects and guards for:

    • Authentication flows (login/logout).

    • Role-based access (admin vs user).

    • Handling deprecated or changed routes.

    How to Set Up a Real Flutter Project Using Go Router

    Before diving into GoRouter, let’s start by setting up a new Flutter project and organizing the codebase. The project structure includes the following folders and files:

    go_router_project/
    |-- lib/
    |   |-- main.dart
    |   |-- models/
    |   |   |-- product.dart
    |   |-- controller/
    |   |   |-- product_controller.dart
    |   |-- config/
    |   |   |-- route_config.dart
    |   |-- screens/
    |   |   |-- product_details_screen.dart
    |   |   |-- product_list_screen.dart
    |   |   |-- product_purchase_screen.dart
    |   |-- widgets/
    |       |-- bottom_container.dart
    |       |-- color_container.dart
    |       |-- ratings.dart
    |       |-- search_section.dart
    |       |-- show_modal.dart
    |       |-- single_product.dart
    |-- pubspec.yaml
    

    Now, open the pubspec.yaml file and add the following dependency:

    <span class="hljs-attr">dependencies:</span>
      <span class="hljs-attr">go_router:</span> <span class="hljs-string">^13.0.0</span>
    

    Save the file and run flutter pub get in the terminal to fetch the dependency.

    We’ll be creating a minimalistic shopping app with just three pages.

    Project Structure

    1. main.dart:

    Replace the code in lib/main.dart with this:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/services.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'go_router/config/route_config.dart'</span>;
    
    <span class="hljs-keyword">void</span> main() {
      runApp(<span class="hljs-keyword">const</span> MyApp());
    }
    
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
      <span class="hljs-keyword">const</span> MyApp({<span class="hljs-keyword">super</span>.key});
    
      <span class="hljs-meta">@override</span>
      Widget build(BuildContext context) {
        SystemChrome.setSystemUIOverlayStyle(
          <span class="hljs-keyword">const</span> SystemUiOverlayStyle(
            statusBarColor: Colors.transparent,
            statusBarIconBrightness: Brightness.dark,
          ),
        );
    
        <span class="hljs-keyword">return</span> MaterialApp.router(
          title: <span class="hljs-string">'Flutter GoRouter'</span>,
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.brown),
            useMaterial3: <span class="hljs-keyword">true</span>,
          ),
          routerConfig: router, <span class="hljs-comment">// go router</span>
        );
      }
    }
    

    Here’s what’s going on in this code:

    • main(): The entry point of the Flutter application. It runs the MyApp widget.

    • SystemChrome.setSystemUIOverlayStyle(...): This configures the system UI overlay, specifically setting the status bar to be transparent and its icons to be dark.

    • MaterialApp.router(...): This is the root widget of our application, configured with go_router.

    • title: 'Flutter GoRouter': Sets the title of the application.

    • theme: ThemeData(...): Defines the visual theme for the application, using a brown seed color and Material 3 design.

    • routerConfig: router: This is where go_router is integrated. router is the GoRouter instance defined in route_config.dart.

    2. Model

    The model folder is where we define our data structures. A model is simply a Dart class that represents the shape of the data you’ll be working with in your app.

    For example, in this project, Product is the model. It holds details such as id, name, imageUrl, description, price, and so on. Models don’t handle logic or UI, they’re just blueprints for data.

    Think of models as the foundation. Whenever your app fetches, stores, or manipulates product information, it uses this Product model for consistency. We are going to create a model called product.dart

    product.dart:

    Add this code to lib/models/product.dart:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/foundation.dart'</span>;
    
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Product</span> </span>{
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> id;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> name;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> imageUrl;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> description;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">double</span> price;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">double</span> previousPrice;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> colors;
    
      Product({
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.id,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.name,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.imageUrl,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.description,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.previousPrice,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.price,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.colors,
      });
    
      <span class="hljs-keyword">factory</span> Product.initial() => Product(
            id: <span class="hljs-string">''</span>,
            name: <span class="hljs-string">''</span>,
            imageUrl: <span class="hljs-string">''</span>,
            description: <span class="hljs-string">''</span>,
            previousPrice: <span class="hljs-number">0.0</span>,
            price: <span class="hljs-number">0.0</span>,
            colors: <span class="hljs-string">''</span>,
          );
    }
    
    • Product class: This class defines the structure for a product, with properties like id, name, imageUrl, description, price, previousPrice, and colors.

    • Product.initial(): A factory constructor to create an empty Product object, useful for initialization.

    3. Controller

    The controllers folder contains classes that manage business logic, how data flows in and out of your app. Controllers sit between your views (UI) and your models (data).

    In this example, the ProductController is a simple in-memory data provider. It:

    • Stores a list of Product objects.

    • Exposes a findById() method so we can look up a product quickly.

    • Provides access to the product list via the products getter.

    In larger apps, controllers often fetch data from APIs, handle caching, or manage app state. Here, it’s kept simple for learning purposes. We are going to create a product controller.

    product_controller.dart:

    Add this code to lib/controllers/product_controller.dart:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'../models/product.dart'</span>;
    
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductController</span> </span>{
      Product findById(<span class="hljs-built_in">String?</span> id) {
        <span class="hljs-keyword">return</span> _products.firstWhere((product) => product.id == id);
      }
    
      <span class="hljs-built_in">List</span><Product> <span class="hljs-keyword">get</span> products => _products;
    
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span><Product> _products = [
        Product(
          id: <span class="hljs-string">'p7'</span>,
          name: <span class="hljs-string">'Leather BackPack'</span>,
          imageUrl:
              <span class="hljs-string">'https://images.unsplash.com/photo-1571689936114-b16146c9570a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NzR8fHByb2R1Y3R8ZW58MHx8MHx8&auto=format&fit=crop&w=800&q=60'</span>,
          description:
              <span class="hljs-string">'The stronger the better it is to load it with all that the eyes sees useful and needful too. BackPack is a all-fit leather strong bag for carrying anything the hands can store and it's literally worth any penny'</span>,
          price: <span class="hljs-number">30.9</span>,
          previousPrice: <span class="hljs-number">40.9</span>,
          colors: <span class="hljs-string">'red,grey,black,indigo,purple'</span>,
        ),
        Product(
          id: <span class="hljs-string">'p1'</span>,
          name: <span class="hljs-string">'Smart Watch'</span>,
          imageUrl:
              <span class="hljs-string">'https://images.unsplash.com/photo-1523275335684-37898b6baf30?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8cHJvZHVjdHxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=800&q=60'</span>,
          description: <span class="hljs-string">'A white smart watch with good features and more'</span>,
          price: <span class="hljs-number">29.99</span>,
          previousPrice: <span class="hljs-number">39.99</span>,
          colors: <span class="hljs-string">'red,grey,black,indigo,purple'</span>,
        ),
        Product(
          id: <span class="hljs-string">'p16'</span>,
          name: <span class="hljs-string">'PowerBook'</span>,
          imageUrl:
              <span class="hljs-string">'https://get.pxhere.com/photo/laptop-computer-macbook-mac-screen-water-board-keyboard-technology-air-mouse-photo-airport-aircraft-tablet-aviation-office-black-monitor-keys-graphic-hardware-image-pc-exhibition-multimedia-calculator-vector-water-cooling-floppy-disk-phased-out-desktop-computer-netbook-personal-computer-computer-monitor-electronic-device-computer-hardware-display-device-448748.jpg'</span>,
          description:
              <span class="hljs-string">'Awesome hardware, crappy keyboard and a hefty price. Buy now before a  one is released!'</span>,
          price: <span class="hljs-number">2299.99</span>,
          previousPrice: <span class="hljs-number">3299.99</span>,
          colors: <span class="hljs-string">'red,grey,black,indigo,purple'</span>,
        ),
        Product(
          id: <span class="hljs-string">'p2'</span>,
          name: <span class="hljs-string">'Red Sneakers'</span>,
          imageUrl:
              <span class="hljs-string">'https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTB8fHByb2R1Y3R8ZW58MHx8MHx8&auto=format&fit=crop&w=800&q=60'</span>,
          description:
              <span class="hljs-string">'Perfect for your joggers and black T-shirts and more. The sneakers comes in different sizes and colors. You never know when that T-shirt needs some styles with the soft layers of a sneakers'</span>,
          price: <span class="hljs-number">199.99</span>,
          previousPrice: <span class="hljs-number">299.99</span>,
          colors: <span class="hljs-string">'yellow,grey,black,red,teal'</span>,
        ),
        Product(
          id: <span class="hljs-string">'p3'</span>,
          name: <span class="hljs-string">'Nikon Camera'</span>,
          imageUrl:
              <span class="hljs-string">'https://images.unsplash.com/photo-1564466809058-bf4114d55352?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MjB8fHByb2R1Y3R8ZW58MHx8MHx8&auto=format&fit=crop&w=800&q=60'</span>,
          description:
              <span class="hljs-string">'You can only see clearer with your eyes but a camera saves the memory in it's eyes'</span>,
          price: <span class="hljs-number">89.9</span>,
          previousPrice: <span class="hljs-number">109.9</span>,
          colors: <span class="hljs-string">'red,grey,black,indigo,purple'</span>,
        ),
        Product(
          id: <span class="hljs-string">'p4'</span>,
          name: <span class="hljs-string">'HeadSets'</span>,
          imageUrl:
              <span class="hljs-string">'https://images.unsplash.com/photo-1583394838336-acd977736f90?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MjJ8fHByb2R1Y3R8ZW58MHx8MHx8&auto=format&fit=crop&w=800&q=60'</span>,
          description:
              <span class="hljs-string">'The louder the sound, the better it feels inside with the body'</span>,
          price: <span class="hljs-number">120.1</span>,
          previousPrice: <span class="hljs-number">150.1</span>,
          colors: <span class="hljs-string">'red,grey,black,indigo,purple'</span>,
        ),
        Product(
          id: <span class="hljs-string">'p5'</span>,
          name: <span class="hljs-string">'Amazon SoundBox'</span>,
          imageUrl:
              <span class="hljs-string">'https://images.unsplash.com/photo-1543512214-318c7553f230?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MjR8fHByb2R1Y3R8ZW58MHx8MHx8&auto=format&fit=crop&w=800&q=60'</span>,
          description:
              <span class="hljs-string">'Automated soundbox with voice recognition and more. What could be more better'</span>,
          price: <span class="hljs-number">78.19</span>,
          previousPrice: <span class="hljs-number">88.19</span>,
          colors: <span class="hljs-string">'red,grey,black,indigo,purple'</span>,
        ),
        Product(
          id: <span class="hljs-string">'p6'</span>,
          name: <span class="hljs-string">'Xbox 360 GamePads'</span>,
          imageUrl:
              <span class="hljs-string">'https://images.unsplash.com/photo-1600080972464-8e5f35f63d08?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mzd8fHByb2R1Y3R8ZW58MHx8MHx8&auto=format&fit=crop&w=800&q=60'</span>,
          description:
              <span class="hljs-string">'You never know when it is time to touch it better except the pads with xbox is there to assist'</span>,
          price: <span class="hljs-number">98.99</span>,
          previousPrice: <span class="hljs-number">108.99</span>,
          colors: <span class="hljs-string">'red,grey,black,indigo,purple'</span>,
        ),
      ];
    }
    
    • ProductController class: This class acts as a simple data source for our products.

    • findById(String? id): A method to find a product by its ID from the _products list.

    • products getter: Provides access to the list of _products. iv. _products: A private list of Product objects, pre-populated with sample product data.

    4. Config

    The config folder stores all the configuration and setup files for your app. In this project, it’s where we keep route_config.dart, which contains all the go_router setup and route definitions.

    This is important because:

    • Routes can get complex as your app grows.

    • Having all navigation setup in one place keeps things clean and manageable.

    • Config files are also a great place to put things like app-wide constants, environment settings, or themes.

    Think of config as the central wiring of your app. It’s not about data or logic, but about how the app is structured and tied together. We are going to create a route config here.

    route_config.dart:

    Add this code to lib/config/route_config.dart:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:get_it_auto_router_go_router/go_router/controllers/product_controller.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:go_router/go_router.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../models/product.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../screens/product_details_screen.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../screens/product_list_screen.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../screens/product_purchase_screen.dart'</span>;
    
    <span class="hljs-comment">/// <span class="markdown">The route configuration.</span></span>
    <span class="hljs-keyword">final</span> GoRouter router = GoRouter(
      routes: <RouteBase>[
        GoRoute(
          path: <span class="hljs-string">'/'</span>,
          builder: (BuildContext context, GoRouterState state) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> ProductListScreen();
          },
          routes: <RouteBase>[
            GoRoute(
              path: ProductDetailsScreen.routeName,
              name: ProductDetailsScreen.routeName,
              builder: (BuildContext context, GoRouterState state) {
                <span class="hljs-keyword">return</span> ProductDetailsScreen(
                  productId: state.uri.queryParameters[<span class="hljs-string">'id'</span>] ?? <span class="hljs-string">""</span>,
                );
              },
              routes: <RouteBase>[
                GoRoute(
                  path: <span class="hljs-string">'product-purchase/:description'</span>,
                  name: ProductPurchaseScreen.routeName,
                  builder: (BuildContext context, GoRouterState state) {
                    <span class="hljs-keyword">return</span> ProductPurchaseScreen(
                      productImage: state.uri.queryParameters[<span class="hljs-string">'img'</span>]!,
                      productPrice: state.uri.queryParameters[<span class="hljs-string">'price'</span>]!,
                      productName: state.uri.queryParameters[<span class="hljs-string">'name'</span>]!,
                      description: state.pathParameters[<span class="hljs-string">'description'</span>]!,
                    );
                  },
                  onExit: (BuildContext context) <span class="hljs-keyword">async</span> {
                    <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool?</span> confirmed = <span class="hljs-keyword">await</span> showDialog<<span class="hljs-built_in">bool</span>>(
                      context: context,
                      builder: (_) {
                        <span class="hljs-keyword">return</span> AlertDialog(
                          content: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Are you sure to leave this page?'</span>),
                          actions: <Widget>[
                            TextButton(
                              onPressed: () => Navigator.of(context).pop(<span class="hljs-keyword">false</span>),
                              child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Cancel'</span>),
                            ),
                            TextButton(
                              onPressed: () => Navigator.of(context).pop(<span class="hljs-keyword">true</span>),
                              child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Confirm'</span>),
                            ),
                          ],
                        );
                      },
                    );
                    <span class="hljs-keyword">return</span> confirmed ?? <span class="hljs-keyword">false</span>;
                  },
                )
              ],
            )
          ],
        ),
      ],
    );
    

    Here’s what’s happening in this code:

    • final GoRouter router = GoRouter(...): This is the main GoRouter instance for our application.

    • routes: <RouteBase>[...]: Defines the top-level routes.

    • GoRoute(path: '/', builder: ...) : The root route, leading to ProductListScreen.

    • Nested GoRoute for ProductDetailsScreen: This route is a child of the root.

    • path: ProductDetailsScreen.routeName: Uses a static constant for the path.

    • name: ProductDetailsScreen.routeName: Assigns a name to the route for easier navigation.

    • builder: Builds the ProductDetailsScreen, extracting productId from query parameters.

    • v. Nested GoRoute for ProductPurchaseScreen: This route is a child of ProductDetailsScreen.

    • path: 'product-purchase/:description': Defines a path with a description path parameter.

    • name: ProductPurchaseScreen.routeName: Assigns a name for navigation.

    • builder: Builds the ProductPurchaseScreen, extracting productImage, productPrice, productName from query parameters and description from path parameters.

    • onExit: (BuildContext context) async { ... }: This is an onExit guard that triggers a confirmation dialog when the user tries to leave the ProductPurchaseScreen. If the user cancels, navigation is prevented.

    5. Screens

    Your screens are the UI pages the user interacts with. Each screen has a different role in the shopping flow:

    ProductListScreen:

    This is the entry screen of your app that shows all available products in a grid format.

    • Acts as a catalog/browse page.

    • Uses the ProductController to fetch product data.

    • Includes a search bar (SearchSection) for filtering products.

    • Navigates to the ProductDetailsScreen when a product is tapped.

    ProductDetailsScreen:

    This screen shows the full details of a selected product.

    • Displays product image, name, price, available colors, and description.

    • Allows the user to view a larger image modal by tapping the image.

    • Provides a “Buy Now” button at the bottom (via bottomContainer).

    • Uses path and query parameters in navigation to pass product data to the next screen.

    ProductPurchaseScreen:

    This is the final confirmation screen before purchasing.

    • Shows the selected product image, name, price, and description.

    • Confirms the user’s intent to purchase with a FloatingActionButton (currently just an icon).

    • Completes the navigation flow: List > Details > Purchase.

    Alright, now let’s go through them one by one:

    product_list_screen.dart:

    Add this code to lib/screens/product_list_screen.dart:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:get_it_auto_router_go_router/go_router/controllers/product_controller.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:get_it_auto_router_go_router/go_router/screens/product_details_screen.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:get_it_auto_router_go_router/go_router/widgets/search_section.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:get_it_auto_router_go_router/go_router/widgets/single_product.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:go_router/go_router.dart'</span>;
    
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../models/product.dart'</span>;
    
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductListScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
      <span class="hljs-keyword">const</span> ProductListScreen({<span class="hljs-keyword">super</span>.key});
    
    
      <span class="hljs-meta">@override</span>
      Widget build(BuildContext context) {
        ProductController productController = ProductController();
        TextEditingController searchController = TextEditingController();
    
        <span class="hljs-keyword">return</span> Scaffold(
          appBar: AppBar(
            title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Products'</span>),
            elevation: <span class="hljs-number">0</span>,
          ),
          body: Padding(
            padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
            child: Column(
              children: [
                SearchSection(
                  searchController: searchController,
                ),
                <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
                Expanded(
                  child: GridView.builder(
                    itemCount: productController.products.length,
                    gridDelegate: <span class="hljs-keyword">const</span> SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: <span class="hljs-number">2</span>,
                      mainAxisSpacing: <span class="hljs-number">10</span>,
                      crossAxisSpacing: <span class="hljs-number">10</span>,
                    ),
                    itemBuilder: (context, index) {
                      Product product = productController.products[index];
    
                      <span class="hljs-keyword">return</span> GestureDetector(
                        onTap: () => context.goNamed(
                          ProductDetailsScreen.routeName,
                          queryParameters: {<span class="hljs-string">'id'</span>: product.id},
                        ),
                        child: SingleProduct(product: product),
                      );
                    },
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    Here’s what’s going on:

    • ProductListScreen: A StatelessWidget that displays a list of products.

    • ProductController productController = ProductController(): Creates an instance of the ProductController to access product data.

    • AppBar: Displays the title “Products”.

    • SearchSection: A custom widget for a search bar.

    • Expanded with GridView.builder: Displays the products in a scrollable grid.

    • GestureDetector onTap: When a product is tapped, it navigates to the ProductDetailsScreen using context.goNamed, passing the product’s id as a query parameter.

    • SingleProduct: A custom widget to display individual product information.

    product_details_screen.dart:

    Add this code to lib/screens/product_details_screen.dart:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/services.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:go_router/go_router.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../controllers/product_controller.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../models/product.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../widgets/bottom_container.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../widgets/color_container.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../widgets/ratings.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../widgets/show_modal.dart'</span>;
    
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductDetailsScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
      <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> routeName = <span class="hljs-string">'product-details'</span>;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> productId;
    
      <span class="hljs-keyword">const</span> ProductDetailsScreen({
        <span class="hljs-keyword">super</span>.key,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.productId,
      });
    
      <span class="hljs-meta">@override</span>
      Widget build(BuildContext context) {
        <span class="hljs-keyword">late</span> Color colored;
    
        <span class="hljs-comment">// get color</span>
        Color getColor(<span class="hljs-built_in">String</span> color) {
          <span class="hljs-keyword">switch</span> (color) {
            <span class="hljs-keyword">case</span> <span class="hljs-string">'red'</span>:
              colored = Colors.red;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'purple'</span>:
              colored = Colors.purple;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'grey'</span>:
              colored = Colors.grey;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'black'</span>:
              colored = Colors.black;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'orange'</span>:
              colored = Colors.orange;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'indigo'</span>:
              colored = Colors.indigo;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'yellow'</span>:
              colored = Colors.yellow;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'blue'</span>:
              colored = Colors.blue;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'brown'</span>:
              colored = Colors.brown;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">'teal'</span>:
              colored = Colors.teal;
              <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">default</span>:
          }
    
          <span class="hljs-keyword">return</span> colored;
        }
    
        ProductController productController = ProductController();
        Product product = productController.findById(productId);
    
        <span class="hljs-built_in">List</span><<span class="hljs-built_in">String</span>> availableColors = product.colors.split(<span class="hljs-string">','</span>);
    
        <span class="hljs-comment">// pay now</span>
        <span class="hljs-keyword">void</span> payNow() {
          context.goNamed(
            <span class="hljs-string">'pay-now'</span>,
            pathParameters: <<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>>{
              <span class="hljs-string">'description'</span>: product.description,
            },
            queryParameters: <<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>>{
              <span class="hljs-string">'img'</span>: product.imageUrl.toString(),
              <span class="hljs-string">'price'</span>: product.price.toString(),
              <span class="hljs-string">'name'</span>: product.name.toString(),
            },
          );
        }
    
        <span class="hljs-keyword">return</span> Scaffold(
          extendBodyBehindAppBar: <span class="hljs-keyword">true</span>,
          appBar: AppBar(
            automaticallyImplyLeading: <span class="hljs-keyword">false</span>,
            backgroundColor: Colors.transparent,
            elevation: <span class="hljs-number">0</span>,
            systemOverlayStyle: <span class="hljs-keyword">const</span> SystemUiOverlayStyle(
              statusBarColor: Colors.transparent,
            ),
            leading: IconButton(
              icon: <span class="hljs-keyword">const</span> Icon(Icons.arrow_back),
              color: Colors.black,
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ),
          body: Column(
            children: [
              Expanded(
                flex: <span class="hljs-number">2</span>,
                child: GestureDetector(
                  onTap: () => showImageModal(context, product),
                  child: ClipRRect(
                    borderRadius: <span class="hljs-keyword">const</span> BorderRadius.vertical(
                      top: Radius.zero,
                      bottom: Radius.circular(<span class="hljs-number">50</span>),
                    ),
                    child: Hero(
                      tag: product.id,
                      child: Image.network(
                        product.imageUrl,
                        fit: BoxFit.cover,
                        width: <span class="hljs-built_in">double</span>.infinity,
                      ),
                    ),
                  ),
                ),
              ),
              Expanded(
                flex: <span class="hljs-number">3</span>,
                child: Padding(
                  padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">15.0</span>),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: [
                      Text(
                        product.name,
                        style: <span class="hljs-keyword">const</span> TextStyle(
                          fontSize: <span class="hljs-number">30</span>,
                        ),
                      ),
                      <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">5</span>),
                      ratings(),
                      <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">5</span>),
                      Row(
                        children: [
                          Text(
                            <span class="hljs-string">'$<span class="hljs-subst">${product.price.toString()}</span>'</span>,
                            style: <span class="hljs-keyword">const</span> TextStyle(
                              fontSize: <span class="hljs-number">20</span>,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          <span class="hljs-keyword">const</span> SizedBox(width: <span class="hljs-number">3</span>),
                          Text(
                            <span class="hljs-string">'$<span class="hljs-subst">${product.previousPrice.toString()}</span>'</span>,
                            style: <span class="hljs-keyword">const</span> TextStyle(
                              fontSize: <span class="hljs-number">15</span>,
                              color: Colors.grey,
                              decoration: TextDecoration.lineThrough,
                            ),
                          ),
                        ],
                      ),
                      <span class="hljs-keyword">const</span> Column(
                        crossAxisAlignment: CrossAxisAlignment.end,
                        children: [
                          Text(
                            <span class="hljs-string">'Available in stocks'</span>,
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          Text(
                            <span class="hljs-string">'Out of stocks'</span>,
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              color: Colors.deepOrange,
                              decoration: TextDecoration.lineThrough,
                            ),
                          ),
                        ],
                      ),
                      <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
                      <span class="hljs-keyword">const</span> Text(
                        <span class="hljs-string">'Colors Available'</span>,
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: <span class="hljs-number">15</span>,
                        ),
                      ),
                      <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> color <span class="hljs-keyword">in</span> availableColors)
                            buildContainer(
                              color,
                              getColor,
                            )
                        ],
                      ),
                      <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">15</span>),
                      <span class="hljs-keyword">const</span> Text(
                        <span class="hljs-string">'About'</span>,
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: <span class="hljs-number">15</span>,
                        ),
                      ),
                      <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
                      Text(
                        product.description,
                        textAlign: TextAlign.justify,
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
          bottomSheet: bottomContainer(product, payNow),
        );
      }
    }
    

    That’s a lot – here’s what this code is doing:

    • ProductDetailsScreen: A StatelessWidget that displays the details of a single product.

    • static const routeName = 'product-details': Defines a static constant for the route name, ensuring consistency.

    • productId: This is a required parameter for the screen, passed during navigation.

    • getColor(String color): A helper function to convert color names (strings) into Color objects.

    • ProductController productController = ProductController(): Accesses product data.

    • Product product = productController.findById(productId): Retrieves the specific product based on the productId received.

    • payNow(): A function that navigates to the ProductPurchaseScreen using context.goNamed, passing product details as both path and query parameters.

    • AppBar: Displays a back arrow to navigate back.

    • Expanded for product image: Displays the product image with a Hero animation for smooth transitions.

    • GestureDetector allows tapping the image to show a modal.

    • Expanded for product details: Displays product name, ratings, prices, availability, available colors, and description.

    • bottomSheet: bottomContainer(product, payNow): Attaches a custom bottomContainer widget to the Scaffold, which includes the “Buy Now” button.

    product_purchase_screen.dart:

    Add this code to lib/screens/product_purchase_screen.dart:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductPurchaseScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
      <span class="hljs-keyword">const</span> ProductPurchaseScreen({
        <span class="hljs-keyword">super</span>.key,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.productImage,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.productName,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.productPrice,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.description,
      });
    
      <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> routeName = <span class="hljs-string">'pay-now'</span>;
    
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> productName;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> productPrice;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> productImage;
      <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> description;
    
      <span class="hljs-meta">@override</span>
      Widget build(BuildContext context) {
        <span class="hljs-keyword">return</span> Scaffold(
          floatingActionButton: <span class="hljs-keyword">const</span> FloatingActionButton(
            onPressed: <span class="hljs-keyword">null</span>,
            child: Icon(
              Icons.check_circle,
            ),
          ),
          appBar: AppBar(
            title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Purchase Item'</span>),
          ),
          body: SingleChildScrollView(
            child: Center(
              child: Padding(
                padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
                child: Column(
                  children: <Widget>[
                    ClipRRect(
                      borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
                      child: Image.network(productImage),
                    ),
                    <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: <Widget>[
                        Text(
                          productName,
                          style: <span class="hljs-keyword">const</span> TextStyle(
                            fontWeight: FontWeight.w800,
                            fontSize: <span class="hljs-number">18</span>,
                          ),
                        ),
                        Text(
                          <span class="hljs-string">'$<span class="hljs-subst">$productPrice</span>'</span>,
                          style: <span class="hljs-keyword">const</span> TextStyle(
                            fontWeight: FontWeight.w800,
                            fontSize: <span class="hljs-number">16</span>,
                            color: Colors.grey,
                          ),
                        )
                      ],
                    ),
                    <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
                    Text(
                      description,
                      style: <span class="hljs-keyword">const</span> TextStyle(
                        fontSize: <span class="hljs-number">16</span>,
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        );
      }
    }
    

    Here’s what’s going on:

    • ProductPurchaseScreen: A StatelessWidget that confirms the product purchase.

    • static const routeName = 'pay-now': Defines the route name.

    • productImage, productName, productPrice, description: These are required parameters received from the previous screen.

    • FloatingActionButton: Displays a checkmark icon, though onPressed is currently null.

    • AppBar: Displays the title “Purchase Item”.

    • SingleChildScrollView: Makes the content scrollable.

    • Image.network(productImage): Displays the product image received.

    • Row for product name and price: Shows the product’s name and its price.

    • Text(description): Displays the product’s description.

    6. Widgets

    Widgets are the reusable building blocks of your UI. Instead of duplicating UI code in multiple screens, you break them into widgets.

    bottom_container.dart:

    Add this code to lib/widgets/bottom_container.dart:

    <span class="hljs-comment">// bottom container</span>
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../models/product.dart'</span>;
    
    Container bottomContainer(Product productDetails,<span class="hljs-built_in">Function</span> payNow) {
      <span class="hljs-keyword">return</span> Container(
        color: Colors.white,
        child: Padding(
          padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(
            horizontal: <span class="hljs-number">18.0</span>,
            vertical: <span class="hljs-number">10</span>,
          ),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  <span class="hljs-keyword">const</span> Text(
                    <span class="hljs-string">'Price'</span>,
                    style: TextStyle(
                      color: Colors.grey,
                      fontWeight: FontWeight.w500,
                      fontSize: <span class="hljs-number">14</span>,
                    ),
                  ),
                  <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">5</span>),
                  Text(
                    <span class="hljs-string">'$<span class="hljs-subst">${productDetails.price}</span>'</span>,
                    style: <span class="hljs-keyword">const</span> TextStyle(
                      color: Colors.brown,
                      fontWeight: FontWeight.w700,
                      fontSize: <span class="hljs-number">25</span>,
                    ),
                  )
                ],
              ),
              Wrap(
                crossAxisAlignment: WrapCrossAlignment.center,
                children: [
                  Container(
                    height: <span class="hljs-number">50</span>,
                    width: <span class="hljs-number">80</span>,
                    decoration: BoxDecoration(
                      color: Colors.brown.withOpacity(<span class="hljs-number">0.3</span>),
                      borderRadius: <span class="hljs-keyword">const</span> BorderRadius.only(
                        bottomLeft: Radius.circular(<span class="hljs-number">5</span>),
                        topLeft: Radius.circular(<span class="hljs-number">5</span>),
                      ),
                    ),
                    child: <span class="hljs-keyword">const</span> Center(
                      child: Wrap(
                        crossAxisAlignment: WrapCrossAlignment.center,
                        children: [
                          Icon(
                            Icons.shopping_cart_checkout,
                            color: Colors.white,
                          ),
                          SizedBox(width: <span class="hljs-number">15</span>),
                          Text(
                            <span class="hljs-string">'1'</span>,
                            style: TextStyle(
                              color: Colors.white,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                  GestureDetector(
                    onTap: () => payNow(),
                    child: Container(
                      height: <span class="hljs-number">50</span>,
                      width: <span class="hljs-number">120</span>,
                      decoration: <span class="hljs-keyword">const</span> BoxDecoration(
                        color: Colors.brown,
                        borderRadius: BorderRadius.only(
                          bottomRight: Radius.circular(<span class="hljs-number">5</span>),
                          topRight: Radius.circular(<span class="hljs-number">5</span>),
                        ),
                      ),
                      child: <span class="hljs-keyword">const</span> Center(
                        child: Text(
                          <span class="hljs-string">'Buy Now'</span>,
                          style: TextStyle(
                            color: Colors.white,
                            fontWeight: FontWeight.w700,
                          ),
                        ),
                      ),
                    ),
                  )
                ],
              )
            ],
          ),
        ),
      );
    }
    

    In this code:

    • bottomContainer: A function that returns a Container widget for the bottom sheet. It displays the product price and a “Buy Now” button.

    • GestureDetector onTap: The “Buy Now” button triggers the payNow function passed as an argument.

    ratings.dart:

    Add this code to lib/widgets/ratings.dart:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    
    Widget ratings() => <span class="hljs-keyword">const</span> Row(
          children: [
            Icon(Icons.star, color: Colors.deepOrange, size: <span class="hljs-number">15</span>),
            Icon(Icons.star, color: Colors.deepOrange, size: <span class="hljs-number">15</span>),
            Icon(Icons.star, color: Colors.deepOrange, size: <span class="hljs-number">15</span>),
            Icon(Icons.star, color: Colors.deepOrange, size: <span class="hljs-number">15</span>),
            Icon(Icons.star, color: Colors.deepOrange, size: <span class="hljs-number">15</span>),
            SizedBox(width: <span class="hljs-number">20</span>),
            Text(<span class="hljs-string">'(3400 Reviews)'</span>)
          ],
        );
    
    • ratings(): A simple widget that displays a row of five orange stars and a review count.

    color_container.dart:

    Add this code to lib/widgets/color_container.dart:

    <span class="hljs-comment">// build container for color</span>
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/cupertino.dart'</span>;
    
    Widget buildContainer(<span class="hljs-built_in">String</span> color,<span class="hljs-built_in">Function</span> getColor) {
      <span class="hljs-keyword">return</span> Container(
        height: <span class="hljs-number">5</span>,
        width: <span class="hljs-number">40</span>,
        decoration: BoxDecoration(
          color: getColor(color),
          borderRadius: BorderRadius.circular(<span class="hljs-number">20</span>),
        ),
      );
    }
    

    Here’s what’s going on:

    • buildContainer: A function that creates a small, rounded Container to represent an available product color. It takes the color name as a string and a getColor function to convert it to a Color object.

    search_section.dart:

    Add this code to lib/widgets/search_section.dart:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/cupertino.dart'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SearchSection</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
      <span class="hljs-keyword">const</span> SearchSection({
        <span class="hljs-keyword">super</span>.key,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.searchController,
      });
    
      <span class="hljs-keyword">final</span> TextEditingController searchController;
    
      <span class="hljs-meta">@override</span>
      Widget build(BuildContext context) {
        <span class="hljs-keyword">return</span> TextField(
          controller: searchController,
          decoration: InputDecoration(
            prefixIcon: <span class="hljs-keyword">const</span> Icon(
              CupertinoIcons.search,
              color: Colors.black,
            ),
            hintText: <span class="hljs-string">'Enter search keyword'</span>,
            label: <span class="hljs-keyword">const</span> Text(
              <span class="hljs-string">'Search Here'</span>,
            ),
            fillColor: Colors.grey.withOpacity(<span class="hljs-number">0.1</span>),
            enabledBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
            ),
            focusedBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
            ),
          ),
        );
      }
    }
    

    In this code:

    • SearchSection: A StatelessWidget that displays a search input field.

    • searchController: A TextEditingController to manage the text input.

    • InputDecoration: Styles the text field with a search icon, hint text, label, and rounded borders.

    show_modal.dart:

    Add this code to lib/widgets/show_modal.dart:

    <span class="hljs-comment">// show modal for image</span>
    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../models/product.dart'</span>;
    
    
    <span class="hljs-keyword">void</span> showImageModal(BuildContext context,Product product) {
      showDialog(
        context: context,
        builder: (BuildContext context) {
          <span class="hljs-keyword">return</span> Dialog(
            insetPadding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">12</span>),
            elevation: <span class="hljs-number">4</span>,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(<span class="hljs-number">20</span>),
            ),
            child: Padding(
              padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">3.0</span>),
              child: Stack(children: [
                ClipRRect(
                  borderRadius: BorderRadius.circular(<span class="hljs-number">20</span>),
                  child: Image(
                    width: <span class="hljs-built_in">double</span>.infinity,
                    fit: BoxFit.cover,
                    image: NetworkImage(product.imageUrl),
                  ),
                ),
                Positioned(
                  right: <span class="hljs-number">1</span>,
                  child: Container(
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
                      color: Colors.grey.withOpacity(<span class="hljs-number">0.5</span>),
                    ),
                    child: Padding(
                      padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
                      child: Row(
                        children: [
                          Text(product.name),
                          <span class="hljs-keyword">const</span> SizedBox(width: <span class="hljs-number">5</span>),
                          Text(
                            <span class="hljs-string">'$<span class="hljs-subst">${product.price}</span>'</span>,
                            style: <span class="hljs-keyword">const</span> TextStyle(
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                )
              ]),
            ),
          );
        },
      );
    }
    

    In this code:

    • showImageModal: A function that displays a dialog with a larger view of the product image and its name and price.

    • Dialog: A material design dialog.

    • Stack with Positioned: Used to overlay the product name and price on top of the image.

    single_product.dart:

    Add this code to lib/widgets/single_product.dart:

    <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
    
    <span class="hljs-keyword">import</span> <span class="hljs-string">'../models/product.dart'</span>;
    
    
    
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SingleProduct</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
      <span class="hljs-keyword">const</span> SingleProduct({
        <span class="hljs-keyword">super</span>.key,
        <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.product,
      });
    
      <span class="hljs-keyword">final</span> Product product;
    
      <span class="hljs-meta">@override</span>
      Widget build(BuildContext context) {
        <span class="hljs-keyword">return</span> Container(
          decoration: BoxDecoration(
            color: Colors.grey.withOpacity(<span class="hljs-number">0.1</span>),
            borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
          ),
          child: Column(
            children: [
              ClipRRect(
                borderRadius: <span class="hljs-keyword">const</span> BorderRadius.only(
                  topRight: Radius.circular(<span class="hljs-number">10</span>),
                  topLeft: Radius.circular(<span class="hljs-number">10</span>),
                ),
                child: Hero(
                  tag: product.id,
                  child: Image.network(
                    product.imageUrl,
                    height: <span class="hljs-number">120</span>,
                    width: <span class="hljs-built_in">double</span>.infinity,
                    fit: BoxFit.cover,
                  ),
                ),
              ),
              <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
              Text(
                product.name,
                maxLines: <span class="hljs-number">1</span>,
                overflow: TextOverflow.ellipsis,
                style: <span class="hljs-keyword">const</span> TextStyle(
                  fontSize: <span class="hljs-number">18</span>,
                  fontWeight: FontWeight.bold,
                ),
              ),
              <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
              Padding(
                padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">8.0</span>),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(<span class="hljs-string">'$<span class="hljs-subst">${product.price}</span>'</span>),
                    Text(
                      <span class="hljs-string">'$<span class="hljs-subst">${product.price}</span>'</span>,
                      style: <span class="hljs-keyword">const</span> TextStyle(
                        decoration: TextDecoration.lineThrough,
                      ),
                    ),
                  ],
                ),
              )
            ],
          ),
        );
      }
    }
    

    Here’s what’s happening:

    • SingleProduct: A StatelessWidget that displays a single product item in the grid.

    • product: The Product object to be displayed.

    • Container: Provides a background color and rounded borders.

    • Hero animation for image: Facilitates a smooth animation when transitioning to the ProductDetailsScreen.

    • Text for product name: Displays the product name, truncated if too long.

    • Row for prices: Shows the current price and the previous price with a strikethrough.

    A Few Screenshots:

    Home page of the app

    product detail page

    image previewer

    down section of the detail page

    dialog box display in the product detail page

    Conclusion

    go_router is a powerful and flexible routing library for Flutter, offering a clean and intuitive API for navigation. Whether you’re building a simple app or a complex navigation structure, go_router provides the tools you need to create a seamless user experience.

    By following this comprehensive guide, you should now be well-equipped to integrate and leverage go_router in your Flutter projects. The provided example of a minimalistic shopping app demonstrates practical application of its features.

    References

    1. For more advanced features and detailed code examples, refer to the official go_router documentation

    2. You can also check out the go_router GitHub repository

    Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleHow to Enhance Images with Neural Networks
    Next Article How to Perform Sentence Similarity Check Using Sentence Transformers

    Related Posts

    Development

    How to focus on building your skills when everything’s so distracting with Ania Kubów [Podcast #187]

    September 6, 2025
    Development

    Introducing freeCodeCamp Daily Python and JavaScript Challenges – Solve a New Programming Puzzle Every Day

    September 6, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    CodeSOD: Would a Function by Any Other Name Still be WTF?

    News & Updates

    CVE-2025-27031 – Cisco Router IOCTL Memory Corruption

    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-9595 – Code-projects Student Information Management System Cross Site Scripting Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-44091 – Yangyouwang Crud XSS

    Common Vulnerabilities and Exposures (CVEs)

    Highlights

    News & Updates

    The Xbox Game Bar for Windows 11 is becoming extremely good — It’s time to give Microsoft credit where it’s due

    May 13, 2025

    After years of graft, Microsoft’s Xbox apps on Windows 11 PCs and gaming handhelds are…

    tminesweeper – terminal-based minesweeper game

    June 18, 2025

    Multimodal LLMs Without Compromise: Researchers from UCLA, UW–Madison, and Adobe Introduce X-Fusion to Add Vision to Frozen Language Models Without Losing Language Capabilities

    May 8, 2025

    Before and After Suite in TestNG

    June 23, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.