fluid_side_menu 1.1.0
fluid_side_menu: ^1.1.0 copied to clipboard
A premium, highly-customizable fluid side navigation menu with organic liquid-reveal transitions, staggered option entrance animations, and rich select feedback behaviors.
Fluid Side Menu #
A premium, highly-customizable fluid side navigation drawer for Flutter. Features an organic, gooey liquid-reveal transition using high-performance custom vector splines, staggered entrance animations, nested dropdown navigation, tactile haptic feedback, edge swipe gestures, and rich selection feedback behaviors.

Table of Contents #
Why Fluid Side Menu? #
While standard side drawers transition rigidly across the screen, Fluid Side Menu uses organic motion curves and wavy vector splines to deliver a fluid, high-fidelity navigational experience.
Key benefits include:
- No Edge Pixelation: Custom waves are drawn dynamically as sharp vector paths, avoiding the pixelation or fuzzy edges common with raster masks.
- Optimized Performance: Leverages isolated
RepaintBoundarynodes and linear animation inputs to run smoothly at 60fps / 120fps even on lower-end devices. - Nested Navigation: Supports arbitrarily deep dropdown item trees with smooth animated expand/collapse transitions.
- Custom Easing Curves: The fluid wave transition supports custom animation curves (springy elastic waves, snappy deceleration, or bounce reveals).
- Scroll-Safe: The menu item list automatically scrolls when content overflows the screen — even with many expanded nested items.
Features #
- Organic Liquid Transition: High-performance transition using custom vector wave splines that merge and expand across the screen.
- Nested Dropdown Navigation: Items can declare child items (
subItems) to create collapsible, animated dropdown groups of arbitrary depth. - Per-Item Sizing: Override text style and icon size on individual
FluidMenuIteminstances independent of global defaults. - Scrollable Item List: When the total item list overflows the screen, the menu scrolls automatically. Fully configurable via
ScrollPhysicsandScrollController. - Staggered Option Animations: Smooth, delayed entrance animations for menu items (fade, scale, or springy slide-up).
- Rich Selection Feedback: A collection of interactive tap animations including the Icon Slide Swap effect.
- Tactile Haptic Feedback: Haptics on open, close, and item selection with de-duplicated triggers.
- Edge Swipe Gestures: Pull-to-open from the left edge, swipe-to-close when the drawer is open.
- Item Alignment: Align all menu items to the left, center, or right of the drawer.
- Item-Level Customization: Override colors, text styles, spacings, and individual item sizes independently.
- Programmatic Control:
open(),close(),toggle()viaFluidSideMenu.of(context)or aGlobalKey.
Getting started #
Add fluid_side_menu to your pubspec.yaml dependencies:
dependencies:
fluid_side_menu: ^1.1.0
Then import the package in your Dart code:
import 'package:fluid_side_menu/fluid_side_menu.dart';
Usage #
Standard Setup #
import 'package:flutter/material.dart';
import 'package:fluid_side_menu/fluid_side_menu.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fluid Side Menu Demo',
theme: ThemeData(useMaterial3: true),
home: const DemoScreen(),
);
}
}
class DemoScreen extends StatefulWidget {
const DemoScreen({super.key});
@override
State<DemoScreen> createState() => _DemoScreenState();
}
class _DemoScreenState extends State<DemoScreen> {
@override
Widget build(BuildContext context) {
final List<FluidMenuItem> items = [
FluidMenuItem(
label: 'Home',
page: const HomeScreen(),
icon: const Icon(Icons.home),
),
FluidMenuItem(
label: 'About',
page: const AboutScreen(),
icon: const Icon(Icons.info),
),
FluidMenuItem(
label: 'Contact',
page: const ContactScreen(),
icon: const Icon(Icons.mail),
// Item-level color overrides
textColor: Colors.orangeAccent,
iconColor: Colors.orangeAccent,
),
];
return Scaffold(
body: FluidSideMenu(
fluidColor: Colors.black,
duration: const Duration(milliseconds: 700),
showBuiltInButtons: true,
menuAnimationType: FluidMenuAnimationType.slide,
selectAnimationType: FluidMenuSelectAnimationType.iconSlideSwap,
menuItems: items,
),
);
}
}
Nested Dropdown Items #
Give any FluidMenuItem a subItems list to turn it into a collapsible dropdown group. Child items can themselves have subItems for arbitrary nesting depth. Parent items that only act as group headers do not need a page.
FluidMenuItem(
label: 'Categories',
icon: const Icon(Icons.category),
// No page — this item is a dropdown header only
subItems: [
FluidMenuItem(
label: 'Baskets',
icon: const Icon(Icons.shopping_basket),
subItems: [
FluidMenuItem(
label: 'Woven Baskets',
page: const BasketsScreen(),
icon: const Icon(Icons.shopping_bag),
),
FluidMenuItem(
label: 'Plastic Baskets',
page: const BasketsScreen(),
icon: const Icon(Icons.shopping_basket),
),
],
),
FluidMenuItem(
label: 'Gifts',
page: const GiftsScreen(),
icon: const Icon(Icons.card_giftcard),
),
],
),
Control the text and icon size for all child items via widget-level parameters:
FluidSideMenu(
menuItems: items,
subMenuItemTextStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
subMenuItemIconSize: 18.0,
// ... other parameters
)
Per-Item Sizing #
Individual items can declare their own textStyle and iconSize, which take the highest priority — overriding the widget-level subMenuItemTextStyle/subMenuItemIconSize and the automatic depth-scaling factor.
FluidMenuItem(
label: 'Featured Item',
page: const FeaturedScreen(),
icon: const Icon(Icons.star),
textStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
letterSpacing: 0.3,
),
iconSize: 22.0,
),
To disable automatic depth-based scaling entirely and rely only on explicit sizes, set scaleChildItemsBasedOnDepth: false on the FluidSideMenu widget.
Programmatic Control #
Open, close, or toggle the drawer from any descendant widget using the static accessor:
// Open the side menu
FluidSideMenu.of(context)?.open();
// Close the side menu
FluidSideMenu.of(context)?.close();
// Toggle the side menu
FluidSideMenu.of(context)?.toggle();
Alternatively assign a GlobalKey<FluidSideMenuState> and call key.currentState?.open().
Customizing the Reveal Background #
Pass a LinearGradient or RadialGradient to create a rich gradient appearance for the gooey wave:
FluidSideMenu(
menuItems: items,
fluidGradient: const LinearGradient(
colors: [
Color(0xFF0F0C20), // Dark indigo-black
Color(0xFF15102A), // Dark violet
Color(0xFF06040A), // Deep black
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
)
Scrollable Menu #
By default (enableScroll: true) the menu item list is wrapped in a SingleChildScrollView, allowing users to scroll down to reach items that overflow the screen height — especially useful when many dropdown groups are expanded simultaneously.
FluidSideMenu(
menuItems: items,
enableScroll: true, // default — can omit
scrollPhysics: const BouncingScrollPhysics(), // custom physics
menuItemPadding: const EdgeInsets.symmetric(vertical: 9.0),
subMenuItemPadding: const EdgeInsets.only(top: 10.0),
)
To disable scrolling entirely and keep a fixed layout, set enableScroll: false.
API Reference #
FluidSideMenu Options #
| Parameter | Type | Default | Description |
|---|---|---|---|
menuItems |
List<FluidMenuItem> |
Required | Navigation labels, icons, pages, and optional nested children. |
child |
Widget? |
null |
Static main-screen override instead of routing through menu item pages. |
contentBuilder |
Widget Function(BuildContext, Animation<double>)? |
null |
Fully custom content builder receiving the animation progress. |
fluidColor |
Color |
Colors.black |
Background color of the reveal wave drawer. |
fluidGradient |
Gradient? |
null |
Gradient override for the reveal wave background (supersedes fluidColor). |
duration |
Duration |
650 ms |
Length of the open and close wave transitions. |
animationCurve |
Curve |
Curves.easeInOutCubic |
Easing curve applied to the fluid wave transition. |
showBuiltInButtons |
bool |
true |
Auto-renders the top-left open button and top-right close toggle. |
menuIcon |
Widget? |
null |
Custom widget for the open toggle button. |
closeIcon |
Widget? |
null |
Custom widget for the close toggle button. |
buttonRadius |
double |
20.0 |
Corner radius of the circular open toggle button. |
menuAnimationType |
FluidMenuAnimationType |
slide |
Entrance animation type for menu items (fade, scale, slide). |
selectAnimationType |
FluidMenuSelectAnimationType |
scalePulse |
Tap selection feedback style. See Selection Feedback Animations. |
menuItemTextStyle |
TextStyle? |
null |
Default text style for top-level menu item labels. |
menuItemTextColor |
Color? |
null |
Default label color fallback for all items (if not set per-item). |
menuItemIconColor |
Color? |
null |
Default icon color fallback for all items (if not set per-item). |
menuItemSpacing |
double |
12.0 |
Horizontal spacing between item icon and label. |
menuItemPadding |
EdgeInsets? |
symmetric(vertical: 12) |
Vertical padding surrounding each top-level item row. |
subMenuItemTextStyle |
TextStyle? |
null |
Widget-level text style applied to all nested child items. |
subMenuItemIconSize |
double? |
null |
Widget-level icon size applied to all nested child items. |
subMenuItemPadding |
EdgeInsets? |
only(top: 12) |
Vertical padding above each nested child item row. |
scaleChildItemsBasedOnDepth |
bool |
true |
Whether child items are automatically scaled down per nesting level. |
enableScroll |
bool |
true |
Wraps item list in a SingleChildScrollView when true. |
scrollPhysics |
ScrollPhysics? |
ClampingScrollPhysics |
Scroll physics for the item list. |
scrollController |
ScrollController? |
null |
External scroll controller for programmatic position control. |
enableSwipeGestures |
bool |
true |
Whether swipe gestures can open or close the drawer. |
edgeDragWidth |
double |
30.0 |
Width of the left-edge drag zone when the drawer is closed. |
revealOrigin |
Offset? |
null |
Custom origin point for the gooey reveal wave (defaults to menu button position). |
enableHapticFeedback |
bool |
true |
Triggers haptic feedback at open, close, and item selection events. |
itemAlignment |
FluidMenuItemAlignment |
center |
Horizontal alignment of menu items within the drawer (left, center, right). |
onItemTapped |
ValueChanged<int>? |
null |
Callback triggered with the top-level item index when any item is selected. |
onSubItemTapped |
void Function(int, int)? |
null |
Callback triggered with the parent index and child index when a nested item is selected. |
menuHeader |
Widget? |
null |
Optional widget rendered at the top of the menu, above all items. |
menuFooter |
Widget? |
null |
Optional widget rendered at the bottom of the menu, below all items. |
FluidMenuItem Options #
| Parameter | Type | Default | Description |
|---|---|---|---|
label |
String |
Required | Label text displayed for the menu option. |
page |
Widget? |
null |
Target screen widget shown when this item is selected. Required for leaf (non-parent) items. |
icon |
Widget? |
null |
Prefix icon or widget displayed to the left of the label. |
textColor |
Color? |
null |
Per-item label color override. |
iconColor |
Color? |
null |
Per-item icon color override. |
textStyle |
TextStyle? |
null |
Per-item text style override. Takes precedence over all widget-level styles and depth scaling. |
iconSize |
double? |
null |
Per-item icon size override. Takes precedence over all widget-level sizes and depth scaling. |
subItems |
List<FluidMenuItem>? |
null |
Nested child items. When provided, tapping the item expands or collapses a dropdown instead of navigating. |
onTap |
VoidCallback? |
null |
Custom callback fired when this specific item is tapped, before any built-in expand or navigation logic. |
Size resolution priority (highest to lowest):
FluidMenuItem.textStyle/FluidMenuItem.iconSize(per-item)FluidSideMenu.subMenuItemTextStyle/FluidSideMenu.subMenuItemIconSize(widget-level)- Automatic depth scaling (controlled by
scaleChildItemsBasedOnDepth)
Selection Feedback Animations #
| Value | Behavior |
|---|---|
iconSlideSwap |
Label fades and collapses; icon slides to the horizontal center. Other items dim to 0.25 opacity. |
scalePulse |
Selected item scales up to 1.08. Other items dim to 0.35 opacity. |
slideRight |
Selected item slides right. Other items dim to 0.35 opacity. |
scaleDownOthers |
Selected item stays stable. Other items scale down to 0.9 and dim. |
fadeOthers |
Selected item stays stable. Other items dim to 0.45 opacity. |
none |
Immediate navigation without any feedback animation. |
Item Alignment #
| Value | Behavior |
|---|---|
FluidMenuItemAlignment.left |
Items are left-aligned with indentation increasing per nesting depth. |
FluidMenuItemAlignment.center |
Items are centered in the drawer (default). |
FluidMenuItemAlignment.right |
Items are right-aligned with indentation increasing per nesting depth. |
Additional information #
Source Code and Contributions #
The source code and examples are hosted on GitHub. Contributions, bug reports, and feature requests are welcome via issues and pull requests.
Reporting Issues #
Please use the repository's GitHub Issues page to report bugs, request documentation updates, or propose new design features.
License #
This project is licensed under the BSD 3-Clause License — see the LICENSE file for details.