my_animated_widgets 0.0.1
my_animated_widgets: ^0.0.1 copied to clipboard
A collection of beautiful animated Flutter widgets with dark/light mode support.
example/lib/main.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:my_animated_widgets/my_animated_widgets.dart';
import 'package:my_animated_widgets/src/liquid_progress_bar.dart';
import 'package:my_animated_widgets/src/sky_theme_toggle.dart';
void main() {
runApp(const MyApp());
}
// ─────────────────────────────────────────────
// APP ROOT
// ─────────────────────────────────────────────
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isDark = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Animated Widgets Showcase',
debugShowCheckedModeBanner: false,
theme: AppTheme.light(),
darkTheme: AppTheme.dark(),
themeMode: _isDark ? ThemeMode.dark : ThemeMode.light,
home: ShowcasePage(
isDark: _isDark,
onThemeToggle: (val) => setState(() => _isDark = val),
),
);
}
}
// ─────────────────────────────────────────────
// THEME
// ─────────────────────────────────────────────
class AppTheme {
static const _teal = Color(0xFF01696F);
static const _darkBg = Color(0xFF171614);
static const _darkSurface = Color(0xFF1C1B19);
static ThemeData light() => ThemeData(
useMaterial3: true,
brightness: Brightness.light,
colorSchemeSeed: _teal,
scaffoldBackgroundColor: const Color(0xFFF7F6F2),
cardColor: Colors.white,
);
static ThemeData dark() => ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorSchemeSeed: _teal,
scaffoldBackgroundColor: _darkBg,
cardColor: _darkSurface,
);
}
// ─────────────────────────────────────────────
// SHOWCASE PAGE
// ─────────────────────────────────────────────
// ✅ StatelessWidget — الـ state كله في MyApp
class ShowcasePage extends StatelessWidget {
final bool isDark;
final ValueChanged<bool> onThemeToggle;
const ShowcasePage({
super.key,
required this.isDark,
required this.onThemeToggle,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
'✨ Animated Widgets',
style: TextStyle(fontWeight: FontWeight.bold),
),
centerTitle: false,
actions: [
Padding(
padding: const EdgeInsets.only(right: 16),
// ✅ DarkModeToggle من الـ package
child: DarkModeToggle(
isDark: isDark,
onChanged: onThemeToggle,
),
),
],
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
SizedBox(height: 600, child: _Page()),
Stack(
children: [
Container(
decoration: const BoxDecoration(
gradient: RadialGradient(
center: Alignment(0, 0.3),
radius: 1.2,
colors: [Color(0xFF1A1A2E), Color(0xFF0D0D14)],
),
),
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'🌌 Orbital Action Menu',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 0.5,
),
),
const SizedBox(height: 8),
const Text(
'اضغط على الزرار',
style: TextStyle(color: Colors.white38, fontSize: 14),
),
const SizedBox(height: 80),
// ★ THE WIDGET ★
OrbitalActionMenu(
icon: Icons.auto_awesome,
color: const Color(0xFF7C4DFF),
actions: [
OrbitalAction(
icon: Icons.receipt_long,
label: 'Invoice',
color: const Color(0xFF00BCD4),
onTap: () {},
),
OrbitalAction(
icon: Icons.person_add,
label: 'Client',
color: const Color(0xFF69F0AE),
onTap: () {},
),
OrbitalAction(
icon: Icons.qr_code_scanner,
label: 'Scan',
color: const Color(0xFFFFD740),
onTap: () {},
),
OrbitalAction(
icon: Icons.bar_chart,
label: 'Reports',
color: const Color(0xFFFF6D9F),
onTap: () {},
),
],
),
],
),
),
],
),
// ── 1. Sky Toggle ──────────────────────────
const _SectionTitle('🌗 1. Sky Theme Toggle'),
_DemoCard(
child: Center(
child: SkyThemeToggle(
isDark: isDark,
// ✅ بيستدعي onThemeToggle مش بيعدل widget.isDark مباشرة
onChanged: onThemeToggle,
width: 220,
),
),
),
const SizedBox(height: 24),
// ── 2. Counter ────────────────────────────
const _SectionTitle('🔢 2. Animated Number Counter'),
const _AnimatedCounterDemo(),
const SizedBox(height: 24),
// ── 3. Progress ───────────────────────────
const _SectionTitle('⏳ 3. Liquid Progress Bar'),
const _LiquidProgressDemo(),
const SizedBox(height: 24),
// ── 4. Shimmer ────────────────────────────
const _SectionTitle('✨ 4. Shimmer Skeleton Loader'),
const _ShimmerDemo(),
const SizedBox(height: 24),
// ── 5. FAB ────────────────────────────────
const _SectionTitle('🃏 5. Morphing FAB Button'),
const _MorphingFabDemo(),
const SizedBox(height: 24),
// ── 6. Bottom Nav ─────────────────────────
const _SectionTitle('🌊 6. Animated Bottom Nav Preview'),
const _BottomNavDemo(),
const SizedBox(height: 24),
// ── 7. Staggered List ─────────────────────
const _SectionTitle('📋 7. Staggered List Animation'),
const _StaggeredListDemo(),
const SizedBox(height: 24),
// ── 8. Glass Card ─────────────────────────
const _SectionTitle('💳 8. Glassmorphism Card'),
const _GlassCardDemo(),
const SizedBox(height: 40),
],
),
);
}
}
// ─────────────────────────────────────────────
// SECTION TITLE
// ─────────────────────────────────────────────
class _SectionTitle extends StatelessWidget {
final String text;
const _SectionTitle(this.text);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Text(
text,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
letterSpacing: 0.3,
),
),
);
}
}
// ═══════════════════════════════════════════════════════
// WIDGET 2 — Animated Counter
// ═══════════════════════════════════════════════════════
class AnimatedCounter extends StatelessWidget {
final double value;
final String prefix;
final String suffix;
final TextStyle? style;
const AnimatedCounter({
super.key,
required this.value,
this.prefix = '',
this.suffix = '',
this.style,
});
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: value),
duration: const Duration(milliseconds: 800),
curve: Curves.easeOutCubic,
builder: (context, v, _) {
final display = v >= 1000
? v.toStringAsFixed(0).replaceAllMapped(RegExp(r'(\d)(?=(\d{3})+$)'), (m) => '${m[1]},')
: v.toStringAsFixed(2);
return Text(
'$prefix$display$suffix',
style: style ??
Theme.of(context).textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
fontFeatures: const [FontFeature.tabularFigures()],
color: const Color(0xFF01696F),
),
);
},
);
}
}
class _AnimatedCounterDemo extends StatefulWidget {
const _AnimatedCounterDemo();
@override
State<_AnimatedCounterDemo> createState() => _AnimatedCounterDemoState();
}
class _AnimatedCounterDemoState extends State<_AnimatedCounterDemo> {
double _amount = 1250.75;
final _rng = Random();
void _randomize() => setState(() => _amount = (_rng.nextDouble() * 50000 + 100));
@override
Widget build(BuildContext context) {
return _DemoCard(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Total Sales', style: Theme.of(context).textTheme.bodySmall),
AnimatedCounter(value: _amount, suffix: ' EGP'),
],
),
FilledButton.icon(
onPressed: _randomize,
icon: const Icon(Icons.refresh, size: 16),
label: const Text('Update'),
),
],
),
);
}
}
class _Page extends StatelessWidget {
const _Page();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'⚡ P L A S M A',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 8,
),
),
const SizedBox(height: 6),
const Text(
'المس الكرة',
style: TextStyle(color: Colors.white30, fontSize: 13, letterSpacing: 2),
),
const SizedBox(height: 56),
// ★ THE WIDGET ★
const PlasmaGlobeWidget(
radius: 155,
primaryColor: Color(0xFF8B5CF6),
secondaryColor: Color(0xFF06B6D4),
),
const SizedBox(height: 48),
const Text(
't o u c h m e',
style: TextStyle(
color: Color(0xFF8B5CF6),
fontSize: 11,
letterSpacing: 5,
),
),
],
),
),
);
}
}
// ═══════════════════════════════════════════════════════
// WIDGET 3 — Liquid Progress Bar Demo
// ═══════════════════════════════════════════════════════
class _LiquidProgressDemo extends StatefulWidget {
const _LiquidProgressDemo();
@override
State<_LiquidProgressDemo> createState() => _LiquidProgressDemoState();
}
class _LiquidProgressDemoState extends State<_LiquidProgressDemo> {
double _progress = 0.45;
@override
Widget build(BuildContext context) {
return _DemoCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Monthly Target'),
const SizedBox(height: 12),
LiquidProgressBar(progress: _progress),
const SizedBox(height: 12),
LiquidProgressBar(progress: _progress * 0.6, color: const Color(0xFF964219)),
const SizedBox(height: 16),
Slider(
value: _progress,
onChanged: (v) => setState(() => _progress = v),
activeColor: const Color(0xFF01696F),
),
],
),
);
}
}
// ═══════════════════════════════════════════════════════
// WIDGET 4 — Shimmer Skeleton
// ═══════════════════════════════════════════════════════
class ShimmerBox extends StatefulWidget {
final double width;
final double height;
final double borderRadius;
const ShimmerBox({
super.key,
required this.width,
required this.height,
this.borderRadius = 8,
});
@override
State<ShimmerBox> createState() => _ShimmerBoxState();
}
class _ShimmerBoxState extends State<ShimmerBox> with SingleTickerProviderStateMixin {
late AnimationController _ctrl;
late Animation<double> _anim;
@override
void initState() {
super.initState();
_ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 1500))..repeat();
_anim = CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut);
}
@override
void dispose() {
_ctrl.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final baseColor = isDark ? const Color(0xFF2D2C2A) : const Color(0xFFE8E8E8);
final highlightColor = isDark ? const Color(0xFF3D3C3A) : const Color(0xFFF5F5F5);
return AnimatedBuilder(
animation: _anim,
builder: (_, __) => Container(
width: widget.width,
height: widget.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(widget.borderRadius),
gradient: LinearGradient(
begin: Alignment(-1 + _anim.value * 2, 0),
end: Alignment(_anim.value * 2, 0),
colors: [baseColor, highlightColor, baseColor],
),
),
),
);
}
}
class _ShimmerDemo extends StatefulWidget {
const _ShimmerDemo();
@override
State<_ShimmerDemo> createState() => _ShimmerDemoState();
}
class _ShimmerDemoState extends State<_ShimmerDemo> {
bool _loading = true;
@override
Widget build(BuildContext context) {
return _DemoCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
_loading ? 'Loading...' : 'Content Loaded ✓',
style: Theme.of(context).textTheme.bodySmall,
),
Switch(
value: _loading,
onChanged: (v) => setState(() => _loading = v),
activeColor: const Color(0xFF01696F),
),
],
),
const SizedBox(height: 12),
if (_loading) _buildSkeleton() else _buildContent(context),
],
),
);
}
Widget _buildSkeleton() => Column(
children: List.generate(
3,
(i) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
children: [
ShimmerBox(width: 40, height: 40, borderRadius: 20),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ShimmerBox(width: double.infinity, height: 14),
const SizedBox(height: 6),
ShimmerBox(width: 120, height: 10),
],
),
),
],
),
),
),
);
Widget _buildContent(BuildContext context) => Column(
children: [
'#1234 - Gold Ring 18K — 1,200 EGP',
'#1235 - Silver Necklace — 450 EGP',
'#1236 - Diamond Earrings — 8,500 EGP',
]
.map(
(t) => ListTile(
leading: const CircleAvatar(
backgroundColor: Color(0xFF01696F),
child: Icon(Icons.diamond, color: Colors.white, size: 18),
),
title: Text(t, style: Theme.of(context).textTheme.bodySmall),
dense: true,
),
)
.toList(),
);
}
// ═══════════════════════════════════════════════════════
// WIDGET 5 — Morphing FAB
// ═══════════════════════════════════════════════════════
class _MorphingFabDemo extends StatefulWidget {
const _MorphingFabDemo();
@override
State<_MorphingFabDemo> createState() => _MorphingFabDemoState();
}
class _MorphingFabDemoState extends State<_MorphingFabDemo> with SingleTickerProviderStateMixin {
bool _expanded = false;
late AnimationController _ctrl;
late Animation<double> _fade;
@override
void initState() {
super.initState();
_ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 400));
_fade = CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut);
}
@override
void dispose() {
_ctrl.dispose();
super.dispose();
}
void _toggle() {
setState(() => _expanded = !_expanded);
_expanded ? _ctrl.forward() : _ctrl.reverse();
}
@override
Widget build(BuildContext context) {
return _DemoCard(
child: Center(
child: GestureDetector(
onTap: _toggle,
child: AnimatedContainer(
duration: const Duration(milliseconds: 500),
curve: Curves.fastLinearToSlowEaseIn,
width: _expanded ? 260 : 60,
height: _expanded ? 160 : 60,
decoration: BoxDecoration(
color: const Color(0xFF01696F),
borderRadius: BorderRadius.circular(_expanded ? 20 : 30),
boxShadow: const [
BoxShadow(color: Color(0x5001696F), blurRadius: 16, spreadRadius: 2),
],
),
child: _expanded
? FadeTransition(
opacity: _fade,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('New Invoice',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16)),
const SizedBox(height: 8),
Row(
children: [
_FabAction(icon: Icons.person_add, label: 'Client'),
const SizedBox(width: 8),
_FabAction(icon: Icons.add_box, label: 'Item'),
const SizedBox(width: 8),
_FabAction(icon: Icons.close, label: 'Close', onTap: _toggle),
],
),
],
),
),
)
: const Icon(Icons.add, color: Colors.white, size: 28),
),
),
),
);
}
}
class _FabAction extends StatelessWidget {
final IconData icon;
final String label;
final VoidCallback? onTap;
const _FabAction({required this.icon, required this.label, this.onTap});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white24,
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: Colors.white, size: 20),
),
const SizedBox(height: 4),
Text(label, style: const TextStyle(color: Colors.white, fontSize: 10)),
],
),
);
}
}
// ═══════════════════════════════════════════════════════
// WIDGET 6 — Animated Bottom Nav
// ═══════════════════════════════════════════════════════
class _BottomNavDemo extends StatefulWidget {
const _BottomNavDemo();
@override
State<_BottomNavDemo> createState() => _BottomNavDemoState();
}
class _BottomNavDemoState extends State<_BottomNavDemo> {
int _selected = 0;
final _items = const [
(Icons.home_rounded, 'Home'),
(Icons.receipt_long, 'Invoice'),
(Icons.bar_chart, 'Reports'),
(Icons.person, 'Profile'),
];
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final bg = isDark ? const Color(0xFF1C1B19) : Colors.white;
const active = Color(0xFF01696F);
return _DemoCard(
child: Container(
height: 72,
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(color: Colors.black.withValues(alpha:0.08), blurRadius: 20, offset: const Offset(0, -4)),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(_items.length, (i) {
final isActive = i == _selected;
return GestureDetector(
onTap: () => setState(() => _selected = i),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutBack,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: isActive ? active.withValues(alpha:0.12) : Colors.transparent,
borderRadius: BorderRadius.circular(16),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedScale(
scale: isActive ? 1.2 : 1.0,
duration: const Duration(milliseconds: 300),
curve: Curves.elasticOut,
child: Icon(
_items[i].$1,
color: isActive ? active : Colors.grey,
size: 24,
),
),
const SizedBox(height: 2),
AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 200),
style: TextStyle(
fontSize: 10,
fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
color: isActive ? active : Colors.grey,
),
child: Text(_items[i].$2),
),
],
),
),
);
}),
),
),
);
}
}
// ═══════════════════════════════════════════════════════
// WIDGET 7 — Staggered List
// ═══════════════════════════════════════════════════════
class _StaggeredListDemo extends StatefulWidget {
const _StaggeredListDemo();
@override
State<_StaggeredListDemo> createState() => _StaggeredListDemoState();
}
class _StaggeredListDemoState extends State<_StaggeredListDemo> with TickerProviderStateMixin {
final _items = [
('Gold Ring 18K', '1,200 EGP', Icons.circle, Colors.amber),
('Silver Necklace', '450 EGP', Icons.circle_outlined, Colors.blueGrey),
('Diamond Earrings', '8,500 EGP', Icons.diamond, Colors.lightBlue),
('Platinum Bracelet', '3,200 EGP', Icons.watch, Colors.grey),
];
late List<AnimationController> _controllers;
late List<Animation<double>> _anims;
@override
void initState() {
super.initState();
_controllers = List.generate(
_items.length,
(i) => AnimationController(vsync: this, duration: const Duration(milliseconds: 400)),
);
_anims = _controllers.map((c) => CurvedAnimation(parent: c, curve: Curves.easeOutBack)).toList();
_animate();
}
void _animate() async {
for (var i = 0; i < _controllers.length; i++) {
await Future.delayed(const Duration(milliseconds: 80));
if (mounted) _controllers[i].forward(from: 0);
}
}
@override
void dispose() {
for (var c in _controllers) {
c.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return _DemoCard(
child: Column(
children: [
...List.generate(_items.length, (i) {
final item = _items[i];
return FadeTransition(
opacity: _anims[i],
child: SlideTransition(
position: Tween<Offset>(begin: const Offset(0, 0.3), end: Offset.zero).animate(_anims[i]),
child: ListTile(
dense: true,
leading: CircleAvatar(
backgroundColor: item.$4.withValues(alpha:0.15),
child: Icon(item.$3, color: item.$4, size: 18),
),
title: Text(item.$1),
trailing:
Text(item.$2, style: const TextStyle(color: Color(0xFF01696F), fontWeight: FontWeight.bold)),
),
),
);
}),
const SizedBox(height: 8),
TextButton.icon(
onPressed: _animate,
icon: const Icon(Icons.replay),
label: const Text('Replay Animation'),
),
],
),
);
}
}
// ═══════════════════════════════════════════════════════
// WIDGET 8 — Glassmorphism Card
// ═══════════════════════════════════════════════════════
class _GlassCardDemo extends StatefulWidget {
const _GlassCardDemo();
@override
State<_GlassCardDemo> createState() => _GlassCardDemoState();
}
class _GlassCardDemoState extends State<_GlassCardDemo> {
bool _pressed = false;
@override
Widget build(BuildContext context) {
return _DemoCard(
child: Container(
height: 160,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFF01696F), Color(0xFF0c4e54)],
),
),
child: Stack(
children: [
Positioned(
right: -20,
top: -20,
child: Container(
width: 120,
height: 120,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white.withValues(alpha:0.08),
),
),
),
Positioned(
left: -10,
bottom: -30,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white.withValues(alpha:0.06),
),
),
),
GestureDetector(
onTapDown: (_) => setState(() => _pressed = true),
onTapUp: (_) => setState(() => _pressed = false),
onTapCancel: () => setState(() => _pressed = false),
child: AnimatedScale(
scale: _pressed ? 0.97 : 1.0,
duration: const Duration(milliseconds: 120),
child: Container(
margin: const EdgeInsets.all(12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white.withValues(alpha:0.12),
border: Border.all(color: Colors.white.withValues(alpha:0.25)),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('💎 Gold Jewelry POS',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
Icon(Icons.more_horiz, color: Colors.white70),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Today\'s Revenue', style: TextStyle(color: Colors.white70, fontSize: 12)),
Text('24,500.00 EGP',
style: TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold)),
],
),
],
),
),
),
),
],
),
),
);
}
}
// ═══════════════════════════════════════════════════════
// SHARED — Demo Card Wrapper
// ═══════════════════════════════════════════════════════
class _DemoCard extends StatelessWidget {
final Widget child;
const _DemoCard({required this.child});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha:Theme.of(context).brightness == Brightness.dark ? 0.25 : 0.06),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: child,
);
}
}