flutter_motion_kit 1.0.0
flutter_motion_kit: ^1.0.0 copied to clipboard
Centralized high-performance Flutter animation and loader library. Premium loaders, glassmorphism, 3D tilt cards, transitions, and shaders in one package.
example/lib/main.dart
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
// Import our package exports
import 'package:flutter_motion_kit/flutter_motion_kit.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Inject and initialize central GetX controllers
await MotionConfigService.init();
runApp(const MotionShowcaseApp());
}
class MotionShowcaseApp extends StatelessWidget {
const MotionShowcaseApp({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<MotionThemeController>();
return Obx(() {
return MaterialApp(
title: 'MotionKit Pro Showcase',
debugShowCheckedModeBanner: false,
theme: themeController.theme.toThemeData(),
home: const ShowcaseDashboard(),
);
});
}
}
class ShowcaseDashboard extends StatefulWidget {
const ShowcaseDashboard({super.key});
@override
State<ShowcaseDashboard> createState() => _ShowcaseDashboardState();
}
class _ShowcaseDashboardState extends State<ShowcaseDashboard> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
int _selectedTab = 0;
int _activeBgType = 1; // 0: None, 1: Particles, 2: Aurora
// Loader Customizations
MotionLoaderType _selectedLoaderType = MotionLoaderType.ai;
Color _loaderColor = MotionColors.primaryNeon;
double _loaderSize = 60.0;
// Morph widget configuration
int _morphStep = 0;
// Fake system monitoring
double _fps = 60.0;
late Timer _fpsTimer;
@override
void initState() {
super.initState();
// Simulate real-time rendering statistics
_fpsTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) {
if (mounted) {
setState(() {
_fps = 58.0 + (math.Random().nextDouble() * 2.0);
});
}
});
}
@override
void dispose() {
_fpsTimer.cancel();
super.dispose();
}
// Helper to generate the preview code snippet
String _generateLoaderCode() {
final colorStr = _loaderColor == MotionColors.primaryNeon
? 'MotionColors.primaryNeon'
: _loaderColor == Colors.amber
? 'Colors.amber'
: _loaderColor == Colors.redAccent
? 'Colors.redAccent'
: _loaderColor == Colors.cyanAccent
? 'Colors.cyanAccent'
: 'Colors.white';
return '''
MotionLoader(
type: MotionLoaderType.${_selectedLoaderType.name},
color: $colorStr,
size: ${_loaderSize.round()}.0,
)
''';
}
@override
Widget build(BuildContext context) {
final activeTheme = MotionTheme.of(context);
final motion = Get.find<MotionController>();
final themeController = Get.find<MotionThemeController>();
final screenWidth = MediaQuery.of(context).size.width;
final isMobile = screenWidth < 900;
final isTablet = screenWidth >= 900 && screenWidth < 1250;
Widget backgroundWidget;
if (_activeBgType == 1) {
backgroundWidget = const MotionParticleBackground();
} else if (_activeBgType == 2) {
backgroundWidget = const MotionAuroraBackground();
} else {
backgroundWidget = Container(color: activeTheme.backgroundColor);
}
return Scaffold(
key: _scaffoldKey,
// On narrow viewports, sidebar and customize panels slide out as Drawers
drawer: isMobile
? Drawer(
backgroundColor: activeTheme.backgroundColor,
child: _buildSidebar(activeTheme, themeController),
)
: null,
endDrawer: (isMobile || isTablet)
? Drawer(
backgroundColor: activeTheme.backgroundColor,
child: _buildControlPanel(activeTheme, motion, themeController),
)
: null,
body: Stack(
children: [
// Dynamic particle or aurora active backgrounds
Positioned.fill(child: backgroundWidget),
// Main horizontal page layout
SafeArea(
child: Row(
children: [
// 1. Sidebar Navigation (Static on Desktop, Drawer on Mobile)
if (!isMobile) _buildSidebar(activeTheme, themeController),
// 2. Main content panels
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Responsive Dashboard Header
_buildHeader(activeTheme, motion, isMobile, isTablet),
const SizedBox(height: 16),
// Active Tab Screen wrapped in glass container
Expanded(
child: MotionGlassContainer(
borderRadius: 24,
blur: 16,
opacity: activeTheme.isDark ? 0.06 : 0.4,
padding: const EdgeInsets.all(16.0),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 250),
child: _buildSelectedTabScreen(activeTheme,
motion, themeController, screenWidth),
),
),
),
],
),
),
),
// 3. Right Customization Sidebar (Static on wide Desktop, Drawer on Tablet/Mobile)
if (!isMobile && !isTablet)
_buildControlPanel(activeTheme, motion, themeController),
],
),
),
],
),
);
}
// Sidebar Layout (Fully scrollable & overflow immune)
Widget _buildSidebar(
MotionThemeData activeTheme, MotionThemeController themeController) {
final isDark = activeTheme.isDark;
return Container(
width: 250,
decoration: BoxDecoration(
color: activeTheme.surfaceColor.withValues(alpha: isDark ? 0.4 : 0.8),
border: Border(
right: BorderSide(
color: activeTheme.primaryColor.withValues(alpha: 0.12),
width: 1,
),
),
),
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Cyberpunk Logo
_buildLogo(activeTheme),
const SizedBox(height: 24),
// Menu navigation options (Vertically scrollable)
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSidebarItem(0, 'Dashboard Home',
Icons.space_dashboard_rounded, activeTheme),
_buildSidebarItem(1, 'Loaders Playground',
Icons.donut_large_rounded, activeTheme),
_buildSidebarItem(2, 'Buttons & Clicks',
Icons.ads_click_rounded, activeTheme),
_buildSidebarItem(
3, 'Cards & Skeletons', Icons.style_rounded, activeTheme),
_buildSidebarItem(4, 'Transitions & Morphs',
Icons.transform_rounded, activeTheme),
],
),
),
),
const SizedBox(height: 16),
// Active Background Selector (Locked at the bottom)
_buildBgSelector(activeTheme),
],
),
);
}
Widget _buildLogo(MotionThemeData activeTheme) {
return Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
gradient: activeTheme.accentGradient,
shape: BoxShape.circle,
),
child: const Icon(Icons.blur_on, color: Colors.black, size: 22),
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'MOTIONKIT',
style: TextStyle(
fontFamily: 'monospace',
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
color: activeTheme.primaryColor,
shadows: [
if (activeTheme.glowEffect)
Shadow(
color: activeTheme.primaryColor.withValues(alpha: 0.8),
blurRadius: 10,
)
]),
),
const Text(
'Ecosystem v1.0',
style: TextStyle(
fontSize: 9, color: Colors.grey, letterSpacing: 1.0),
),
],
),
],
);
}
Widget _buildBgSelector(MotionThemeData activeTheme) {
return Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'BACKGROUND SHADER',
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.grey,
letterSpacing: 0.5),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildBgButton(0, 'Void', activeTheme),
_buildBgButton(1, 'Particles', activeTheme),
_buildBgButton(2, 'Aurora', activeTheme),
],
),
],
),
);
}
Widget _buildSidebarItem(
int index, String title, IconData icon, MotionThemeData activeTheme) {
final selected = _selectedTab == index;
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: InkWell(
onTap: () {
setState(() => _selectedTab = index);
// Close drawer on tap on mobile
if (_scaffoldKey.currentState?.isDrawerOpen ?? false) {
Navigator.pop(context);
}
},
borderRadius: BorderRadius.circular(12),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: selected ? activeTheme.accentGradient : null,
color: selected ? null : Colors.transparent,
),
child: Row(
children: [
Icon(
icon,
color: selected ? Colors.black : Colors.grey,
size: 18,
),
const SizedBox(width: 12),
Text(
title,
style: TextStyle(
fontWeight: selected ? FontWeight.bold : FontWeight.w500,
color: selected ? Colors.black : Colors.grey[400],
fontSize: 13,
),
),
],
),
),
),
);
}
Widget _buildBgButton(int type, String label, MotionThemeData activeTheme) {
final active = _activeBgType == type;
return InkWell(
onTap: () => setState(() => _activeBgType = type),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 5),
decoration: BoxDecoration(
color: active ? activeTheme.primaryColor : Colors.transparent,
borderRadius: BorderRadius.circular(8),
),
child: Text(
label,
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: active ? Colors.black : Colors.white70,
),
),
),
);
}
// Dashboard Header with Responsive drawer buttons
Widget _buildHeader(MotionThemeData activeTheme, MotionController motion,
bool isMobile, bool isTablet) {
final double screenWidth = MediaQuery.of(context).size.width;
final bool isVerySmall = screenWidth < 385;
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Hamburger menu button on mobile
if (isMobile)
Padding(
padding: const EdgeInsets.only(right: 6.0),
child: IconButton(
icon: const Icon(Icons.menu, color: Colors.white, size: 22),
onPressed: () => _scaffoldKey.currentState?.openDrawer(),
),
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
_selectedTab == 0
? 'Core Monitoring'
: _selectedTab == 1
? 'Loaders Lab'
: _selectedTab == 2
? 'Micro-Interactions'
: _selectedTab == 3
? '3D Tilt & Skeletons'
: 'Routes & Morphs',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (!isMobile)
Text(
'Real-time simulation engine running at 60fps',
style: TextStyle(fontSize: 10, color: Colors.grey[400]),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
const SizedBox(width: 8),
// Live stats panel & Customize Drawer trigger
Row(
mainAxisSize: MainAxisSize.min,
children: [
if (!isVerySmall) ...[
_buildStatChip('FPS', '${_fps.toStringAsFixed(1)} Hz',
Colors.green, isMobile),
const SizedBox(width: 6),
],
_buildStatChip(
'SPEED',
'${motion.speedMultiplier.toStringAsFixed(1)}x',
motion.speedMultiplier != 1.0 ? Colors.amber : Colors.blue,
isMobile,
),
// Customize trigger button for Tablet/Mobile
if (isMobile || isTablet)
Padding(
padding: const EdgeInsets.only(left: 6.0),
child: IconButton(
icon: const Icon(Icons.tune_rounded,
color: Colors.white, size: 22),
onPressed: () => _scaffoldKey.currentState?.openEndDrawer(),
),
),
],
),
],
);
}
Widget _buildStatChip(
String label, String value, Color color, bool isMobile) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: color.withValues(alpha: 0.3)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 6,
height: 6,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
),
const SizedBox(width: 6),
if (!isMobile)
Text(
'$label: ',
style: const TextStyle(
fontSize: 9, color: Colors.grey, fontWeight: FontWeight.bold),
),
Text(
value,
style: const TextStyle(
fontSize: 10,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'monospace'),
),
],
),
);
}
// Selected Screen Generator (Responsive layout routing)
Widget _buildSelectedTabScreen(
MotionThemeData activeTheme,
MotionController motion,
MotionThemeController themeController,
double width) {
switch (_selectedTab) {
case 0:
return _buildDashboardHome(activeTheme, motion, width);
case 1:
return _buildLoadersPlayground(activeTheme, width);
case 2:
return _buildButtonsPlayground(activeTheme, width);
case 3:
return _buildCardsPlayground(activeTheme, width);
case 4:
return _buildTransitionsPlayground(activeTheme, width);
default:
return const SizedBox.shrink();
}
}
// Tab 0: Home Overview
Widget _buildDashboardHome(MotionThemeData activeTheme,
MotionController motion, double screenWidth) {
final useColumnLayout = screenWidth < 900;
return ListView(
physics: const BouncingScrollPhysics(),
children: [
// Intro Showcase Box
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
activeTheme.primaryColor.withValues(alpha: 0.15),
activeTheme.secondaryColor.withValues(alpha: 0.05),
],
),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: activeTheme.primaryColor.withValues(alpha: 0.2)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'CENTRALIZED FLUTTER MOTION ECOSYSTEM',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
color: activeTheme.primaryColor,
),
),
const SizedBox(height: 10),
const Text(
'One Single Package. Zero Performance Overhead.',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
'This package acts as a centralized animation platform. Combining micro-interactions, responsive tap engines, fluid page transitions, glassmorphic filters, and AI computing animations under one consistent, accessibility-aware state management layer driven exclusively by GetX.',
style: TextStyle(
fontSize: 12, color: Colors.grey[300], height: 1.5),
),
],
),
),
const SizedBox(height: 16),
// Quick feature stats grids (Responsive wrapping to prevent overflows)
GridView.count(
crossAxisCount: useColumnLayout ? 1 : 3,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
shrinkWrap: true,
childAspectRatio: useColumnLayout ? 2.5 : 1.6,
physics: const NeverScrollableScrollPhysics(),
children: [
_buildFeatureStatsCard(
'11 beautiful custom loaders',
'Ready to deploy including futuristic AI scan and custom falling code matrix.',
Icons.donut_large,
activeTheme),
_buildFeatureStatsCard(
'Accessibility Compliant',
'Dynamic listeners support "Reduce Motion" system triggers, mapping heavy items instantly.',
Icons.accessibility_new,
activeTheme),
_buildFeatureStatsCard(
'3D Canvas Tilt & Glass',
'Physical models transforming on 3D perspective grids during pointer updates.',
Icons.filter_hdr_outlined,
activeTheme),
],
),
const SizedBox(height: 16),
// Micro features check lists
if (useColumnLayout) ...[
_buildEcosystemDirectivesBox(),
const SizedBox(height: 16),
_buildAccessibilityGuidelinesBox(),
] else
Row(
children: [
Expanded(child: _buildEcosystemDirectivesBox()),
const SizedBox(width: 16),
Expanded(child: _buildAccessibilityGuidelinesBox()),
],
)
],
);
}
Widget _buildEcosystemDirectivesBox() {
return MotionGlassContainer(
borderRadius: 16,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Ecosystem Directives',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 12),
_buildCheckRow('GetX Central Controllers',
'Inject theme, toggles, performance mode, and speeds globally.'),
_buildCheckRow('Repaint Boundaries',
'Constrained nodes avoiding full tree calculations.'),
_buildCheckRow('No Placeholder Policy',
'Complete live painters driving the canvas directly.'),
],
),
);
}
Widget _buildAccessibilityGuidelinesBox() {
return MotionGlassContainer(
borderRadius: 16,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Accessibility Guidelines',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 12),
_buildCheckRow('Scale Fallback',
'Ticking speeds fall to zero scaling for vestibular conditions.'),
_buildCheckRow('Low Memory Shader scaling',
'Disables blurs for low-spec performance configurations.'),
_buildCheckRow('Flexible curves',
'Easily tweak elastic bounce behaviors in real time.'),
],
),
);
}
Widget _buildFeatureStatsCard(
String title, String desc, IconData icon, MotionThemeData activeTheme) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.white),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Icon(icon, color: activeTheme.primaryColor, size: 20),
],
),
const SizedBox(height: 6),
Text(
desc,
style:
TextStyle(fontSize: 10, color: Colors.grey[400], height: 1.3),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
],
),
);
}
Widget _buildCheckRow(String label, String desc) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(Icons.check_circle_outline, color: Colors.cyan, size: 16),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.white)),
Text(desc,
style: const TextStyle(fontSize: 10, color: Colors.grey),
maxLines: 1,
overflow: TextOverflow.ellipsis),
],
),
)
],
),
);
}
// Tab 1: Loaders Playground (Fully responsive, overflow-free)
Widget _buildLoadersPlayground(
MotionThemeData activeTheme, double screenWidth) {
final useVerticalLayout = screenWidth < 950;
if (useVerticalLayout) {
return SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
// Grid selection wrapper with explicit height
const Text(
'Select Animation Preset',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(height: 12),
SizedBox(
height: 300,
child: _buildLoadersGrid(activeTheme),
),
const SizedBox(height: 20),
// Live config and codes
const Text(
'Live Renderer & Configurator',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(height: 12),
SizedBox(
height: 460,
child: _buildLoadersConfig(activeTheme),
),
],
),
);
}
return Row(
children: [
// 1. Grid of loaders selection
Expanded(
flex: 4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Select Animation Preset',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(height: 12),
Expanded(
child: _buildLoadersGrid(activeTheme),
),
],
),
),
const SizedBox(width: 20),
// 2. Active Preview & live configs
Expanded(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Live Renderer & Configurator',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(height: 12),
Expanded(
child: _buildLoadersConfig(activeTheme),
),
],
),
)
],
);
}
Widget _buildLoadersGrid(MotionThemeData activeTheme) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 1.3,
),
physics: const BouncingScrollPhysics(),
itemCount: MotionLoaderType.values.length,
itemBuilder: (context, index) {
final type = MotionLoaderType.values[index];
final active = _selectedLoaderType == type;
return InkWell(
onTap: () => setState(() => _selectedLoaderType = type),
borderRadius: BorderRadius.circular(16),
child: Container(
decoration: BoxDecoration(
color: active
? activeTheme.primaryColor.withValues(alpha: 0.08)
: Colors.black.withValues(alpha: 0.18),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: active ? activeTheme.primaryColor : Colors.white10,
width: active ? 1.5 : 1,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 32,
height: 32,
child: Center(
child: MotionLoader(
type: type,
color:
active ? activeTheme.primaryColor : Colors.grey[500],
size: 28,
),
),
),
const SizedBox(height: 8),
Text(
type.name.toUpperCase(),
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: active ? activeTheme.primaryColor : Colors.white70,
letterSpacing: 0.5,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
);
},
);
}
Widget _buildLoadersConfig(MotionThemeData activeTheme) {
return Column(
children: [
// Live Preview Canvas
Expanded(
flex: 5,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.white10),
),
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Center(
child: MotionLoader(
type: _selectedLoaderType,
color: _loaderColor,
size: _loaderSize,
),
),
),
const SizedBox(height: 10),
// Config controls with wrapped layout to prevent overflows
Row(
children: [
const Text('Size: ',
style: TextStyle(fontSize: 10, color: Colors.grey)),
Expanded(
child: Slider(
value: _loaderSize,
min: 30,
max: 100,
activeColor: activeTheme.primaryColor,
onChanged: (val) => setState(() => _loaderSize = val),
),
),
Text('${_loaderSize.round()}px',
style: const TextStyle(
fontSize: 10,
color: Colors.white,
fontFamily: 'monospace')),
],
),
const SizedBox(height: 8),
Wrap(
spacing: 12,
runSpacing: 8,
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
const Text('Color: ',
style: TextStyle(fontSize: 10, color: Colors.grey)),
_buildColorDot(MotionColors.primaryNeon),
_buildColorDot(Colors.amber),
_buildColorDot(Colors.redAccent),
_buildColorDot(Colors.cyanAccent),
_buildColorDot(Colors.white),
],
),
],
),
),
),
const SizedBox(height: 12),
// Monospace Code output panel
Expanded(
flex: 4,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.black45,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white10),
),
padding: const EdgeInsets.all(12),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'INTEGRATION CODE',
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.cyan,
letterSpacing: 0.5),
),
MotionMorphingButton(
width: 70,
height: 24,
borderRadius: 6,
color: Colors.white12,
onTap: () async {
await Clipboard.setData(
ClipboardData(text: _generateLoaderCode()));
await Future.delayed(const Duration(
milliseconds: 600)); // mock copy delay
},
child: const Text('COPY',
style: TextStyle(
fontSize: 9,
color: Colors.white,
fontWeight: FontWeight.bold)),
)
],
),
const SizedBox(height: 8),
Text(
_generateLoaderCode(),
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 10,
color: Colors.lightGreenAccent,
),
),
],
),
),
),
)
],
);
}
Widget _buildColorDot(Color col) {
final active = _loaderColor == col;
return InkWell(
onTap: () => setState(() => _loaderColor = col),
child: Container(
width: 22,
height: 22,
decoration: BoxDecoration(
color: col,
shape: BoxShape.circle,
border: Border.all(
color: active ? Colors.white : Colors.transparent,
width: 2,
),
),
),
);
}
// Tab 2: Buttons & Clicks (Responsive layout with Wrap or stacks to avoid overflows)
Widget _buildButtonsPlayground(
MotionThemeData activeTheme, double screenWidth) {
final useVerticalLayout = screenWidth < 900;
final demo1 = _buildClickDemoBox(
'Scale On Tap Effect',
'Pills compress scale down by 7% gracefully on pointer contacts, resuming elastic bounds on release.',
MotionButton(
effect: MotionButtonEffect.scale,
onTap: () {},
child: const Text('Scale Trigger',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 12)),
),
activeTheme,
);
final demo2 = _buildClickDemoBox(
'Neon Glow Blur Shadow',
'Surrounds border bounds with glowing blurred radius, fading away if accessibility reduces motion.',
MotionButton(
effect: MotionButtonEffect.glow,
onTap: () {},
child: const Text('Glow Trigger',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 12)),
),
activeTheme,
);
final demo3 = _buildClickDemoBox(
'Custom Expanding Ripple',
'Intercepts local pointer events, drawing expanding high-contrast fills outwards from the exact click origin.',
MotionButton(
effect: MotionButtonEffect.ripple,
onTap: () {},
child: const Text('Ripple Trigger',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 12)),
),
activeTheme,
);
final demo4 = _buildClickDemoBox(
'Pointer Magnetic Pulling',
'Calculates finger displacement vector against center point, translating the matrix physically toward pointer.',
MotionButton(
effect: MotionButtonEffect.magnetic,
onTap: () {},
child: const Text('Magnetic Pull',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 12)),
),
activeTheme,
);
final morphingAndMicro = useVerticalLayout
? Column(
children: [
_buildMorphingButtonSimulationBox(activeTheme),
const SizedBox(height: 16),
_buildMicroInteractionsBox(),
],
)
: Row(
children: [
Expanded(child: _buildMorphingButtonSimulationBox(activeTheme)),
const SizedBox(width: 16),
Expanded(child: _buildMicroInteractionsBox()),
],
);
return ListView(
physics: const BouncingScrollPhysics(),
children: [
const Text(
'Dynamic Responsive Clicks',
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.bold, color: Colors.white),
),
const SizedBox(height: 12),
if (useVerticalLayout) ...[
demo1,
const SizedBox(height: 12),
demo2,
const SizedBox(height: 12),
demo3,
const SizedBox(height: 12),
demo4,
] else ...[
Row(
children: [
Expanded(child: demo1),
const SizedBox(width: 12),
Expanded(child: demo2),
],
),
const SizedBox(height: 16),
Row(
children: [
Expanded(child: demo3),
const SizedBox(width: 12),
Expanded(child: demo4),
],
),
],
const SizedBox(height: 20),
// Morphing and Micro-interactions
morphingAndMicro,
],
);
}
Widget _buildMorphingButtonSimulationBox(MotionThemeData activeTheme) {
return MotionGlassContainer(
borderRadius: 16,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('State-Morphing Button (API Simulator)',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 6),
const Text(
'Tapping initiates simulated async operations, morphing layout into loaders, checkmarks or failure crosses.',
style: TextStyle(fontSize: 10, color: Colors.grey)),
const SizedBox(height: 20),
Wrap(
spacing: 16,
runSpacing: 12,
alignment: WrapAlignment.center,
children: [
MotionMorphingButton(
width: 130,
height: 40,
borderRadius: 10,
onTap: () async {
await Future.delayed(const Duration(seconds: 2));
},
child: const Text('Success Morph',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: Colors.black)),
),
MotionMorphingButton(
width: 130,
height: 40,
borderRadius: 10,
color: Colors.deepPurple,
onTap: () async {
await Future.delayed(const Duration(seconds: 2));
throw Exception("Mock API Error");
},
child: const Text('Error Morph',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: Colors.white)),
),
],
),
],
),
);
}
Widget _buildMicroInteractionsBox() {
return MotionGlassContainer(
borderRadius: 16,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Interactive Micro-Interactions',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 12),
Wrap(
spacing: 16,
runSpacing: 16,
alignment: WrapAlignment.spaceEvenly,
children: [
_buildMicroDot('Heart Burst', const MotionLikeButton(size: 28)),
_buildMicroDot('Checkmark',
StatefulBuilder(builder: (context, setState) {
bool checked = false;
return InkWell(
onTap: () => setState(() => checked = !checked),
child: MotionAnimatedCheckmark(checked: checked, size: 28),
);
})),
_buildMicroDot('Bookmark', const MotionBookmarkButton(size: 28)),
_buildMicroDot('Liquid Switch',
StatefulBuilder(builder: (context, setState) {
bool toggled = false;
return MotionLiquidToggle(
initialValue: toggled,
onChanged: (val) => toggled = val,
);
})),
],
),
],
),
);
}
Widget _buildMicroDot(String label, Widget child) {
return SizedBox(
width: 70,
child: Column(
children: [
child,
const SizedBox(height: 6),
Text(label,
style: const TextStyle(fontSize: 9, color: Colors.grey),
maxLines: 1,
overflow: TextOverflow.ellipsis),
],
),
);
}
Widget _buildClickDemoBox(
String title, String desc, Widget btn, MotionThemeData activeTheme) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 6),
Text(desc,
style: TextStyle(
fontSize: 10, color: Colors.grey[400], height: 1.3)),
const SizedBox(height: 16),
Center(child: btn),
],
),
);
}
// Tab 3: Cards & Skeletons (Responsive wrapper to prevent overflows)
Widget _buildCardsPlayground(
MotionThemeData activeTheme, double screenWidth) {
final useVerticalLayout = screenWidth < 950;
if (useVerticalLayout) {
return SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
_build3DCardSection(activeTheme),
const SizedBox(height: 24),
_buildSkeletonsSection(activeTheme),
],
),
);
}
return Row(
children: [
// 1. 3D Tilt Card preview
Expanded(
flex: 4,
child: _build3DCardSection(activeTheme),
),
const SizedBox(width: 20),
// 2. Shimmer & Skeletons Live presets
Expanded(
flex: 3,
child: _buildSkeletonsSection(activeTheme),
)
],
);
}
Widget _build3DCardSection(MotionThemeData activeTheme) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('3D Perspective Canvas Tilt Card',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 4),
const Text(
'Drag pointer or mouse across the container below to tilt the card on Z-axis coordinates.',
style: TextStyle(fontSize: 10, color: Colors.grey)),
const SizedBox(height: 20),
Center(
child: MotionCard(
maxTiltAngleX: 18,
maxTiltAngleY: 18,
elevation: 10,
borderRadius: 24,
shadowColor: activeTheme.primaryColor.withValues(alpha: 0.25),
child: Container(
width: 300,
height: 180,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
activeTheme.primaryColor.withValues(alpha: 0.7),
activeTheme.secondaryColor.withValues(alpha: 0.7),
],
),
),
padding: const EdgeInsets.all(20),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('MOTIONKIT PLATINUM',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
letterSpacing: 1.5,
color: Colors.black)),
Icon(Icons.nfc, color: Colors.black, size: 20),
],
),
Spacer(),
Text('4000 1284 5691 0032',
style: TextStyle(
fontFamily: 'monospace',
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
letterSpacing: 1.5)),
Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('CARD HOLDER',
style: TextStyle(
fontSize: 8,
color: Colors.black54,
fontWeight: FontWeight.bold)),
Text('ELON MUSK',
style: TextStyle(
fontSize: 11,
color: Colors.black,
fontWeight: FontWeight.bold)),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('EXPIRES',
style: TextStyle(
fontSize: 8,
color: Colors.black54,
fontWeight: FontWeight.bold)),
Text('12/32',
style: TextStyle(
fontSize: 11,
color: Colors.black,
fontWeight: FontWeight.bold)),
],
),
],
),
],
),
),
),
),
],
);
}
Widget _buildSkeletonsSection(MotionThemeData activeTheme) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Unified Shimmer Skeletons',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 12),
Container(
height: 320,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.white10),
),
padding: const EdgeInsets.symmetric(horizontal: 4),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: Text('PROFILE LAYOUT PRESET',
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.grey)),
),
MotionSkeleton.profile(avatarSize: 50.0),
const Divider(color: Colors.white10),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: Text('CHAT INTERACTIVE PRESET',
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.grey)),
),
MotionSkeleton.chat(),
const Divider(color: Colors.white10),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: Text('CARD PREVIEW PRESET',
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.grey)),
),
MotionSkeleton.card(),
const SizedBox(height: 10),
],
),
),
),
],
);
}
// Tab 4: Page Transitions & Morphs (Responsive vertical stacking)
Widget _buildTransitionsPlayground(
MotionThemeData activeTheme, double screenWidth) {
final useVerticalLayout = screenWidth < 950;
if (useVerticalLayout) {
return SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
_buildMorphSection(activeTheme),
const SizedBox(height: 24),
_buildTransitionsSection(activeTheme),
],
),
);
}
return Row(
children: [
// 1. Morph widget
Expanded(
flex: 4,
child: _buildMorphSection(activeTheme),
),
const SizedBox(width: 20),
// 2. Page transition routers list
Expanded(
flex: 3,
child: _buildTransitionsSection(activeTheme),
)
],
);
}
Widget _buildMorphSection(MotionThemeData activeTheme) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Shape & Layout Morphing',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 4),
const Text(
'Transition boundary shapes and internal child widgets dynamically with curves.',
style: TextStyle(fontSize: 10, color: Colors.grey)),
const SizedBox(height: 16),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MotionMorphContainer(
width: _morphStep == 0
? 110.0
: _morphStep == 1
? 260.0
: 220.0,
height: _morphStep == 0
? 110.0
: _morphStep == 1
? 70.0
: 160.0,
duration: const Duration(milliseconds: 550),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
_morphStep == 0
? 55.0
: _morphStep == 1
? 12.0
: 20.0,
),
gradient: LinearGradient(
colors: _morphStep == 0
? [Colors.tealAccent, Colors.cyan]
: _morphStep == 1
? [Colors.pinkAccent, Colors.purple]
: [Colors.amber, Colors.orangeAccent],
),
boxShadow: [
BoxShadow(
color: (_morphStep == 0
? Colors.cyan
: _morphStep == 1
? Colors.pinkAccent
: Colors.amber)
.withValues(alpha: 0.3),
blurRadius: 12,
spreadRadius: 1,
)
],
),
child: _buildMorphChild(),
),
const SizedBox(height: 20),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: activeTheme.primaryColor,
foregroundColor: Colors.black,
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
onPressed: () {
setState(() {
_morphStep = (_morphStep + 1) % 3;
});
},
child: const Text('TRIGGER SHAPE MORPH',
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 11)),
),
],
),
),
],
);
}
Widget _buildTransitionsSection(MotionThemeData activeTheme) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Premium Custom Page Routes',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 12),
Container(
height: 320,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.white10),
),
padding: const EdgeInsets.all(12),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildTransitionRouteBtn('Symmetrical Fade transition', () {
Navigator.push(
context,
MotionTransition.fade(
page: const SecondarySamplePage(title: 'Fade Route')));
}),
_buildTransitionRouteBtn('Swift Slide offset transition', () {
Navigator.push(
context,
MotionTransition.slideRight(
page: const SecondarySamplePage(title: 'Slide Route')));
}),
_buildTransitionRouteBtn('Springy Zoom transition', () {
Navigator.push(
context,
MotionTransition.zoom(
page: const SecondarySamplePage(title: 'Zoom Route')));
}),
_buildTransitionRouteBtn('Cyber Glass Overlay Backdrop blur', () {
Navigator.push(
context,
MotionTransition.glassOverlay(
page: const SecondarySamplePage(
title: 'Glass Blur Overlay', hasScaffold: false)));
}),
_buildTransitionRouteBtn('Shared Axis fading sliding', () {
Navigator.push(
context,
MotionTransition.sharedAxis(
page: const SecondarySamplePage(
title: 'Shared Axis Route'),
vertical: true));
}),
_buildTransitionRouteBtn('Fluid Liquid wave swipe sweep', () {
Navigator.push(
context,
MotionTransition.liquidSwipe(
page: const SecondarySamplePage(
title: 'Liquid Sweep Route')));
}),
],
),
),
],
);
}
Widget _buildMorphChild() {
switch (_morphStep) {
case 0:
return const Center(
key: ValueKey('c0'),
child: Icon(Icons.face_retouching_natural,
size: 40, color: Colors.black87),
);
case 1:
return const Center(
key: ValueKey('c1'),
child: Text(
'SLIDE OVER BANNER',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 11,
color: Colors.white,
fontFamily: 'monospace'),
),
);
case 2:
return const Padding(
key: ValueKey('c2'),
padding: EdgeInsets.all(12.0),
child: FittedBox(
fit: BoxFit.scaleDown,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.credit_card, size: 36, color: Colors.black87),
SizedBox(height: 6),
Text(
'MEMBER ACCESS',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 11,
color: Colors.black87),
),
Text(
'ID: 993-841-A2',
style: TextStyle(
fontSize: 9,
color: Colors.black54,
fontFamily: 'monospace'),
),
],
),
),
);
default:
return const SizedBox.shrink();
}
}
Widget _buildTransitionRouteBtn(String label, VoidCallback routeTrigger) {
return SizedBox(
width: double.infinity,
height: 38,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.white12),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
foregroundColor: Colors.cyan,
padding: const EdgeInsets.symmetric(horizontal: 12),
),
onPressed: routeTrigger,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
label,
style: const TextStyle(
fontSize: 11,
color: Colors.white,
fontWeight: FontWeight.w500),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const Icon(Icons.arrow_forward_ios_rounded,
size: 12, color: Colors.white60),
],
),
),
);
}
// Right Side global animation configuration console (Scrollable vertically, overflow immune)
Widget _buildControlPanel(MotionThemeData activeTheme,
MotionController motion, MotionThemeController themeController) {
return Container(
width: 280,
decoration: BoxDecoration(
color: activeTheme.surfaceColor
.withValues(alpha: activeTheme.isDark ? 0.4 : 0.8),
border: Border(
left: BorderSide(
color: activeTheme.primaryColor.withValues(alpha: 0.12),
width: 1,
),
),
),
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.tune_rounded, color: Colors.amber, size: 18),
SizedBox(width: 8),
Text(
'MOTION CONSOLE',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.white,
letterSpacing: 1.0),
),
],
),
const SizedBox(height: 20),
// Scrollable Settings List
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Speed Multiplier
_buildConsoleSlider(
'ANIMATION SPEED MULTIPLIER',
motion.speedMultiplier,
0.1,
3.0,
(val) => motion.setSpeedMultiplier(val),
'${motion.speedMultiplier.toStringAsFixed(1)}x',
),
const SizedBox(height: 20),
// Accessibility features
const Text(
'ACCESSIBILITY OPTIONS',
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.grey,
letterSpacing: 0.5),
),
const SizedBox(height: 10),
_buildToggleRow(
'Reduce Motion',
'Vestibular crossfade overrides',
motion.reducedMotion,
(val) => motion.setReducedMotion(val),
),
const SizedBox(height: 10),
_buildToggleRow(
'Performance Mode',
'Saves resource consumption',
motion.performanceMode,
(val) => motion.setPerformanceMode(val),
),
const SizedBox(height: 10),
_buildToggleRow(
'Active Glows',
'Glow filters on surfaces',
themeController.glowEffect,
(val) => themeController.setGlowEffect(val),
),
const SizedBox(height: 20),
// Presets Toggles
const Text(
'THEMATIC PRESETS',
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.grey,
letterSpacing: 0.5),
),
const SizedBox(height: 10),
_buildPresetBtn(
'Cyberpunk Pink Neon',
() => themeController.applyCyberpunkPreset(),
activeTheme),
const SizedBox(height: 6),
_buildPresetBtn(
'Gold Amber Midnight',
() => themeController.applyMidnightPresets(),
activeTheme),
const SizedBox(height: 6),
_buildPresetBtn(
'Ultra Violet Purple',
() => themeController.applyNeonPurplePreset(),
activeTheme),
const SizedBox(height: 6),
_buildPresetBtn(
'Clean Standard Light',
() => themeController.setTheme(MotionThemeData.light()),
activeTheme),
const SizedBox(height: 6),
_buildPresetBtn(
'Clean Midnight Dark',
() => themeController.setTheme(MotionThemeData.dark()),
activeTheme),
],
),
),
),
],
),
);
}
Widget _buildConsoleSlider(String label, double value, double min, double max,
ValueChanged<double> onChanged, String valStr) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label,
style: const TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.grey)),
Text(valStr,
style: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white,
fontFamily: 'monospace')),
],
),
const SizedBox(height: 6),
Slider(
value: value,
min: min,
max: max,
onChanged: onChanged,
),
],
);
}
Widget _buildToggleRow(
String label, String sub, bool val, ValueChanged<bool> onChanged) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label,
style: const TextStyle(
fontSize: 11,
color: Colors.white,
fontWeight: FontWeight.bold)),
Text(sub,
style: const TextStyle(fontSize: 8, color: Colors.grey),
maxLines: 1,
overflow: TextOverflow.ellipsis),
],
),
),
Switch(
value: val,
onChanged: onChanged,
),
],
);
}
Widget _buildPresetBtn(
String label, VoidCallback apply, MotionThemeData activeTheme) {
return SizedBox(
width: double.infinity,
height: 32,
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.white.withValues(alpha: 0.04),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
foregroundColor: activeTheme.primaryColor,
padding: const EdgeInsets.symmetric(vertical: 4),
),
onPressed: apply,
child: Text(label,
style: const TextStyle(
fontSize: 10,
color: Colors.white,
fontWeight: FontWeight.w500)),
),
);
}
}
// Sample Secondary Page loaded during route transitions
class SecondarySamplePage extends StatelessWidget {
final String title;
final bool hasScaffold;
const SecondarySamplePage({
super.key,
required this.title,
this.hasScaffold = true,
});
@override
Widget build(BuildContext context) {
final activeTheme = MotionTheme.of(context);
final content = Center(
child: MotionGlassContainer(
width: 280,
height: 240,
borderRadius: 24,
blur: 20,
opacity: 0.15,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: const BoxDecoration(
color: Colors.green, shape: BoxShape.circle),
child: const Icon(Icons.check_rounded,
color: Colors.black, size: 24),
),
const SizedBox(height: 16),
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(height: 6),
const Text(
'Route transitions successfully compiled!',
style: TextStyle(fontSize: 10, color: Colors.grey),
),
const SizedBox(height: 20),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: activeTheme.primaryColor,
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
onPressed: () => Navigator.pop(context),
child: const Text('DISMISS & RETURN',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 11)),
),
],
),
),
);
if (hasScaffold) {
return Scaffold(
backgroundColor: activeTheme.backgroundColor,
body: content,
);
} else {
return Material(
color: Colors.transparent,
child: content,
);
}
}
}