DEV Community

Cover image for How to Use AutoTabsRouter for a Custom Drawer Dashboard in Flutter (Admin Panel)
مصعب يوسف
مصعب يوسف

Posted on Originally published at Medium on

How to Use AutoTabsRouter for a Custom Drawer Dashboard in Flutter (Admin Panel)

How to Use AutoTabsRouter for a Custom Drawer Dashboard in Flutter (Admin Panel)

Creating an admin dashboard with a custom side menu (drawer) in Flutter can be elegantly achieved using the auto_route package. With AutoTabsRouter, we can manage tabs and nested routing while keeping the code modular and easy to scale.

In this article, we’ll walk through:

  • Setting up AutoRoute
  • Creating a SideMenu (custom drawer)
  • Using AutoTabsRoute
  • Nesting routes and using a wrapper screen when needed

Packages Used

dependencies:
  auto_route: ^7.2.0
  flutter:
    sdk: flutter

dev_dependencies:
flutter_lints: ^3.0.2
auto_route_generator:
json_serializable:
Enter fullscreen mode Exit fullscreen mode

Step 1: Define the App Routes

We define our app routes using AutoRoute and nest screens under HomeRoute, including a RouterWrapperRoute for grouping related screens.

@AutoRouterConfig(
  replaceInRouteName: 'Screen,Route',
)
class AppRouter extends _$AppRouter {
  AppRouter([GlobalKey<NavigatorState>? navigatorKey])
      : super(navigatorKey: navigatorKey);

@override
  List<AutoRoute> get routes => [
        AutoRoute(page: LoginRoute.page, path: '/auth/login'),
        AutoRoute(page: ForgetPasswordRoute.page, path: '/forget-password'),
        AutoRoute(page: ChangePasswordRoute.page, path: '/change-password'),
        AutoRoute(
          page: HomeRoute.page,
          path: "/home",
          initial: true,
          children: [
            AutoRoute(
              path: 'dashboard',
              page: DashboardRoute.page,
              initial: true,
            ),
            // Wrapped routes (nested)
            AutoRoute(
              path: '',
              page: RouterWrapperRoute.page,
              children: [
                AutoRoute(path: 'events', page: EventsRoute.page, initial: true),
                AutoRoute(path: 'create-event', page: CreateNewEventRoute.page),
              ],
            ),
          ],
        ),
      ];
}
Enter fullscreen mode Exit fullscreen mode

Why Use a Wrapper?

Some screens (like EventsRoute and CreateNewEventRoute) are related and should share layout or behavior. So we wrap them inside a common route (RouterWrapperRoute), which gives us better control and a cleaner hierarchy.

Step 2: Building the HomeScreenBody with AutoTabsRouter

We use AutoTabsRouter.pageView to handle switching between routes inside the admin dashboard

class HomeScreenBody extends StatelessWidget {
  const HomeScreenBody({super.key});

@override
  Widget build(BuildContext context) {
    return AutoTabsRouter.pageView(
      routes: const [
        DashboardRoute(),
        EventsRoute(), // Comes from RouterWrapper
      ],
      builder: (context, child, _) {
        final tabsRouter = AutoTabsRouter.of(context);
        return Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            SideMenu(tabsRouter: tabsRouter), // Our Custom Drawer
            Expanded(child: child),
          ],
        );
      },
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: The Custom Side Drawer (SideMenu)

class SideMenu extends StatelessWidget {
  final TabsRouter tabsRouter;
  const SideMenu({super.key, required this.tabsRouter});

@override
  Widget build(BuildContext context) {
    final currentIndex = tabsRouter.activeIndex;
    return Container(
      width: 235,
      decoration: BoxDecoration(color: context.colors.whiteColor),
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 24.rw, vertical: 16.rh),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Assets.images.ventlyLogoDashboard.image(package: 'design_system', width: 135.rw),
            Gap(24.rh),
            MenuItem(
              title: context.homeDashboard,
              icon: Assets.dashboard.dashboard,
              onTap: () => tabsRouter.setActiveIndex(0),
              isSelected: currentIndex == 0,
            ),
            MenuItem(
              title: context.homeEvents,
              icon: Assets.dashboard.events,
              onTap: () => tabsRouter.setActiveIndex(1),
              isSelected: currentIndex == 1,
            ),
          ],
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

MenuItem Component

class MenuItem extends StatelessWidget {
  const MenuItem({
    super.key,
    required this.title,
    required this.icon,
    this.trailing,
    this.onTap,
    this.isSelected = false,
  });

final String title;
  final SvgGenImage icon;
  final VoidCallback? onTap;
  final bool isSelected;
  final Widget? trailing;
  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: onTap,
      borderRadius: BorderRadius.circular(8),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
        decoration: BoxDecoration(
          color: isSelected ? context.colors.primaryColor : Colors.transparent,
          borderRadius: BorderRadius.circular(8),
        ),
        child: Row(
          children: [
            AppIcon(icon,
                color: isSelected
                    ? context.colors.whiteColor
                    : context.colors.primaryColor),
            Gap(8.rw),
            Expanded(
              child: AppText.body16TextStyle(
                title,
                color: isSelected
                    ? context.colors.whiteColor
                    : context.colors.primaryColor,
              ),
            ),
            if (trailing != null) trailing!,
          ],
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

How to Use AutoTabsRouter for a Custom Drawer Dashboard in Flutter (Admin Panel)

Step 4: Adding a Wrapper Screen for Nested Tabs

You can create a screen like RouterWrapperScreen that acts as a layout wrapper for multiple nested router

import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';

@RoutePage()
class RouterWrapperScreen extends StatelessWidget {
  const RouterWrapperScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: AutoRouter(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

This allows nested routes under it to inherit layout or functionality without needing repetition.

Final Thoughts

With AutoTabsRouter, you can create a scalable, clean and nested dashboard layout in Flutter. Whether you're building an admin panel, CMS, or B2B dashboard — combining a custom side menu and auto_route makes routing feel native and seamless.

Want to go further? Add role-based access, dynamic tabs, or responsive drawer behavior.

Top comments (0)