hydro_glass_nav_bar 1.0.0-pre.1 copy "hydro_glass_nav_bar: ^1.0.0-pre.1" to clipboard
hydro_glass_nav_bar: ^1.0.0-pre.1 copied to clipboard

A beautiful, Apple-style hydro glass floating navigation bar with advanced physics-based animations, draggable indicator, and expandable FAB support for Flutter.

example/lib/main.dart

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

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

/// Example app demonstrating the hydro_glass_nav_bar package.
class HydroGlassNavBarExample extends StatelessWidget {
  const HydroGlassNavBarExample({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hydro Glass Nav Bar',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.deepPurple,
          brightness: Brightness.light,
        ),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.deepPurple,
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
      ),
      themeMode: ThemeMode.system,
      home: const HomeScreen(),
    );
  }
}

/// Home screen with the hydro glass nav bar.
class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;
  final ValueNotifier<int> _selectedCount = ValueNotifier(0);

  static const _items = [
    HydroGlassNavItem(
      label: 'Home',
      icon: Icons.home_outlined,
      selectedIcon: Icons.home,
    ),
    HydroGlassNavItem(
      label: 'Search',
      icon: Icons.search_outlined,
      selectedIcon: Icons.search,
    ),
    HydroGlassNavItem(
      label: 'Favorites',
      icon: Icons.favorite_outline,
      selectedIcon: Icons.favorite,
    ),
    HydroGlassNavItem(
      label: 'Profile',
      icon: Icons.person_outline,
      selectedIcon: Icons.person,
    ),
  ];

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: _items.length, vsync: this);
  }

  @override
  void dispose() {
    _tabController.dispose();
    _selectedCount.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // Background content
          TabBarView(
            controller: _tabController,
            children: [
              _PageContent(
                title: 'Home',
                icon: Icons.home,
                color: Colors.blue,
                onSelectionChanged: (count) => _selectedCount.value = count,
              ),
              _PageContent(
                title: 'Search',
                icon: Icons.search,
                color: Colors.green,
                onSelectionChanged: (count) => _selectedCount.value = count,
              ),
              _PageContent(
                title: 'Favorites',
                icon: Icons.favorite,
                color: Colors.red,
                onSelectionChanged: (count) => _selectedCount.value = count,
              ),
              _PageContent(
                title: 'Profile',
                icon: Icons.person,
                color: Colors.purple,
                onSelectionChanged: (count) => _selectedCount.value = count,
              ),
            ],
          ),

          // Hydro Glass Nav Bar
          HydroGlassNavBar(
            controller: _tabController,
            items: _items,
            selectedItemsCount: _selectedCount,
            fabConfig: HydroGlassNavBarFABConfig(
              icon: Icons.more_vert,
              semanticLabel: 'Actions',
              actions: [
                HydroGlassNavBarAction(
                  icon: Icons.share,
                  label: 'Share',
                  onTap: () => _showSnackBar(context, 'Share tapped'),
                ),
                HydroGlassNavBarAction(
                  icon: Icons.download,
                  label: 'Download',
                  onTap: () => _showSnackBar(context, 'Download tapped'),
                ),
                HydroGlassNavBarAction(
                  icon: Icons.delete,
                  label: 'Delete',
                  onTap: () {
                    _showSnackBar(context, 'Delete tapped');
                    _selectedCount.value = 0;
                  },
                  isDanger: true,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  void _showSnackBar(BuildContext context, String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        behavior: SnackBarBehavior.floating,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        margin: const EdgeInsets.fromLTRB(16, 16, 16, 100),
      ),
    );
  }
}

/// Content for each page.
class _PageContent extends StatefulWidget {
  const _PageContent({
    required this.title,
    required this.icon,
    required this.color,
    required this.onSelectionChanged,
  });

  final String title;
  final IconData icon;
  final Color color;
  final ValueChanged<int> onSelectionChanged;

  @override
  State<_PageContent> createState() => _PageContentState();
}

class _PageContentState extends State<_PageContent> {
  final Set<int> _selectedItems = {};

  void _toggleSelection(int index) {
    setState(() {
      if (_selectedItems.contains(index)) {
        _selectedItems.remove(index);
      } else {
        _selectedItems.add(index);
      }
    });
    widget.onSelectionChanged(_selectedItems.length);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [
            widget.color.withValues(alpha: 0.1),
            widget.color.withValues(alpha: 0.05),
          ],
        ),
      ),
      child: CustomScrollView(
        slivers: [
          SliverAppBar.large(
            title: Text(widget.title),
            centerTitle: false,
            backgroundColor: Colors.transparent,
            actions: [
              if (_selectedItems.isNotEmpty)
                Padding(
                  padding: const EdgeInsets.only(right: 8),
                  child: Chip(
                    label: Text('${_selectedItems.length} selected'),
                    deleteIcon: const Icon(Icons.close, size: 18),
                    onDeleted: () {
                      setState(() => _selectedItems.clear());
                      widget.onSelectionChanged(0);
                    },
                  ),
                ),
            ],
          ),
          SliverPadding(
            padding: const EdgeInsets.fromLTRB(16, 0, 16, 120),
            sliver: SliverGrid.builder(
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                mainAxisSpacing: 12,
                crossAxisSpacing: 12,
                childAspectRatio: 0.8,
              ),
              itemCount: 20,
              itemBuilder: (context, index) {
                final isSelected = _selectedItems.contains(index);
                return GestureDetector(
                  onLongPress: () => _toggleSelection(index),
                  onTap: _selectedItems.isNotEmpty
                      ? () => _toggleSelection(index)
                      : null,
                  child: AnimatedContainer(
                    duration: const Duration(milliseconds: 200),
                    decoration: BoxDecoration(
                      color:
                          Theme.of(context).colorScheme.surfaceContainerHighest,
                      borderRadius: BorderRadius.circular(16),
                      border: Border.all(
                        color: isSelected ? widget.color : Colors.transparent,
                        width: 3,
                      ),
                    ),
                    child: Stack(
                      children: [
                        Center(
                          child: Column(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              Icon(
                                widget.icon,
                                size: 48,
                                color: widget.color.withValues(alpha: 0.5),
                              ),
                              const SizedBox(height: 8),
                              Text(
                                'Item ${index + 1}',
                                style: Theme.of(context).textTheme.titleMedium,
                              ),
                              const SizedBox(height: 4),
                              Text(
                                'Long press to select',
                                style: Theme.of(context)
                                    .textTheme
                                    .bodySmall
                                    ?.copyWith(
                                      color: Theme.of(context)
                                          .colorScheme
                                          .onSurfaceVariant,
                                    ),
                              ),
                            ],
                          ),
                        ),
                        if (isSelected)
                          Positioned(
                            top: 8,
                            right: 8,
                            child: Container(
                              padding: const EdgeInsets.all(4),
                              decoration: BoxDecoration(
                                color: widget.color,
                                shape: BoxShape.circle,
                              ),
                              child: const Icon(
                                Icons.check,
                                size: 16,
                                color: Colors.white,
                              ),
                            ),
                          ),
                      ],
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}
11
likes
0
points
68
downloads

Publisher

verified publisherkamalayman.is-a.dev

Weekly Downloads

A beautiful, Apple-style hydro glass floating navigation bar with advanced physics-based animations, draggable indicator, and expandable FAB support for Flutter.

Repository (GitHub)
View/report issues

Topics

#nav-bar #hydro-glass #navigation #widget #animation

Documentation

Documentation

Funding

Consider supporting this project:

buymeacoffee.com

License

unknown (license)

Dependencies

flutter, liquid_glass_renderer, motor

More

Packages that depend on hydro_glass_nav_bar