Skip to main content

super_navigation_sidebar 3.2.0 copy "super_navigation_sidebar: ^3.2.0" to clipboard
super_navigation_sidebar: ^3.2.0 copied to clipboard

A themeable, responsive navigation sidebar for Flutter with expanded, rail, and drawer modes, typed NavNode<T> trees, search, favorites, recents, state persistence, localization, RTL support, and a re [...]

example/lib/main.dart

// super_navigation_sidebar · Example app launcher
//
// A polished launcher: hero header + responsive card grid, each card carrying
// a live token-driven mini-preview of the sidebar mode it opens. Every example
// is pushed with a floating "back to demos" button.

import 'package:flutter/material.dart';
import 'package:super_core/super_core.dart';
import 'package:super_navigation_sidebar/super_navigation_sidebar.dart';

import 'example_01_responsive_shell.dart';
import 'example_02_admin_dashboard.dart';
import 'example_03_theme_rtl.dart';
import 'example_04_erp_banking.dart';
import 'example_05_navigation_search_view.dart';
import 'example_07_kitchen_sink.dart';
import 'navigation_sidebar_demo.dart';

void main() => runApp(const ExampleApp());

class ExampleApp extends StatefulWidget {
  const ExampleApp({super.key});
  @override
  State<ExampleApp> createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  bool _dark = true;

  @override
  Widget build(BuildContext context) {
    final typography = SuperTextTheme();
    final dark = SuperMaterialThemeData.dark(
      textTheme: typography,
      primaryTextTheme: typography,
    );
    final light = SuperMaterialThemeData.light(
      textTheme: typography,
      primaryTextTheme: typography,
    );
    return MaterialApp(
      title: 'super_navigation_sidebar 3.2.0 examples',
      debugShowCheckedModeBanner: false,
      localizationsDelegates:
          SuperNavigationLocalization.localizationsDelegates,
      supportedLocales: SuperNavigationLocalization.supportedLocales,
      theme: light.copyWith(
        extensions: [SuperNavigationSidebarThemeData.fromMaterialTheme(light)],
      ),
      darkTheme: dark.copyWith(
        extensions: [SuperNavigationSidebarThemeData.fromMaterialTheme(dark)],
      ),
      themeMode: _dark ? ThemeMode.dark : ThemeMode.light,
      home: LauncherScreen(
        dark: _dark,
        onToggleTheme: (v) => setState(() => _dark = v),
      ),
    );
  }
}

// ════════════════════════════════════════════════════════════
// LAUNCHER
// ════════════════════════════════════════════════════════════
class LauncherScreen extends StatelessWidget {
  final bool dark;
  final ValueChanged<bool> onToggleTheme;
  const LauncherScreen({
    super.key,
    required this.dark,
    required this.onToggleTheme,
  });

  @override
  Widget build(BuildContext context) {
    final s = SuperNavigationSidebarThemeData.of(context);

    final demos = <_Demo>[
      _Demo(
        title: 'Kitchen sink — every feature (3.2)',
        subtitle:
            'One workbench with the full 3.2 surface: adaptive pane layouts, '
            'screen codes + keyword search, dialog/sheet SuperNavigationSearchView, '
            'recent navigation, snapshot save/restore, badge roll-up, favorites, '
            'locked/status nodes and RTL — all behind live toggles.',
        badge: 'ALL · search view · codes · recents · snapshot',
        preview: const _SidebarThumb(
          mode: _Mode.expanded,
          activeIndex: 2,
          badges: true,
          erp: true,
        ),
        screen: const KitchenSinkExample(),
      ),
      _Demo(
        title: 'Responsive app shell',
        subtitle:
            'A device-width simulator (Fill · Desktop · Tablet · Mobile) drives '
            'SuperNavSidebarBreakpoints — watch the same controller flip between '
            'expanded, rail and drawer modes.',
        badge: 'expanded · rail · drawer',
        preview: const _SidebarThumb(mode: _Mode.expanded, activeIndex: 1),
        screen: const ResponsiveShellExample(),
      ),
      _Demo(
        title: 'Admin dashboard',
        subtitle:
            '15+ nodes across 4 sections with badge tones, favorites and search. '
            'Deep-link from page content via of(context), and live badge updates '
            'through replaceSections.',
        badge: 'badges · favorites · search · of(context)',
        preview: const _SidebarThumb(
          mode: _Mode.expanded,
          activeIndex: 2,
          badges: true,
        ),
        screen: const AdminDashboardExample(),
      ),
      _Demo(
        title: 'Custom theme + RTL',
        subtitle:
            'Three themes (Dark / Light / Warm) built with copyWith, an LTR/RTL '
            'toggle, plus showGuides and railFlyouts switches.',
        badge: 'theming · RTL',
        preview: const _SidebarThumb(
          mode: _Mode.expanded,
          activeIndex: 0,
          warm: true,
        ),
        screen: const ThemeRtlExample(),
      ),
      _Demo(
        title: 'Banking / accounting ERP',
        subtitle:
            'The ERP capability set — built-in search & filter, Quick Access '
            'favorites, permission-locked screens, and fiscal-period status '
            'dots on a deep banking tree.',
        badge: 'search · favorites · locked · status',
        preview: const _SidebarThumb(
          mode: _Mode.expanded,
          activeIndex: 2,
          badges: true,
          erp: true,
        ),
        screen: const ErpBankingExample(),
      ),
      _Demo(
        title: 'SuperNavigationSearchView',
        subtitle:
            'The reusable 3.2 search experience shown inline, in a dialog, or '
            'as a modal bottom sheet. It shares the same controller and typed '
            'navigation tree used by SuperNavigationSidebar.',
        badge: 'embedded · dialog · sheet',
        preview: const _SidebarThumb(
          mode: _Mode.expanded,
          activeIndex: 1,
          badges: true,
        ),
        screen: const NavigationSearchViewExample(),
      ),
      _Demo(
        title: 'Full component workbench',
        subtitle:
            'The original showcase — device simulator, all three modes, live '
            'Light/Dark, LTR/RTL, sidebar search, and workspace + user menus.',
        badge: 'Original',
        preview: const _SidebarThumb(mode: _Mode.rail, activeIndex: 1),
        screen: const NavigationSidebarDemo(),
      ),
    ];

    return Scaffold(
      backgroundColor: s.bg,
      body: SafeArea(
        child: Center(
          child: ConstrainedBox(
            constraints: const BoxConstraints(maxWidth: 1040),
            child: ListView(
              padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 44),
              children: [
                // ── hero ──────────────────────────────────────
                Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Row(
                            children: [
                              _Mark(),
                              const SizedBox(width: 12),
                              Text(
                                'SUPER_NAVIGATION_SIDEBAR',
                                style: TextStyle(
                                  fontFamily:
                                      SuperNavigationSidebarThemeData.monoFont,
                                  fontSize: 12,
                                  fontWeight: FontWeight.w700,
                                  letterSpacing: 1.6,
                                  color: SuperNavigationSidebarThemeData.accent,
                                ),
                              ),
                              const SizedBox(width: 10),
                              _VersionPill(),
                            ],
                          ),
                          const SizedBox(height: 16),
                          Text(
                            'Responsive app navigation',
                            style: TextStyle(
                              fontFamily:
                                  SuperNavigationSidebarThemeData.displayFont,
                              fontSize: 34,
                              fontWeight: FontWeight.w800,
                              letterSpacing: -0.8,
                              height: 1.05,
                              color: s.fg1,
                            ),
                          ),
                          const SizedBox(height: 12),
                          ConstrainedBox(
                            constraints: const BoxConstraints(maxWidth: 640),
                            child: Text(
                              'One typed SuperNavNode<T> tree renders as an expanded '
                              'tree, an icon rail with flyouts, or an off-canvas '
                              'drawer. Search, badges, favorites, header/footer slots and RTL '
                              'are included. Open any example to try it live.',
                              style: TextStyle(
                                fontFamily:
                                    SuperNavigationSidebarThemeData.bodyFont,
                                fontSize: 14.5,
                                height: 1.6,
                                color: s.fg3,
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),
                    _ThemeToggle(dark: dark, onToggle: onToggleTheme),
                  ],
                ),
                const SizedBox(height: 36),
                // ── grid ──────────────────────────────────────
                LayoutBuilder(
                  builder: (context, c) {
                    final cols = c.maxWidth > 720 ? 2 : 1;
                    return GridView.count(
                      crossAxisCount: cols,
                      crossAxisSpacing: 18,
                      mainAxisSpacing: 18,
                      childAspectRatio: 1.42,
                      shrinkWrap: true,
                      physics: const NeverScrollableScrollPhysics(),
                      children: [
                        for (var i = 0; i < demos.length; i++)
                          _DemoCard(
                            index: i + 1,
                            demo: demos[i],
                            onTap: () => Navigator.of(context).push(
                              MaterialPageRoute(
                                builder: (_) =>
                                    _BackScaffold(child: demos[i].screen),
                              ),
                            ),
                          ),
                      ],
                    );
                  },
                ),
                const SizedBox(height: 24),
                Center(
                  child: Text(
                    'MIT © GeniusLink · pure Flutter, zero dependencies',
                    style: TextStyle(
                      fontFamily: SuperNavigationSidebarThemeData.monoFont,
                      fontSize: 11,
                      color: s.fg4,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class _Demo {
  final String title, subtitle, badge;
  final Widget preview;
  final Widget screen;
  const _Demo({
    required this.title,
    required this.subtitle,
    required this.badge,
    required this.preview,
    required this.screen,
  });
}

// ── Demo card ─────────────────────────────────────────────────────
class _DemoCard extends StatefulWidget {
  final int index;
  final _Demo demo;
  final VoidCallback onTap;
  const _DemoCard({
    required this.index,
    required this.demo,
    required this.onTap,
  });
  @override
  State<_DemoCard> createState() => _DemoCardState();
}

class _DemoCardState extends State<_DemoCard> {
  bool _h = false;
  @override
  Widget build(BuildContext context) {
    final s = SuperNavigationSidebarThemeData.of(context);
    return MouseRegion(
      cursor: SystemMouseCursors.click,
      onEnter: (_) => setState(() => _h = true),
      onExit: (_) => setState(() => _h = false),
      child: GestureDetector(
        onTap: widget.onTap,
        child: AnimatedContainer(
          duration: SuperNavigationSidebarThemeData.durFast,
          curve: SuperNavigationSidebarThemeData.curveStandard,
          transform: _h
              ? Matrix4.translationValues(0.0, -4.0, 0.0)
              : Matrix4.identity(),
          decoration: BoxDecoration(
            color: s.surface,
            border: Border.all(
              color: _h
                  ? SuperNavigationSidebarThemeData.accent
                      .withValues(alpha: 0.55)
                  : s.border,
            ),
            borderRadius: BorderRadius.circular(s.radiusXl),
            boxShadow: _h ? SuperNavigationSidebarThemeData.popShadow : null,
          ),
          clipBehavior: Clip.antiAlias,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Expanded(
                child: Stack(
                  children: [
                    Positioned.fill(child: widget.demo.preview),
                    Positioned(
                      top: 10,
                      right: 10,
                      child: Container(
                        width: 24,
                        height: 24,
                        alignment: Alignment.center,
                        decoration: BoxDecoration(
                          color: s.bg.withValues(alpha: 0.82),
                          borderRadius: BorderRadius.circular(7),
                          border: Border.all(color: s.border),
                        ),
                        child: Text(
                          '0${widget.index}',
                          style: TextStyle(
                            fontFamily:
                                SuperNavigationSidebarThemeData.monoFont,
                            fontSize: 10,
                            fontWeight: FontWeight.w700,
                            color: s.fg2,
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
              Container(
                decoration: BoxDecoration(
                  color: s.surface,
                  border: Border(top: BorderSide(color: s.border)),
                ),
                padding: const EdgeInsets.fromLTRB(16, 14, 16, 16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Expanded(
                          child: Text(
                            widget.demo.title,
                            style: TextStyle(
                              fontFamily:
                                  SuperNavigationSidebarThemeData.displayFont,
                              fontSize: 16.5,
                              fontWeight: FontWeight.w700,
                              color: s.fg1,
                            ),
                          ),
                        ),
                        Icon(
                          Icons.arrow_outward,
                          size: 16,
                          color: _h
                              ? SuperNavigationSidebarThemeData.accent
                              : s.fg3,
                        ),
                      ],
                    ),
                    const SizedBox(height: 6),
                    Text(
                      widget.demo.subtitle,
                      maxLines: 3,
                      overflow: TextOverflow.ellipsis,
                      style: TextStyle(
                        fontFamily: SuperNavigationSidebarThemeData.bodyFont,
                        fontSize: 12.5,
                        height: 1.5,
                        color: s.fg3,
                      ),
                    ),
                    const SizedBox(height: 10),
                    _TagPill(widget.demo.badge),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// ════════════════════════════════════════════════════════════
// MINI SIDEBAR PREVIEW (token-driven; reflects dark/light)
// ════════════════════════════════════════════════════════════
enum _Mode { expanded, rail }

class _SidebarThumb extends StatelessWidget {
  final _Mode mode;
  final int activeIndex;
  final bool badges;
  final bool warm;
  final bool erp;
  const _SidebarThumb({
    required this.mode,
    required this.activeIndex,
    this.badges = false,
    this.warm = false,
    this.erp = false,
  });

  @override
  Widget build(BuildContext context) {
    final base = SuperNavigationSidebarThemeData.of(context);
    final s = warm
        ? base.copyWith(
            bg: const Color(0xFFF3EEE7),
            surface: const Color(0xFFFFFFFF),
            inputBg: const Color(0xFFEAE3D9),
            hover: const Color(0xFFEAE3D9),
            border: const Color(0xFFDED7CB),
            guide: const Color(0xFFCEC6B9),
            fg1: const Color(0xFF231F1A),
            fg3: const Color(0xFF8A8175),
            fg4: const Color(0xFFB4A99A),
          )
        : base;
    const accent = SuperNavigationSidebarThemeData.accent;

    final rows = erp
        ? <(IconData, String, SuperNavBadgeTone?)>[
            (Icons.fact_check_outlined, 'Approvals', SuperNavBadgeTone.danger),
            (Icons.event_available_outlined, 'FY25 · Q3', null),
            (Icons.edit_note_outlined, 'Journal Entry', null),
            (Icons.bolt_outlined, 'Wire / SWIFT', null),
            (Icons.balance, 'Trial Balance', null),
          ]
        : <(IconData, String, SuperNavBadgeTone?)>[
            (Icons.dashboard_outlined, 'Dashboard', null),
            (
              Icons.menu_book_outlined,
              'Accounts',
              badges ? SuperNavBadgeTone.success : null,
            ),
            (
              Icons.receipt_long_outlined,
              'Journals',
              badges ? SuperNavBadgeTone.danger : null,
            ),
            (Icons.storefront_outlined, 'Inventory', null),
            (Icons.settings_outlined, 'Settings', null),
          ];

    Color toneColor(SuperNavBadgeTone t) {
      switch (t) {
        case SuperNavBadgeTone.success:
          return SuperNavigationSidebarThemeData.success;
        case SuperNavBadgeTone.danger:
          return SuperNavigationSidebarThemeData.danger;
        case SuperNavBadgeTone.warning:
          return SuperNavigationSidebarThemeData.warning;
        case SuperNavBadgeTone.muted:
          return s.fg3;
        case SuperNavBadgeTone.accent:
          return accent;
      }
    }

    final isRail = mode == _Mode.rail;
    final panelW = isRail ? 52.0 : 132.0;

    Widget railIcon(int i) {
      final (icon, _, tone) = rows[i];
      final active = i == activeIndex;
      return Container(
        width: 34,
        height: 34,
        margin: const EdgeInsets.symmetric(vertical: 3),
        alignment: Alignment.center,
        decoration: BoxDecoration(
          color: active ? accent.withValues(alpha: 0.16) : Colors.transparent,
          borderRadius: BorderRadius.circular(8),
          border:
              active ? Border.all(color: accent.withValues(alpha: 0.5)) : null,
        ),
        child: Stack(
          clipBehavior: Clip.none,
          children: [
            Icon(icon, size: 15, color: active ? accent : s.fg3),
            if (tone != null)
              Positioned(
                right: -3,
                top: -3,
                child: Container(
                  width: 6,
                  height: 6,
                  decoration: BoxDecoration(
                    color: toneColor(tone),
                    shape: BoxShape.circle,
                  ),
                ),
              ),
          ],
        ),
      );
    }

    Widget expandedRow(int i) {
      final (icon, label, tone) = rows[i];
      final active = i == activeIndex;
      final locked = erp && i == 3;
      final statusOpen = erp && i == 1;
      final star = erp && (i == 2 || i == 4);
      return Opacity(
        opacity: locked ? 0.5 : 1,
        child: Container(
          height: 26,
          margin: const EdgeInsets.only(bottom: 4),
          padding: const EdgeInsets.symmetric(horizontal: 8),
          decoration: BoxDecoration(
            color: active ? accent : Colors.transparent,
            borderRadius: BorderRadius.circular(7),
          ),
          child: Row(
            children: [
              Icon(icon, size: 12, color: active ? Colors.white : s.fg3),
              const SizedBox(width: 7),
              if (statusOpen) ...[
                Container(
                  width: 6,
                  height: 6,
                  decoration: const BoxDecoration(
                    color: SuperNavigationSidebarThemeData.success,
                    shape: BoxShape.circle,
                  ),
                ),
                const SizedBox(width: 5),
              ],
              Expanded(
                child: Text(
                  label,
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(
                    fontFamily: SuperNavigationSidebarThemeData.bodyFont,
                    fontSize: 10,
                    fontWeight: active ? FontWeight.w700 : FontWeight.w500,
                    color: active ? Colors.white : s.fg2,
                  ),
                ),
              ),
              if (tone != null)
                Container(
                  padding: const EdgeInsets.symmetric(
                    horizontal: 4,
                    vertical: 1,
                  ),
                  decoration: BoxDecoration(
                    color: active
                        ? Colors.white.withValues(alpha: 0.25)
                        : toneColor(tone).withValues(alpha: 0.18),
                    borderRadius: BorderRadius.circular(999),
                  ),
                  child: Text(
                    tone == SuperNavBadgeTone.success ? 'Live' : '9+',
                    style: TextStyle(
                      fontFamily: SuperNavigationSidebarThemeData.monoFont,
                      fontSize: 7,
                      fontWeight: FontWeight.w700,
                      color: active ? Colors.white : toneColor(tone),
                    ),
                  ),
                ),
              if (star)
                Icon(
                  Icons.star_rounded,
                  size: 11,
                  color: active
                      ? Colors.white
                      : SuperNavigationSidebarThemeData.accent,
                ),
              if (locked) Icon(Icons.lock_outline, size: 10, color: s.fg3),
            ],
          ),
        ),
      );
    }

    return Container(
      color: s.bg,
      child: Row(
        children: [
          // panel
          Container(
            width: panelW,
            decoration: BoxDecoration(
              color: s.surface,
              border: Border(right: BorderSide(color: s.border)),
            ),
            padding: EdgeInsets.symmetric(
              horizontal: isRail ? 9 : 10,
              vertical: 10,
            ),
            child: Column(
              crossAxisAlignment:
                  isRail ? CrossAxisAlignment.center : CrossAxisAlignment.start,
              children: [
                // header
                if (isRail)
                  Container(
                    width: 26,
                    height: 26,
                    alignment: Alignment.center,
                    decoration: BoxDecoration(
                      color: accent,
                      borderRadius: BorderRadius.circular(7),
                    ),
                    child: const Text(
                      'GL',
                      style: TextStyle(
                        fontFamily: SuperNavigationSidebarThemeData.displayFont,
                        fontSize: 9,
                        fontWeight: FontWeight.w800,
                        color: Colors.white,
                      ),
                    ),
                  )
                else
                  Row(
                    children: [
                      Container(
                        width: 22,
                        height: 22,
                        alignment: Alignment.center,
                        decoration: BoxDecoration(
                          color: accent,
                          borderRadius: BorderRadius.circular(6),
                        ),
                        child: const Text(
                          'GL',
                          style: TextStyle(
                            fontFamily:
                                SuperNavigationSidebarThemeData.displayFont,
                            fontSize: 8,
                            fontWeight: FontWeight.w800,
                            color: Colors.white,
                          ),
                        ),
                      ),
                      const SizedBox(width: 7),
                      Text(
                        'GeniusLink',
                        style: TextStyle(
                          fontFamily:
                              SuperNavigationSidebarThemeData.displayFont,
                          fontSize: 11,
                          fontWeight: FontWeight.w800,
                          color: s.fg1,
                        ),
                      ),
                    ],
                  ),
                const SizedBox(height: 12),
                if (erp && !isRail) ...[
                  Container(
                    height: 22,
                    padding: const EdgeInsetsDirectional.only(start: 7, end: 4),
                    decoration: BoxDecoration(
                      color: s.inputBg,
                      borderRadius: BorderRadius.circular(6),
                      border: Border.all(color: s.border),
                    ),
                    child: Row(
                      children: [
                        Icon(Icons.search, size: 10, color: s.fg3),
                        const SizedBox(width: 5),
                        Text(
                          'Search…',
                          style: TextStyle(
                            fontFamily:
                                SuperNavigationSidebarThemeData.bodyFont,
                            fontSize: 8.5,
                            color: s.fg4,
                          ),
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(height: 8),
                ],
                if (!isRail)
                  Padding(
                    padding: const EdgeInsets.only(left: 2, bottom: 6),
                    child: Text(
                      erp ? 'GENERAL LEDGER' : 'OVERVIEW',
                      style: TextStyle(
                        fontFamily: SuperNavigationSidebarThemeData.monoFont,
                        fontSize: 7,
                        letterSpacing: 1.2,
                        fontWeight: FontWeight.w700,
                        color: s.fg4,
                      ),
                    ),
                  ),
                Expanded(
                  child: Column(
                    children: [
                      for (var i = 0; i < rows.length; i++)
                        isRail ? railIcon(i) : expandedRow(i),
                    ],
                  ),
                ),
                // footer help
                if (!isRail)
                  Container(
                    height: 26,
                    padding: const EdgeInsets.symmetric(horizontal: 8),
                    decoration: BoxDecoration(
                      color: s.inputBg,
                      borderRadius: BorderRadius.circular(7),
                      border: Border.all(color: s.border),
                    ),
                    child: Row(
                      children: [
                        Icon(Icons.info_outline, size: 11, color: accent),
                        const SizedBox(width: 6),
                        Text(
                          'Need help?',
                          style: TextStyle(
                            fontFamily:
                                SuperNavigationSidebarThemeData.bodyFont,
                            fontSize: 9,
                            fontWeight: FontWeight.w600,
                            color: s.fg2,
                          ),
                        ),
                      ],
                    ),
                  )
                else
                  Container(
                    width: 32,
                    height: 26,
                    alignment: Alignment.center,
                    decoration: BoxDecoration(
                      color: s.inputBg,
                      borderRadius: BorderRadius.circular(7),
                      border: Border.all(color: s.border),
                    ),
                    child: Icon(Icons.info_outline, size: 12, color: accent),
                  ),
              ],
            ),
          ),
          // faux page
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(12),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Container(
                    width: 80,
                    height: 8,
                    decoration: BoxDecoration(
                      color: s.fg1.withValues(alpha: 0.65),
                      borderRadius: BorderRadius.circular(3),
                    ),
                  ),
                  const SizedBox(height: 10),
                  Row(
                    children: [
                      for (var i = 0; i < 3; i++) ...[
                        if (i > 0) const SizedBox(width: 6),
                        Expanded(
                          child: Container(
                            height: 30,
                            decoration: BoxDecoration(
                              color: s.surface,
                              borderRadius: BorderRadius.circular(6),
                              border: Border.all(color: s.border),
                            ),
                          ),
                        ),
                      ],
                    ],
                  ),
                  const SizedBox(height: 8),
                  Expanded(
                    child: Container(
                      width: double.infinity,
                      decoration: BoxDecoration(
                        color: s.surface,
                        borderRadius: BorderRadius.circular(6),
                        border: Border.all(color: s.border),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// ── small shared bits ─────────────────────────────────────────────
class _Mark extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 30,
      height: 30,
      alignment: Alignment.center,
      decoration: BoxDecoration(
        color: SuperNavigationSidebarThemeData.accent,
        borderRadius: BorderRadius.circular(8),
      ),
      child: const Icon(
        Icons.view_sidebar_rounded,
        size: 16,
        color: Colors.white,
      ),
    );
  }
}

class _VersionPill extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
      decoration: BoxDecoration(
        color: SuperNavigationSidebarThemeData.accent.withValues(alpha: 0.13),
        borderRadius: BorderRadius.circular(999),
        border: Border.all(
          color: SuperNavigationSidebarThemeData.accent.withValues(alpha: 0.35),
        ),
      ),
      child: const Text(
        'v3.2.0',
        style: TextStyle(
          fontFamily: SuperNavigationSidebarThemeData.monoFont,
          fontSize: 10.5,
          fontWeight: FontWeight.w700,
          color: SuperNavigationSidebarThemeData.accent,
        ),
      ),
    );
  }
}

class _TagPill extends StatelessWidget {
  final String text;
  const _TagPill(this.text);
  @override
  Widget build(BuildContext context) {
    final s = SuperNavigationSidebarThemeData.of(context);
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      decoration: BoxDecoration(
        color: s.inputBg,
        borderRadius: BorderRadius.circular(6),
        border: Border.all(color: s.border),
      ),
      child: Text(
        text,
        style: TextStyle(
          fontFamily: SuperNavigationSidebarThemeData.monoFont,
          fontSize: 10,
          fontWeight: FontWeight.w600,
          letterSpacing: 0.3,
          color: s.fg3,
        ),
      ),
    );
  }
}

class _ThemeToggle extends StatelessWidget {
  final bool dark;
  final ValueChanged<bool> onToggle;
  const _ThemeToggle({required this.dark, required this.onToggle});
  @override
  Widget build(BuildContext context) {
    final s = SuperNavigationSidebarThemeData.of(context);
    return GestureDetector(
      onTap: () => onToggle(!dark),
      child: MouseRegion(
        cursor: SystemMouseCursors.click,
        child: Container(
          height: 38,
          padding: const EdgeInsets.symmetric(horizontal: 14),
          decoration: BoxDecoration(
            color: s.surface,
            border: Border.all(color: s.borderStrong),
            borderRadius: BorderRadius.circular(s.radiusMd),
          ),
          child: Row(
            children: [
              Icon(
                dark ? Icons.dark_mode_outlined : Icons.light_mode_outlined,
                size: 15,
                color: s.fg2,
              ),
              const SizedBox(width: 8),
              Text(
                dark ? 'Dark' : 'Light',
                style: TextStyle(
                  fontFamily: SuperNavigationSidebarThemeData.bodyFont,
                  fontSize: 13,
                  fontWeight: FontWeight.w600,
                  color: s.fg1,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// ── floating "back to demos" wrapper for a pushed screen ──────────
class _BackScaffold extends StatelessWidget {
  final Widget child;
  const _BackScaffold({required this.child});
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Positioned.fill(child: child),
        Positioned(
          left: 16,
          bottom: 16,
          child: SafeArea(
            child: Material(
              color: Colors.black.withValues(alpha: 0.62),
              borderRadius: BorderRadius.circular(999),
              child: InkWell(
                borderRadius: BorderRadius.circular(999),
                onTap: () => Navigator.of(context).maybePop(),
                child: const Padding(
                  padding: EdgeInsets.symmetric(horizontal: 14, vertical: 10),
                  child: Row(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Icon(Icons.arrow_back, size: 16, color: Colors.white),
                      SizedBox(width: 7),
                      Text(
                        'Demos',
                        style: TextStyle(
                          fontFamily: SuperNavigationSidebarThemeData.bodyFont,
                          fontSize: 13,
                          fontWeight: FontWeight.w600,
                          color: Colors.white,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}
0
likes
150
points
136
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A themeable, responsive navigation sidebar for Flutter with expanded, rail, and drawer modes, typed NavNode<T> trees, search, favorites, recents, state persistence, localization, RTL support, and a reusable NavigationSearchView that opens as a dialog or modal bottom sheet.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, flutter_localizations, intl, super_core, super_form_field

More

Packages that depend on super_navigation_sidebar