Creating Breadcrumbs in Flutter Using GoRouter: A Step-by-Step Guide

Breadcrumbs are an essential UI element for enhancing app navigation, especially for hierarchical or multi-level applications. This blog demonstrates creating breadcrumbs in a Flutter app using the GoRouter package. Additionally, it will showcase a complete navigation system that adapts to mobile and web layouts.

What We’ll Build

We’ll create a Flutter app with the following:

  1. Navigation Drawer: For smaller screens (mobile).

  2. Sidebar Navigation: For larger screens (web).

  3. Breadcrumbs: To display the current navigation path and allow users to navigate back to previous levels.

Step 1: Setting Up the Project

First, create a new Flutter project and add the go_router package to the pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  go_router: ^14.6.2

Install the package by running:

flutter pub get

Step 2: Define Pages

Define the main screens for the app. Each screen will have basic content and a Scaffold for consistency. Below are the screen implementations:

DashboardPage

import 'package:flutter/material.dart';
class DashboardPage extends StatelessWidget {
  const DashboardPage({super.key});
@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Dashboard")),
      body: const Center(child: Text("Dashboard Content")),
    );
  }
}

Homework Page

import 'package:flutter/material.dart';
class Homework extends StatelessWidget {
  const Homework({super.key});
@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Homework")),
      body: const Center(child: Text("Homework Content")),
    );
  }
}

Similarly, create the Message, Circular, and ContactUs pages.

Step 3: Create the App Layout

The AppLayout widget serves as a container for the app’s structure. It supports:

  • Drawer navigation for mobile devices.

  • Sidebar navigation for web layouts.

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class AppLayout extends StatelessWidget {
  final Widget child;
const AppLayout({super.key, required this.child});
@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("My App")),
      drawer: MediaQuery.of(context).size.width < 600
          ? Drawer(
              child: NavigationMenu(
                onTap: (route) => context.go(route),
              ),
            )
          : null,
      body: Row(
        children: [
          if (MediaQuery.of(context).size.width >= 600)
            SizedBox(
              width: 200,
              child: NavigationMenu(
                onTap: (route) => context.go(route),
              ),
            ),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                if (MediaQuery.of(context).size.width >= 600) const Breadcrumbs(),
                Expanded(child: child),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Step 4: Build the Navigation Menu

The NavigationMenu widget is a reusable menu that supports:

  • Collapsible submenus for items like “Dashboard.”

  • Drawer closing behavior on mobile.

class NavigationMenu extends StatefulWidget {
  final Function(String route) onTap;
const NavigationMenu({super.key, required this.onTap});
@override
  State<NavigationMenu> createState() => _NavigationMenuState();
}
class _NavigationMenuState extends State<NavigationMenu> {
  bool isDashboardExpanded = false;
@override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        ListTile(
          title: const Text("Dashboard"),
          trailing: Icon(isDashboardExpanded ? Icons.expand_less : Icons.expand_more),
          onTap: () {
            setState(() {
              isDashboardExpanded = !isDashboardExpanded;
            });
          },
        ),
        if (isDashboardExpanded) ...[
          _buildSubmenuItem("Homework", "/dashboard/homework"),
          _buildSubmenuItem("Messages", "/dashboard/messages"),
          _buildSubmenuItem("Circular", "/dashboard/circular"),
        ],
        const Divider(),
        ListTile(
          title: const Text("Contact Us"),
          onTap: () {
            widget.onTap('/contactus');
            _closeDrawer(context);
          },
        ),
      ],
    );
  }
Widget _buildSubmenuItem(String title, String route) {
    return ListTile(
      title: Padding(
        padding: const EdgeInsets.only(left: 16.0),
        child: Text(title),
      ),
      onTap: () {
        widget.onTap(route);
        _closeDrawer(context);
      },
    );
  }
void _closeDrawer(BuildContext context) {
    if (MediaQuery.of(context).size.width < 600) {
      Navigator.of(context).pop();
    }
  }
}

Step 5: Add Breadcrumbs

The Breadcrumbs widget dynamically displays the current path and allows navigation to previous levels:

class Breadcrumbs extends StatelessWidget {
  const Breadcrumbs({super.key});
@override
  Widget build(BuildContext context) {
    final currentLocation = GoRouter.of(context).routerDelegate.currentConfiguration.fullPath;
    final segments = currentLocation.split('/').where((s) => s.isNotEmpty).toList();
return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Wrap(
        spacing: 8.0,
        children: [
          if (segments.isNotEmpty && segments.first == 'dashboard') ...[
            InkWell(
              onTap: () => context.go('/'),
              child: const Text(
                'Dashboard',
                style: TextStyle(color: Colors.blue),
              ),
            ),
          ],
          for (int i = 1; i < segments.length; i++) ...[
            const Text(' > '),
            InkWell(
              onTap: () {
                final path = '/' + segments.sublist(0, i + 1).join('/');
                context.go(path);
              },
              child: Text(
                segments[i].capitalize(),
                style: const TextStyle(color: Colors.blue),
              ),
            ),
          ],
        ],
      ),
    );
  }
}

extension StringExtension on String {
  String capitalize() => '${this[0].toUpperCase()}${substring(1)}';

Step 6: Define Routes Using GoRouter

Now define the routes for the application using GoRouter.

final GoRouter router = GoRouter(
  routes: [
    ShellRoute(
      builder: (context, state, child) => AppLayout(child: child),
      routes: [
        GoRoute(
          path: '/',
          builder: (context, state) => const DashboardPage(),
        ),
        GoRoute(
          path: '/dashboard/homework',
          builder: (context, state) => const Homework(),
        ),
        GoRoute(
          path: '/dashboard/messages',
          builder: (context, state) => const Message(),
        ),
        GoRoute(
          path: '/dashboard/circular',
          builder: (context, state) => const Circular(),
        ),
      ],
    ),
    GoRoute(
      path: '/contactus',
      builder: (context, state) => const AppLayout(child: ContactUs()),
    ),
  ],
);

Step 7: Run the Application

Finally, configure the MaterialApp.router to use the GoRouter.

void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
@override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      debugShowCheckedModeBanner: false,
      routerConfig: router,
    );
  }
}

Conclusion

In this blog, we built a Flutter app with breadcrumbs and responsive navigation using GoRouter. The complete code is available on GitHub. Clone it to explore further!

Hope you enjoyed this article!

0
Subscribe to my newsletter

Read articles from NonStop io Technologies directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

NonStop io Technologies
NonStop io Technologies

Product Development as an Expertise Since 2015 Founded in August 2015, we are a USA-based Bespoke Engineering Studio providing Product Development as an Expertise. With 80+ satisfied clients worldwide, we serve startups and enterprises across San Francisco, Seattle, New York, London, Pune, Bangalore, Tokyo and other prominent technology hubs.