MotionX
A high-performance, fluid, and customizable navigation bar framework for Flutter featuring unified indicator physics, smooth Bezier notch cutouts, and flexible badge architecture.
Overview
MotionX is designed to modernize bottom navigation in Flutter applications. Standard navigation components often suffer from rigid layouts, limited indicator shapes, or abrupt state transitions. MotionX addresses these limitations with a single frame-synchronized rendering architecture that computes floating indicator translation and background notch cutout geometry continuously from a single offset driver (animationOffset).
Key Architectural Pillars:
- Modern Design Language: Built ground-up to support elevated floating circles, background capsules, top pill bars, bottom underline strips, and custom geometry.
- Continuous Frame Synchronization: Synchronizes floating indicators and background notch cutouts frame-by-frame during user drag gestures and programmatic animations.
- Granular Customization: Complete control over shapes, proportional scaling, depth, Bezier shoulder radii, haptics, and padding.
- Flexible Badge Engine: Positioning system supporting preset anchors, pixel-precise offsets, follow-motion toggles, and customizable container/content metrics.
- Decoupled Controller Architecture: First-class integration with
PageControllerandTabControllerfor synchronizedPageViewdragging without layout recalculations. - Production Accessibility: Complete Right-to-Left (RTL) localization support, screen-reader semantics tags, and Material 3 compliance.
Preview
Features
xFloating Indicators: Elevated circular FAB, background capsule card, top accent pill, and bottom underline strip.xProportionalindicatorSizeScaling: Automatically scales widths, heights, and thickness across all built-in indicator shapes.x2D Bezier Notch Geometry: Smooth Bezier cutouts (SmoothNotchShape) and classic circular arc cutouts (ClassicNotchShape) with customizable cradle depth.xPersistent Docked Cutout: Background notch cutout remains permanently visible framing the active indicator at rest, during dragging, and after settling.xFlexible Badge Positioning: Position badges using preset alignment anchors (topRight,topLeft,bottomRight,bottomLeft,topCenter) or customOffset(dx, dy)vectors.xDecoupled Badge Opacity: Badges remain 100% visible on selected and unselected tabs with optionalfollowIndicatormotion lift.xPageView&TabControllerSync: Single continuous animation value binding for gesture dragging without position jumps.xNavigation Guards (onTabWillChange): Asynchronous interceptors for tab switching validation (e.g. form confirmation modals).xRight-to-Left (RTL) Support: Automatic directionality inversion for internationalized applications.xAccessible Semantics: Accessible button semantics, selected state flags, and custom semantic labels.
Installation
Add motionx to your pubspec.yaml dependencies:
dependencies:
motionx: ^0.1.0
Then run:
flutter pub get
Quick Start
import 'package:flutter/material.dart';
import 'package:motionx/motionx.dart';
class QuickStartExample extends StatefulWidget {
const QuickStartExample({super.key});
@override
State<QuickStartExample> createState() => _QuickStartExampleState();
}
class _QuickStartExampleState extends State<QuickStartExample> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Active Tab Index: $_currentIndex'),
),
bottomNavigationBar: MotionXNavigationBar(
index: _currentIndex,
onTabSelected: (index) => setState(() => _currentIndex = index),
items: const [
MotionXItem(label: 'Explore', icon: Icon(Icons.explore_outlined)),
MotionXItem(label: 'Messages', icon: Icon(Icons.chat_bubble_outline)),
MotionXItem(label: 'Settings', icon: Icon(Icons.settings_outlined)),
],
),
);
}
}
Basic Usage
A complete, production-ready implementation showcasing custom colors, active icons, badges, and smooth notch cutout styling:
import 'package:flutter/material.dart';
import 'package:motionx/motionx.dart';
class BasicUsageExample extends StatefulWidget {
const BasicUsageExample({super.key});
@override
State<BasicUsageExample> createState() => _BasicUsageExampleState();
}
class _BasicUsageExampleState extends State<BasicUsageExample> {
int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: MotionXNavigationBar(
index: _selectedIndex,
onTabSelected: (index) => setState(() => _selectedIndex = index),
height: 65.0,
enableHaptics: true,
useSafeArea: true,
animationDuration: const Duration(milliseconds: 350),
animationCurve: Curves.easeOutCubic,
indicatorStyle: const MotionXIndicatorStyle(
shape: MotionXIndicatorShape.floatingCircle,
size: 56.0,
color: Color(0xFF6366F1),
shadows: [
BoxShadow(
color: Color(0x406366F1),
blurRadius: 12.0,
offset: Offset(0, 4),
),
],
),
notchStyle: const MotionXNotchStyle(
enabled: true,
depth: 0.5,
cornerRadius: 14.0,
shape: SmoothNotchShape(cornerRadius: 14.0),
),
items: const [
MotionXItem(
label: 'Explore',
icon: Icon(Icons.explore_outlined),
activeIcon: Icon(Icons.explore),
),
MotionXItem(
label: 'Messages',
icon: Icon(Icons.chat_bubble_outline),
activeIcon: Icon(Icons.chat_bubble),
badge: MotionXBadge(
text: '8',
badgeSize: 20.0,
position: MotionXBadgePosition.topRight,
),
),
MotionXItem(
label: 'Updates',
icon: Icon(Icons.notifications_outlined),
activeIcon: Icon(Icons.notifications),
badge: MotionXBadge.dot(
color: Colors.amber,
size: 10.0,
),
),
MotionXItem(
label: 'Settings',
icon: Icon(Icons.settings_outlined),
activeIcon: Icon(Icons.settings),
),
],
),
);
}
}
PageView Synchronization
Connect MotionXNavigationBar to a PageController using MotionXController. The floating indicator and background notch follow swipe gestures in real time without lag or layout recalculations.
import 'package:flutter/material.dart';
import 'package:motionx/motionx.dart';
class PageViewSyncExample extends StatefulWidget {
const PageViewSyncExample({super.key});
@override
State<PageViewSyncExample> createState() => _PageViewSyncExampleState();
}
class _PageViewSyncExampleState extends State<PageViewSyncExample> {
late final MotionXController _navController;
late final PageController _pageController;
@override
void initState() {
super.initState();
_navController = MotionXController.create(initialIndex: 0);
_pageController = PageController(initialPage: 0);
_navController.attachPageController(_pageController);
}
@override
void dispose() {
_pageController.dispose();
_navController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController,
children: const [
Center(child: Text('Explore Page')),
Center(child: Text('Messages Page')),
Center(child: Text('Settings Page')),
],
),
bottomNavigationBar: MotionXNavigationBar(
controller: _navController,
items: const [
MotionXItem(label: 'Explore', icon: Icon(Icons.explore)),
MotionXItem(label: 'Messages', icon: Icon(Icons.chat_bubble)),
MotionXItem(label: 'Settings', icon: Icon(Icons.settings)),
],
),
);
}
}
Badge Examples
MotionX includes a flexible badge engine supporting numeric text, dot indicators, custom alignment anchors, pixel nudges, and sizing parameters.
1. Dot Badge
MotionXBadge.dot(
color: Colors.red,
size: 10.0,
position: MotionXBadgePosition.topRight,
)
2. Number Badge
MotionXBadge(
text: '12',
backgroundColor: const Color(0xFFFF3B30),
textColor: Colors.white,
badgeSize: 20.0,
contentSize: 11.0,
)
3. Text Badge with Outline Border
MotionXBadge(
text: 'NEW',
backgroundColor: const Color(0xFF10B981),
badgeSize: 22.0,
contentSize: 10.0,
padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 2.0),
border: Border.all(color: Colors.white, width: 1.5),
)
4. Custom Position & Offset
MotionXBadge(
text: '5',
position: MotionXBadgePosition(
alignment: Alignment.topRight,
offset: Offset(8.0, -2.0),
),
followIndicator: true, // Badge translates with icon lift animation
)
Indicator Styles
MotionX provides 4 built-in indicator shapes and supports custom vector paths via customPainter:
1. Floating Circle (Default)
Elevated circular indicator designed to sit above a docked 2D background notch cutout.
MotionXIndicatorStyle(
shape: MotionXIndicatorShape.floatingCircle,
size: 56.0,
color: Color(0xFF6366F1),
)
2. Capsule
Stadium highlight card centered behind active tab content inside the navigation bar container.
MotionXIndicatorStyle(
shape: MotionXIndicatorShape.capsule,
size: 56.0,
color: Color(0xFF10B981),
)
3. Pill
Compact floating bar positioned at the top edge of the navigation bar container.
MotionXIndicatorStyle(
shape: MotionXIndicatorShape.pill,
size: 56.0,
color: Color(0xFFEC4899),
)
4. Underline
Sleek active strip positioned at the bottom edge of the navigation bar container.
MotionXIndicatorStyle(
shape: MotionXIndicatorShape.underline,
size: 56.0,
color: Color(0xFFF59E0B),
)
5. Custom Vector Painter
Supply a custom CustomPainter to render arbitrary indicator geometry (e.g., diamonds, custom polygons).
MotionXIndicatorStyle(
shape: MotionXIndicatorShape.custom,
size: 56.0,
color: Colors.purple,
customPainter: MyCustomDiamondPainter(),
)
Notch Styles
The background notch path is driven by strategy implementations of MotionXNotchShape.
Smooth Bezier Notch (SmoothNotchShape)
Renders continuous Bezier curves for organic shoulder transitions:
MotionXNotchStyle(
enabled: true,
depth: 0.5,
cornerRadius: 14.0,
shape: SmoothNotchShape(cornerRadius: 14.0),
)
Classic Circular Arc Notch (ClassicNotchShape)
Renders classic circular arc geometry:
MotionXNotchStyle(
enabled: true,
depth: 0.5,
shape: ClassicNotchShape(),
)
Customization Reference
| Configuration Area | Parameter | Description |
|---|---|---|
| Bar Container | height |
Overall navigation bar height (defaults to 65.0). |
backgroundColor |
Background bar color (defaults to Theme.of(context).cardColor). |
|
useSafeArea |
Integrates SafeArea for devices with home indicator bars. |
|
| Indicator Style | shape |
floatingCircle, capsule, pill, underline, custom. |
size |
Base indicator size scaling width, height, and thickness proportionally. | |
color |
Active indicator fill color. | |
shadows |
List of BoxShadow objects for elevation rendering. |
|
| Notch Style | enabled |
Enables or disables background notch path geometry. |
depth |
Cradle depth ratio ($0.0 \to 1.0$). | |
shape |
SmoothNotchShape or ClassicNotchShape. |
|
| Interactions | enableHaptics |
Enables tactile feedback on tab selection. |
onTabWillChange |
Asynchronous interceptor returning bool to approve or cancel tab transitions. |
Interactive Example App
The MotionX repository includes a full-featured Showcase Studio application in example/lib/main.dart.
The showcase app allows developers to interactively tweak indicator shapes, sizes, notch depths, Bezier radii, haptics, RTL directionality, and badge metrics in real time while generating ready-to-use Dart code snippets.
To run the showcase app locally:
cd example
flutter run
Accessibility & Localization
- Screen-Reader Semantics: Every tab slot is wrapped in a
Semanticswidget marked withbutton: true, selected flags, and configurablesemanticLabelvalues. - Material 3 Palette: Fully compatible with Flutter
ColorSchemedynamic light and dark themes. - RTL Support: Calculates effective layout offsets based on active
Directionality, reversing gesture and animation vectors seamlessly for Right-to-Left locales.
Rendering Performance Architecture
MotionX is designed for high performance:
- Single Continuous Animation Value: Both floating indicators and background notch cutouts derive 2D coordinates from a single
animationOffsetvalue inMotionXController. - Canvas-Level Painting: Background paths and notch cutouts are painted at the canvas level inside
CustomPainterwithout instantiating extra layout widgets. - No Layout Recalculation: Indicator motion during
PageViewdrags interpolates coordinates mathematically without triggering layout passes.
Roadmap
Planned features for future releases:
AGSL / SkSL Shaders: Liquid metal and chromatic aberration shader effects for floating indicators.Vertical Navigation Rail: Adaptive layout adapter for desktop and tablet form factors.Badge Entry Animations: Scale and bounce entry physics for dynamic badge updates.
Contributing
Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests on GitHub.
- Fork the Repository
- Create a Feature Branch (
git checkout -b feature/amazing-feature) - Commit Changes (
git commit -m 'Add amazing feature') - Push to Branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Libraries
- motionx
- MotionX - A high-performance, fluid, and extensible navigation bar framework for Flutter.