voo_navigation 1.0.9 copy "voo_navigation: ^1.0.9" to clipboard
voo_navigation: ^1.0.9 copied to clipboard

A comprehensive, adaptive navigation package for Flutter that automatically adjusts to different screen sizes and platforms with Material 3 design.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:voo_navigation/voo_navigation.dart';

import 'page_override_example.dart';
import 'theme_showcase_example.dart';

void main() {
  runApp(const VooNavigationExampleApp());
}

class VooNavigationExampleApp extends StatefulWidget {
  const VooNavigationExampleApp({super.key});

  @override
  State<VooNavigationExampleApp> createState() => _VooNavigationExampleAppState();
}

class _VooNavigationExampleAppState extends State<VooNavigationExampleApp> {
  ThemeMode _themeMode = ThemeMode.system;

  void _toggleTheme() {
    setState(() {
      _themeMode = _themeMode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'VooNavigation Demo',
      debugShowCheckedModeBanner: false,
      themeMode: _themeMode,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF4F75FF),
          brightness: Brightness.light,
        ),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF4F75FF),
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
      ),
      home: ExampleSelector(onToggleTheme: _toggleTheme),
    );
  }
}

/// Landing page to select between examples
class ExampleSelector extends StatelessWidget {
  final VoidCallback onToggleTheme;

  const ExampleSelector({super.key, required this.onToggleTheme});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('VooNavigation Examples'),
        actions: [
          IconButton(
            icon: Icon(
              Theme.of(context).brightness == Brightness.dark ? Icons.light_mode : Icons.dark_mode,
            ),
            onPressed: onToggleTheme,
            tooltip: 'Toggle theme',
          ),
        ],
      ),
      body: Center(
        child: ConstrainedBox(
          constraints: const BoxConstraints(maxWidth: 600),
          child: Padding(
            padding: const EdgeInsets.all(24),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Icon(
                  Icons.navigation,
                  size: 64,
                  color: theme.colorScheme.primary,
                ),
                const SizedBox(height: 24),
                Text(
                  'VooNavigation',
                  style: theme.textTheme.headlineLarge?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
                  textAlign: TextAlign.center,
                ),
                const SizedBox(height: 8),
                Text(
                  'Adaptive Navigation Package',
                  style: theme.textTheme.bodyLarge?.copyWith(
                    color: theme.colorScheme.onSurfaceVariant,
                  ),
                  textAlign: TextAlign.center,
                ),
                const SizedBox(height: 48),
                _buildExampleCard(
                  context,
                  title: 'Theme Showcase',
                  description: 'Explore all 4 visual theme presets: Glassmorphism, Neomorphism, Material 3 Enhanced, and Minimal Modern',
                  icon: Icons.palette,
                  isPrimary: true,
                  onTap: () => Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (_) => const ThemeShowcaseExample(),
                    ),
                  ),
                ),
                const SizedBox(height: 16),
                _buildExampleCard(
                  context,
                  title: 'Page Overrides',
                  description: 'VooPage for per-page customization: custom FAB, app bar, fullscreen pages',
                  icon: Icons.layers,
                  isPrimary: false,
                  onTap: () => Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (_) => const PageOverrideExample(),
                    ),
                  ),
                ),
                const SizedBox(height: 16),
                _buildExampleCard(
                  context,
                  title: 'Full Feature Demo',
                  description: 'Complete navigation example with sections, badges, custom headers, and responsive layouts',
                  icon: Icons.dashboard,
                  isPrimary: false,
                  onTap: () => Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (_) => const NavigationExample(),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildExampleCard(
    BuildContext context, {
    required String title,
    required String description,
    required IconData icon,
    required bool isPrimary,
    required VoidCallback onTap,
  }) {
    final theme = Theme.of(context);

    return Card(
      elevation: isPrimary ? 4 : 1,
      clipBehavior: Clip.antiAlias,
      child: InkWell(
        onTap: onTap,
        child: Container(
          decoration: isPrimary
              ? BoxDecoration(
                  gradient: LinearGradient(
                    colors: [
                      theme.colorScheme.primaryContainer,
                      theme.colorScheme.secondaryContainer,
                    ],
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                  ),
                )
              : null,
          padding: const EdgeInsets.all(20),
          child: Row(
            children: [
              Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: isPrimary ? theme.colorScheme.primary : theme.colorScheme.surfaceContainerHigh,
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Icon(
                  icon,
                  color: isPrimary ? theme.colorScheme.onPrimary : theme.colorScheme.onSurfaceVariant,
                  size: 28,
                ),
              ),
              const SizedBox(width: 16),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      title,
                      style: theme.textTheme.titleMedium?.copyWith(
                        fontWeight: FontWeight.bold,
                        color: isPrimary ? theme.colorScheme.onPrimaryContainer : null,
                      ),
                    ),
                    const SizedBox(height: 4),
                    Text(
                      description,
                      style: theme.textTheme.bodySmall?.copyWith(
                        color: isPrimary ? theme.colorScheme.onPrimaryContainer.withValues(alpha: 0.8) : theme.colorScheme.onSurfaceVariant,
                      ),
                    ),
                  ],
                ),
              ),
              Icon(
                Icons.chevron_right,
                color: isPrimary ? theme.colorScheme.onPrimaryContainer : theme.colorScheme.onSurfaceVariant,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class NavigationExample extends StatefulWidget {
  const NavigationExample({super.key});

  @override
  State<NavigationExample> createState() => _NavigationExampleState();
}

class _NavigationExampleState extends State<NavigationExample> {
  String _selectedId = 'dashboard';
  bool _isDrawerCollapsed = false;

  /// Helper to find an item by ID, searching through children as well
  VooNavigationItem? _findItemById(String id, List<VooNavigationItem> items) {
    for (final item in items) {
      if (item.id == id) return item;
      if (item.children != null) {
        final found = _findItemById(id, item.children!);
        if (found != null) return found;
      }
    }
    return null;
  }

  // Create navigation items with various features
  List<VooNavigationItem> get _navigationItems => [
        VooNavigationItem(
          id: 'dashboard',
          label: 'Dashboard',
          mobilePriority: true,
          icon: Icons.dashboard_outlined,
          selectedIcon: Icons.dashboard,
          onTap: () {},
          tooltip: 'View your dashboard',
          badgeCount: 3,
        ),
        VooNavigationItem(
          id: 'analytics',
          label: 'Analytics',
          mobilePriority: true,
          icon: Icons.analytics_outlined,
          selectedIcon: Icons.analytics,
          onTap: () {},
          tooltip: 'View analytics',
          showDot: true,
          badgeColor: Colors.green,
        ),
        VooNavigationItem(
          id: 'projects',
          label: 'Projects',
          mobilePriority: true,
          icon: Icons.folder_outlined,
          selectedIcon: Icons.folder,
          onTap: () {},
          tooltip: 'Manage projects',
          badgeText: 'NEW',
          badgeColor: Colors.orange,
        ),
        VooNavigationItem.section(
          label: 'Communication',
          id: 'communication',
          isExpanded: true,
          children: [
            VooNavigationItem(
              id: 'messages',
              label: 'Messages',
              mobilePriority: true,
              icon: Icons.message_outlined,
              selectedIcon: Icons.message,
              onTap: () {},
              badgeCount: 12,
            ),
            VooNavigationItem(
              id: 'notifications',
              label: 'Notifications',
              mobilePriority: true,
              icon: Icons.notifications_outlined,
              selectedIcon: Icons.notifications,
              onTap: () {},
              badgeCount: 5,
            ),
            VooNavigationItem(
              id: 'email',
              label: 'Email',
              icon: Icons.email_outlined,
              selectedIcon: Icons.email,
              onTap: () {},
              showDot: true,
            ),
          ],
        ),
        VooNavigationItem.divider(),
        VooNavigationItem(
          id: 'calendar',
          label: 'Calendar',
          icon: Icons.calendar_today_outlined,
          selectedIcon: Icons.calendar_today,
          onTap: () {},
          tooltip: 'View calendar',
        ),
        VooNavigationItem(
          id: 'team',
          label: 'Team',
          icon: MdiIcons.accountGroupOutline,
          selectedIcon: MdiIcons.accountGroup,
          onTap: () {},
          tooltip: 'Manage team',
        ),
        VooNavigationItem(
          id: 'settings',
          label: 'Settings',
          icon: Icons.settings_outlined,
          selectedIcon: Icons.settings,
          onTap: () {},
          tooltip: 'App settings',
        ),
        VooNavigationItem(
          id: 'help',
          label: 'Help & Support',
          icon: Icons.help_outline,
          selectedIcon: Icons.help,
          onTap: () {},
          tooltip: 'Get help',
          isEnabled: true,
        ),
      ];

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    // Create navigation configuration
    final config = VooNavigationConfig(
      items: _navigationItems,
      selectedId: _selectedId,
      isAdaptive: true,
      enableAnimations: true,
      enableHapticFeedback: true,
      showNotificationBadges: true,
      groupItemsBySections: true,
      animationDuration: const Duration(milliseconds: 300),
      animationCurve: Curves.easeInOutCubic,
      railLabelType: NavigationRailLabelType.selected,
      // Only extend rail when width > 840px, respecting responsive breakpoints
      useExtendedRail: true,
      showNavigationRailDivider: true,
      // App bar positioned alongside navigation rail (default behavior)
      appBarAlongsideRail: true,
      selectedItemColor: const Color(0xFF4F75FF),
      indicatorShape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      // Enable collapsible drawer on desktop
      enableCollapsibleRail: true,
      onCollapseChanged: (isCollapsed) {
        setState(() {
          _isDrawerCollapsed = isCollapsed;
        });
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(isCollapsed ? 'Drawer collapsed' : 'Drawer expanded'),
            duration: const Duration(milliseconds: 500),
          ),
        );
      },
      // Show user profile in footer
      showUserProfile: true,
      userProfileWidget: VooUserProfileFooter(
        userName: 'John Doe',
        userEmail: 'john.doe@example.com',
        initials: 'JD',
        status: VooUserStatus.online,
        onTap: () {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Profile tapped')),
          );
        },
        onSettingsTap: () {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Settings tapped')),
          );
        },
        onLogout: () {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Logout tapped')),
          );
        },
      ),
      drawerHeader: Container(
        height: 200,
        decoration: BoxDecoration(
          gradient: LinearGradient(
            colors: [
              theme.colorScheme.primary,
              theme.colorScheme.secondary,
            ],
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
          ),
        ),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(
                Icons.rocket_launch,
                size: 64,
                color: theme.colorScheme.onPrimary,
              ),
              const SizedBox(height: 16),
              Text(
                'VooNavigation',
                style: theme.textTheme.headlineSmall?.copyWith(
                  color: theme.colorScheme.onPrimary,
                  fontWeight: FontWeight.bold,
                ),
              ),
              const SizedBox(height: 4),
              Text(
                'Adaptive Navigation System',
                style: theme.textTheme.bodyMedium?.copyWith(
                  color: theme.colorScheme.onPrimary.withAlpha((0.8 * 255).round()),
                ),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('FAB pressed')),
          );
        },
        label: const Text('Create'),
        icon: const Icon(Icons.add),
      ),
      onNavigationItemSelected: (itemId) {
        setState(() {
          _selectedId = itemId;
        });

        // Show snackbar for demonstration
        final item = _findItemById(itemId, _navigationItems);
        if (item != null) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text('Selected: ${item.label}'),
              duration: const Duration(seconds: 1),
            ),
          );
        }
      },
    );

    // Build adaptive scaffold
    return VooAdaptiveScaffold(
      config: config,
      body: _buildContent(context),
    );
  }

  Widget _buildContent(BuildContext context) {
    final theme = Theme.of(context);
    final selectedItem = _findItemById(_selectedId, _navigationItems) ?? _navigationItems.first;

    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [
            theme.colorScheme.surface,
            theme.colorScheme.surface.withAlpha((0.95 * 255).round()),
          ],
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
        ),
      ),
      child: Container(
        alignment: Alignment.center,
        color: Colors.purple,
        child: SingleChildScrollView(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(
                selectedItem.effectiveSelectedIcon,
                size: 120,
                color: theme.colorScheme.primary,
              ),
              const SizedBox(height: 24),
              Text(
                selectedItem.label,
                style: theme.textTheme.displaySmall?.copyWith(
                  fontWeight: FontWeight.bold,
                  color: theme.colorScheme.onSurface,
                ),
              ),
              const SizedBox(height: 16),
              Text(
                'This is the ${selectedItem.label} page content',
                style: theme.textTheme.bodyLarge?.copyWith(
                  color: theme.colorScheme.onSurfaceVariant,
                ),
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 48),
              _buildFeatureCards(context),
              const SizedBox(height: 32),
              _buildScreenSizeInfo(context),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildFeatureCards(BuildContext context) {
    final theme = Theme.of(context);

    final features = [
      ('Adaptive', Icons.devices, 'Automatically adjusts to screen size'),
      ('Material 3', Icons.palette, 'Follows latest Material Design'),
      ('Customizable', Icons.tune, 'Extensive customization options'),
      ('Animated', Icons.animation, 'Beautiful transitions'),
    ];

    return Wrap(
      spacing: 16,
      runSpacing: 16,
      alignment: WrapAlignment.center,
      children: features.map((feature) {
        return Card(
          elevation: 2,
          child: Container(
            width: 200,
            padding: const EdgeInsets.all(20),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(
                  feature.$2,
                  size: 48,
                  color: theme.colorScheme.primary,
                ),
                const SizedBox(height: 12),
                Text(
                  feature.$1,
                  style: theme.textTheme.titleMedium?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const SizedBox(height: 8),
                Text(
                  feature.$3,
                  textAlign: TextAlign.center,
                  style: theme.textTheme.bodySmall?.copyWith(
                    color: theme.colorScheme.onSurfaceVariant,
                  ),
                ),
              ],
            ),
          ),
        );
      }).toList(),
    );
  }

  Widget _buildScreenSizeInfo(BuildContext context) {
    final theme = Theme.of(context);
    final size = MediaQuery.of(context).size;

    String getNavigationType() {
      if (size.width < 600) return 'Bottom Navigation';
      if (size.width < 840) return 'Navigation Rail';
      if (size.width < 1240) return 'Extended Navigation Rail';
      return 'Navigation Drawer';
    }

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            Icon(
              Icons.aspect_ratio,
              size: 48,
              color: theme.colorScheme.secondary,
            ),
            const SizedBox(height: 16),
            Text(
              'Screen Information',
              style: theme.textTheme.titleLarge?.copyWith(
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 16),
            Text(
              'Width: ${size.width.toStringAsFixed(0)}px',
              style: theme.textTheme.bodyLarge,
            ),
            const SizedBox(height: 8),
            Text(
              'Height: ${size.height.toStringAsFixed(0)}px',
              style: theme.textTheme.bodyLarge,
            ),
            const SizedBox(height: 8),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              decoration: BoxDecoration(
                color: theme.colorScheme.primaryContainer,
                borderRadius: BorderRadius.circular(20),
              ),
              child: Text(
                getNavigationType(),
                style: TextStyle(
                  color: theme.colorScheme.onPrimaryContainer,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ),
            if (_isDrawerCollapsed) ...[
              const SizedBox(height: 8),
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
                decoration: BoxDecoration(
                  color: theme.colorScheme.tertiaryContainer,
                  borderRadius: BorderRadius.circular(16),
                ),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Icon(
                      Icons.compress,
                      size: 14,
                      color: theme.colorScheme.onTertiaryContainer,
                    ),
                    const SizedBox(width: 4),
                    Text(
                      'Drawer Collapsed',
                      style: TextStyle(
                        color: theme.colorScheme.onTertiaryContainer,
                        fontWeight: FontWeight.w500,
                        fontSize: 12,
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ],
        ),
      ),
    );
  }
}
1
likes
140
points
805
downloads

Publisher

verified publishervoostack.com

Weekly Downloads

A comprehensive, adaptive navigation package for Flutter that automatically adjusts to different screen sizes and platforms with Material 3 design.

Homepage
Repository (GitHub)
View/report issues

Topics

#flutter #navigation #responsive #material-design

Documentation

API reference

License

MIT (license)

Dependencies

collection, equatable, flutter, flutter_bloc, go_router, material_design_icons_flutter, meta, voo_motion, voo_responsive, voo_tokens, voo_ui_core

More

Packages that depend on voo_navigation