flip_counter_plus 1.0.0 copy "flip_counter_plus: ^1.0.0" to clipboard
flip_counter_plus: ^1.0.0 copied to clipboard

An implicit animation widget that flips from one number to another, with support for customize styles, decimals and negative values.

example/lib/main.dart

import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flip_counter_plus/flip_counter_plus.dart';

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

final ValueNotifier<ThemeMode> themeNotifier = ValueNotifier(ThemeMode.dark);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<ThemeMode>(
      valueListenable: themeNotifier,
      builder: (context, currentMode, _) {
        return MaterialApp(
          title: 'FlipCounter Hub',
          debugShowCheckedModeBanner: false,
          themeMode: currentMode,
          theme: ThemeData.light().copyWith(
            scaffoldBackgroundColor: const Color(0xFFF8FAFC), // Slate 50
            primaryColor: const Color(0xFF4F46E5), // Indigo 600
            colorScheme: const ColorScheme.light(
              primary: Color(0xFF4F46E5),
              secondary: Color(0xFFDB2777), // Pink 600
              surface: Colors.white,
              error: Color(0xFFDC2626),
              onSurface: Color(0xFF0F172A),
              onPrimary: Colors.white,
            ),
            cardTheme: const CardThemeData(
              color: Colors.white,
              elevation: 2,
              margin: EdgeInsets.zero,
            ),
            sliderTheme: const SliderThemeData(
              activeTrackColor: Color(0xFF4F46E5),
              thumbColor: Color(0xFF4F46E5),
            ),
            dividerTheme: const DividerThemeData(
              color: Color(0xFFE2E8F0),
            ),
          ),
          darkTheme: ThemeData.dark().copyWith(
            scaffoldBackgroundColor: const Color(0xFF0B0F19), // Midnight dark background
            primaryColor: const Color(0xFF6366F1), // Indigo
            colorScheme: const ColorScheme.dark(
              primary: Color(0xFF6366F1),
              secondary: Color(0xFFEC4899), // Pink
              surface: Color(0xFF151D30), // Dark slate cards
              error: Color(0xFFEF4444),
              onSurface: Colors.white,
              onPrimary: Colors.white,
            ),
            cardTheme: const CardThemeData(
              color: Color(0xFF151D30),
              elevation: 4,
              margin: EdgeInsets.zero,
            ),
            sliderTheme: const SliderThemeData(
              activeTrackColor: Color(0xFF6366F1),
              thumbColor: Color(0xFF6366F1),
            ),
            dividerTheme: const DividerThemeData(
              color: Color(0xFF1E293B),
            ),
          ),
          home: const DashboardPage(),
        );
      },
    );
  }
}

class DashboardPage extends StatefulWidget {
  const DashboardPage({super.key});

  @override
  State<DashboardPage> createState() => _DashboardPageState();
}

class _DashboardPageState extends State<DashboardPage> {
  int _activeTab = 0;

  final List<Map<String, dynamic>> _tabs = [
    {
      'title': 'Visual Sandbox',
      'icon': Icons.tune_rounded,
      'subtitle': 'Interactive transition & speed controls',
    },
    {
      'title': 'Formats & Localizations',
      'icon': Icons.language_rounded,
      'subtitle': 'Currencies, groupings & regional digits',
    },
    {
      'title': 'Custom Stylings',
      'icon': Icons.palette_rounded,
      'subtitle': 'Retro flip clock & neon builders',
    },
    {
      'title': 'Controller & Lifecycle',
      'icon': Icons.settings_input_component_rounded,
      'subtitle': 'Imperative APIs & status listeners',
    },
    {
      'title': 'Stock Market',
      'icon': Icons.trending_up_rounded,
      'subtitle': 'Real-time quotes & order book simulation',
    },
  ];

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;
    final isWide = width >= 850;

    return Scaffold(
      body: SafeArea(
        child: isWide
            ? Row(
                children: [
                  _buildSidebar(context),
                  VerticalDivider(width: 1, color: Theme.of(context).dividerTheme.color ?? const Color(0xFF1E293B)),
                  Expanded(
                    child: _buildMainContent(),
                  ),
                ],
              )
            : Column(
                children: [
                  _buildHeader(),
                  Expanded(
                    child: _buildMainContent(),
                  ),
                ],
              ),
      ),
      bottomNavigationBar: isWide
          ? null
          : BottomNavigationBar(
              currentIndex: _activeTab,
              onTap: (index) => setState(() => _activeTab = index),
              selectedItemColor: Theme.of(context).colorScheme.primary,
              unselectedItemColor: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
              backgroundColor: Theme.of(context).colorScheme.surface,
              type: BottomNavigationBarType.fixed,
              items: _tabs.map((tab) {
                return BottomNavigationBarItem(
                  icon: Icon(tab['icon']),
                  label: tab['title'],
                );
              }).toList(),
            ),
    );
  }

  Widget _buildHeader() {
    final isDark = Theme.of(context).brightness == Brightness.dark;
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
      color: Theme.of(context).colorScheme.surface,
      child: Row(
        children: [
          Container(
            padding: const EdgeInsets.all(8),
            decoration: BoxDecoration(
              gradient: const LinearGradient(
                colors: [Color(0xFF6366F1), Color(0xFFEC4899)],
              ),
              borderRadius: BorderRadius.circular(12),
            ),
            child: const Icon(Icons.flash_on_rounded, color: Colors.white),
          ),
          const SizedBox(width: 16),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                'FlipCounter Hub',
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.w900,
                  letterSpacing: 0.5,
                  color: Theme.of(context).colorScheme.onSurface,
                ),
              ),
              Text(
                'High Performance Counters',
                style: TextStyle(
                  fontSize: 11,
                  color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                ),
              ),
            ],
          ),
          const Spacer(),
          IconButton(
            icon: Icon(
              isDark ? Icons.light_mode_rounded : Icons.dark_mode_rounded,
              color: Theme.of(context).colorScheme.onSurface,
            ),
            onPressed: () {
              themeNotifier.value = isDark ? ThemeMode.light : ThemeMode.dark;
            },
          ),
        ],
      ),
    );
  }

  Widget _buildSidebar(BuildContext context) {
    final isDark = Theme.of(context).brightness == Brightness.dark;
    return Container(
      width: 260,
      color: Theme.of(context).colorScheme.surface,
      padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Container(
                padding: const EdgeInsets.all(8),
                decoration: BoxDecoration(
                  gradient: const LinearGradient(
                    colors: [Color(0xFF6366F1), Color(0xFFEC4899)],
                  ),
                  borderRadius: BorderRadius.circular(12),
                ),
                child: const Icon(Icons.flash_on_rounded, color: Colors.white, size: 24),
              ),
              const SizedBox(width: 12),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'FlipCounter',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.w900,
                      letterSpacing: 0.5,
                      color: Theme.of(context).colorScheme.onSurface,
                    ),
                  ),
                  Text(
                    'Hub v1.0.0',
                    style: TextStyle(
                      fontSize: 11,
                      color: Theme.of(context).colorScheme.primary,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            ],
          ),
          const SizedBox(height: 32),
          Text(
            'NAVIGATION',
            style: TextStyle(
              fontSize: 10,
              fontWeight: FontWeight.w800,
              color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.4),
              letterSpacing: 1.5,
            ),
          ),
          const SizedBox(height: 12),
          Expanded(
            child: ListView.separated(
              itemCount: _tabs.length,
              separatorBuilder: (_, __) => const SizedBox(height: 8),
              itemBuilder: (context, index) {
                final tab = _tabs[index];
                final isSelected = _activeTab == index;
                return InkWell(
                  onTap: () => setState(() => _activeTab = index),
                  borderRadius: BorderRadius.circular(12),
                  child: Container(
                    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                    decoration: BoxDecoration(
                      color: isSelected
                          ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.15)
                          : Colors.transparent,
                      borderRadius: BorderRadius.circular(12),
                      border: Border.all(
                        color: isSelected
                            ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.3)
                            : Colors.transparent,
                        width: 1,
                      ),
                    ),
                    child: Row(
                      children: [
                        Icon(
                          tab['icon'],
                          color: isSelected ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                          size: 20,
                        ),
                        const SizedBox(width: 16),
                        Expanded(
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                tab['title'],
                                style: TextStyle(
                                  fontSize: 13,
                                  fontWeight: FontWeight.bold,
                                  color: isSelected ? Theme.of(context).colorScheme.onSurface : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                                ),
                              ),
                              const SizedBox(height: 2),
                              Text(
                                tab['subtitle'],
                                style: TextStyle(
                                  fontSize: 9,
                                  color: isSelected
                                      ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.8)
                                      : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.4),
                                  overflow: TextOverflow.ellipsis,
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
                );
              },
            ),
          ),
          Row(
            children: [
              Expanded(
                child: Text(
                  'Theme Mode',
                  style: TextStyle(
                    fontSize: 12,
                    fontWeight: FontWeight.bold,
                    color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
                  ),
                ),
              ),
              IconButton(
                icon: Icon(
                  isDark ? Icons.light_mode_rounded : Icons.dark_mode_rounded,
                  color: Theme.of(context).colorScheme.primary,
                ),
                onPressed: () {
                  themeNotifier.value = isDark ? ThemeMode.light : ThemeMode.dark;
                },
              ),
            ],
          ),
          const SizedBox(height: 12),
          Container(
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              color: Theme.of(context).scaffoldBackgroundColor,
              borderRadius: BorderRadius.circular(12),
            ),
            child: Row(
              children: [
                const Icon(Icons.bolt, color: Colors.amberAccent, size: 16),
                const SizedBox(width: 8),
                Expanded(
                  child: Text(
                    'Render time: < 2.5us\nIsolated via Repaints',
                    style: TextStyle(fontSize: 10, color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6), height: 1.3),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildMainContent() {
    switch (_activeTab) {
      case 0:
        return const VisualSandboxTab();
      case 1:
        return const FormatsAndLocalesTab();
      case 2:
        return const CustomStylingsTab();
      case 3:
        return const ControllerTab();
      case 4:
        return const StockMarketTab();
      default:
        return const SizedBox.shrink();
    }
  }
}

/// ==========================================
/// TAB 1: VISUAL SANDBOX
/// ==========================================
class VisualSandboxTab extends StatefulWidget {
  const VisualSandboxTab({super.key});

  @override
  State<VisualSandboxTab> createState() => _VisualSandboxTabState();
}

class _VisualSandboxTabState extends State<VisualSandboxTab> {
  double _value = 345.67;
  CounterTransitionType _transition = CounterTransitionType.roll;
  double _speed = 1.0;
  double _delayMs = 0.0;
  double _staggerMs = 50.0;
  StaggerDirection _staggerDir = StaggerDirection.rightToLeft;
  AxisDirection _flipDir = AxisDirection.up;
  final TextEditingController _inputController = TextEditingController();

  double _liveHz = 0.0;
  double _liveRenderTimeUs = 1764.0;
  bool _benchmarkRunning = false;
  final List<DateTime> _lastUpdateTimes = [];
  Timer? _hzDecayTimer;

  @override
  void initState() {
    super.initState();
    _hzDecayTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
      if (mounted && _lastUpdateTimes.isNotEmpty) {
        final now = DateTime.now();
        if (now.difference(_lastUpdateTimes.last).inMilliseconds > 1000) {
          setState(() {
            _liveHz = 0.0;
            _lastUpdateTimes.clear();
          });
        }
      }
    });
  }

  @override
  void dispose() {
    _hzDecayTimer?.cancel();
    _inputController.dispose();
    super.dispose();
  }

  void _updateValue(double newValue) {
    setState(() {
      _value = newValue;
      final now = DateTime.now();
      _lastUpdateTimes.add(now);
      _lastUpdateTimes.removeWhere((t) => now.difference(t).inMilliseconds > 1000);
      if (_lastUpdateTimes.length > 1) {
        final duration = now.difference(_lastUpdateTimes.first).inMilliseconds / 1000.0;
        _liveHz = duration > 0 ? (_lastUpdateTimes.length - 1) / duration : 0.0;
      } else {
        _liveHz = 0.0;
      }
    });
  }

  Future<void> _runStressTest() async {
    if (_benchmarkRunning) return;
    setState(() {
      _benchmarkRunning = true;
      _liveHz = 0.0;
    });

    final stopwatch = Stopwatch()..start();
    const int totalTicks = 60;
    final double startVal = _value;

    for (int i = 1; i <= totalTicks; i++) {
      await Future.delayed(const Duration(milliseconds: 16));
      if (!mounted) return;
      _updateValue(startVal + i * 2.5);
    }

    stopwatch.stop();
    setState(() {
      _liveRenderTimeUs = (stopwatch.elapsedMicroseconds / (totalTicks * 2));
      _benchmarkRunning = false;
    });
  }

  double _estimateMemoryFootprint() {
    final double digitCount = _value.toStringAsFixed(2).length.toDouble();
    return 2.4 + (digitCount * 12.0) + 4.0 + 0.8;
  }

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;
    final isDesktop = width >= 1000;

    return Padding(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // Visual Showcase Card - Sticky/Fixed at the top
          _buildHeroDisplayCard(),
          const SizedBox(height: 24),

          // Scrollable area for Telemetry & Controls
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  // Performance Telemetry Card
                  _buildPerformanceTelemetryCard(),
                  const SizedBox(height: 24),

                  // Side-by-side controls or vertical layouts
                  if (isDesktop)
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Expanded(child: _buildValueControls()),
                        const SizedBox(width: 24),
                        Expanded(child: _buildAnimationConfigs()),
                      ],
                    )
                  else ...[
                    _buildValueControls(),
                    const SizedBox(height: 24),
                    _buildAnimationConfigs(),
                  ],
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildHeroDisplayCard() {
    final isDark = Theme.of(context).brightness == Brightness.dark;
    final gradientColors = isDark
        ? [const Color(0xFF1E1E38), const Color(0xFF111827)]
        : [const Color(0xFFEEF2F6), const Color(0xFFE2E8F0)];
    final borderColor = isDark ? const Color(0xFF312E81) : const Color(0xFFCBD5E1);
    final shadowColor = isDark
        ? const Color(0xFF6366F1).withValues(alpha: 0.15)
        : const Color(0xFF4F46E5).withValues(alpha: 0.1);

    return Container(
      padding: const EdgeInsets.symmetric(vertical: 48, horizontal: 24),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: gradientColors,
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(24),
        border: Border.all(color: borderColor, width: 1.5),
        boxShadow: [
          BoxShadow(
            color: shadowColor,
            blurRadius: 30,
            spreadRadius: 2,
          ),
        ],
      ),
      child: Column(
        children: [
          Text(
            'SANDBOX INTERACTIVE MONITOR',
            style: TextStyle(
              fontSize: 10,
              fontWeight: FontWeight.w900,
              color: isDark ? const Color(0xFF818CF8) : const Color(0xFF4F46E5),
              letterSpacing: 2,
            ),
          ),
          const SizedBox(height: 32),
          Center(
            child: AnimatedFlipCounter(
              value: _value,
              fractionDigits: 2,
              thousandSeparator: ',',
              transitionType: _transition,
              speedMultiplier: _speed,
              flipDirection: _flipDir,
              staggerDelay: Duration(milliseconds: _staggerMs.round()),
              staggerDirection: _staggerDir,
              startDelay: Duration(milliseconds: _delayMs.round()),
              increasingColor: const Color(0xFF10B981),
              decreasingColor: const Color(0xFFEF4444),
              colorFadeDuration: const Duration(milliseconds: 500),
              textStyle: TextStyle(
                fontSize: 64,
                fontWeight: FontWeight.w900,
                color: isDark ? Colors.white : const Color(0xFF0F172A),
                letterSpacing: -1.0,
                shadows: isDark
                    ? const [
                        BoxShadow(
                          color: Colors.black45,
                          offset: Offset(4, 6),
                          blurRadius: 12,
                        ),
                      ]
                    : const [
                        BoxShadow(
                          color: Colors.black12,
                          offset: Offset(2, 4),
                          blurRadius: 6,
                        ),
                      ],
              ),
            ),
          ),
          const SizedBox(height: 20),
          Text(
            'Active configuration: ${_transition.name.toUpperCase()} / ${_flipDir.name.toUpperCase()}',
            style: TextStyle(
              fontSize: 11,
              color: isDark ? const Color(0xFF64748B) : const Color(0xFF475569),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildPerformanceTelemetryCard() {
    final width = MediaQuery.sizeOf(context).width;
    final isMobile = width < 680;

    final telemetryWidgets = [
      _buildTelemetryTile(
        title: 'UPDATE FREQUENCY',
        value: '${_liveHz.toStringAsFixed(1)} Hz',
        subtitle: 'Live value updates per second',
        color: _liveHz > 0 ? const Color(0xFF10B981) : const Color(0xFF64748B),
        glow: _liveHz > 0,
      ),
      _buildTelemetryTile(
        title: 'FRAME RENDER TIME',
        value: '${_liveRenderTimeUs.toStringAsFixed(1)} µs',
        subtitle: 'Avg build time (~${(1000000 / _liveRenderTimeUs).toStringAsFixed(0)} FPS)',
        color: const Color(0xFF06B6D4),
        glow: _benchmarkRunning,
      ),
      _buildTelemetryTile(
        title: 'ESTIMATED RAM FOOTPRINT',
        value: '${_estimateMemoryFootprint().toStringAsFixed(1)} KB',
        subtitle: 'Subtree memory footprint',
        color: const Color(0xFF8B5CF6),
        glow: true,
      ),
    ];

    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(
                  'PERFORMANCE & TELEMETRY MONITOR',
                  style: TextStyle(
                    fontSize: 11,
                    fontWeight: FontWeight.bold,
                    color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                    letterSpacing: 1,
                  ),
                ),
                if (_benchmarkRunning)
                  SizedBox(
                    width: 16,
                    height: 16,
                    child: CircularProgressIndicator(
                      strokeWidth: 2,
                      color: Theme.of(context).colorScheme.primary,
                    ),
                  ),
              ],
            ),
            const SizedBox(height: 16),
            if (isMobile)
              Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  telemetryWidgets[0],
                  const SizedBox(height: 12),
                  telemetryWidgets[1],
                  const SizedBox(height: 12),
                  telemetryWidgets[2],
                ],
              )
            else
              Row(
                children: [
                  Expanded(child: telemetryWidgets[0]),
                  const SizedBox(width: 16),
                  Expanded(child: telemetryWidgets[1]),
                  const SizedBox(width: 16),
                  Expanded(child: telemetryWidgets[2]),
                ],
              ),
            const SizedBox(height: 20),
            Row(
              children: [
                Expanded(
                  child: Text(
                    'Subtree utilizes RepaintBoundary to isolate canvas repaints and caches TextPainters to bypass font layouts.',
                    style: TextStyle(
                      fontSize: 11,
                      color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                    ),
                  ),
                ),
                const SizedBox(width: 16),
                ElevatedButton.icon(
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Theme.of(context).colorScheme.primary,
                    foregroundColor: Colors.white,
                    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
                    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                  ),
                  onPressed: _benchmarkRunning ? null : _runStressTest,
                  icon: const Icon(Icons.speed_rounded, size: 16),
                  label: const Text('Run Live FPS Test', style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold)),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildTelemetryTile({
    required String title,
    required String value,
    required String subtitle,
    required Color color,
    required bool glow,
  }) {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Theme.of(context).scaffoldBackgroundColor,
        borderRadius: BorderRadius.circular(12),
        border: Border.all(
          color: glow ? color.withValues(alpha: 0.3) : (Theme.of(context).dividerTheme.color ?? const Color(0xFF1E293B)),
          width: 1.5,
        ),
        boxShadow: glow
            ? [
                BoxShadow(
                  color: color.withValues(alpha: 0.15),
                  blurRadius: 8,
                  spreadRadius: 1,
                )
              ]
            : null,
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            title,
            style: TextStyle(fontSize: 9, fontWeight: FontWeight.bold, color: color, letterSpacing: 0.5),
          ),
          const SizedBox(height: 8),
          Text(
            value,
            style: TextStyle(
              fontSize: 20,
              fontWeight: FontWeight.w900,
              color: Theme.of(context).colorScheme.onSurface,
            ),
          ),
          const SizedBox(height: 4),
          Text(
            subtitle,
            style: const TextStyle(fontSize: 10, color: Color(0xFF64748B)),
          ),
        ],
      ),
    );
  }

  Widget _buildValueControls() {
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'VALUATION TRIGGER',
              style: TextStyle(
                fontSize: 11,
                fontWeight: FontWeight.bold,
                color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                letterSpacing: 1,
              ),
            ),
            const SizedBox(height: 16),
            const Text('Manual Increments', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
            const SizedBox(height: 12),
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                _buildDeltaBtn(-100, isPositive: false),
                _buildDeltaBtn(-10, isPositive: false),
                _buildDeltaBtn(-0.5, isPositive: false),
                _buildDeltaBtn(0.5, isPositive: true),
                _buildDeltaBtn(10, isPositive: true),
                _buildDeltaBtn(100, isPositive: true),
              ],
            ),
            const Padding(
              padding: EdgeInsets.symmetric(vertical: 16),
              child: Divider(),
            ),
            const Text('Set Custom Value', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
            const SizedBox(height: 12),
            Row(
              children: [
                Expanded(
                  child: SizedBox(
                    height: 48,
                    child: TextField(
                      controller: _inputController,
                      keyboardType: const TextInputType.numberWithOptions(decimal: true),
                      style: const TextStyle(fontSize: 14),
                      decoration: InputDecoration(
                        hintText: 'e.g. 1045.50',
                        filled: true,
                        fillColor: Theme.of(context).scaffoldBackgroundColor,
                        contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 0),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(12),
                          borderSide: BorderSide.none,
                        ),
                      ),
                    ),
                  ),
                ),
                const SizedBox(width: 12),
                ElevatedButton(
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Theme.of(context).colorScheme.primary,
                    foregroundColor: Colors.white,
                    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
                    padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
                  ),
                  onPressed: () {
                    final val = double.tryParse(_inputController.text);
                    if (val != null) {
                      _updateValue(val);
                      FocusScope.of(context).unfocus();
                    }
                  },
                  child: const Text('Apply'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildDeltaBtn(double delta, {required bool isPositive}) {
    final text = isPositive ? '+$delta' : '$delta';
    final color = isPositive ? const Color(0xFF10B981) : const Color(0xFFEF4444);
    return InkWell(
      onTap: () => _updateValue(_value + delta),
      borderRadius: BorderRadius.circular(10),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
        decoration: BoxDecoration(
          color: color.withValues(alpha: 0.1),
          border: Border.all(color: color.withValues(alpha: 0.25)),
          borderRadius: BorderRadius.circular(10),
        ),
        child: Text(
          text,
          style: TextStyle(
            color: color,
            fontWeight: FontWeight.w900,
            fontSize: 12,
          ),
        ),
      ),
    );
  }

  Widget _buildAnimationConfigs() {
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'ANIMATION PARAMETERS',
              style: TextStyle(
                fontSize: 11,
                fontWeight: FontWeight.bold,
                color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                letterSpacing: 1,
              ),
            ),
            const SizedBox(height: 16),
            const Text('Transition Effect', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
            const SizedBox(height: 8),
            Wrap(
              spacing: 6,
              runSpacing: 6,
              children: CounterTransitionType.values.map((t) {
                final selected = _transition == t;
                return ChoiceChip(
                  label: Text(t.name.toUpperCase(), style: const TextStyle(fontSize: 10, fontWeight: FontWeight.bold)),
                  selected: selected,
                  selectedColor: Theme.of(context).colorScheme.primary,
                  checkmarkColor: Colors.white,
                  backgroundColor: Theme.of(context).scaffoldBackgroundColor,
                  labelStyle: TextStyle(
                    color: selected ? Colors.white : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                  ),
                  onSelected: (_) => setState(() => _transition = t),
                );
              }).toList(),
            ),
            const SizedBox(height: 16),
            const Text('Flip Scroll Direction', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
            const SizedBox(height: 8),
            Row(
              children: [AxisDirection.up, AxisDirection.down, AxisDirection.left, AxisDirection.right].map((dir) {
                final selected = _flipDir == dir;
                return Padding(
                  padding: const EdgeInsets.only(right: 8),
                  child: ChoiceChip(
                    label: Text(dir.name.toUpperCase(), style: const TextStyle(fontSize: 10, fontWeight: FontWeight.bold)),
                    selected: selected,
                    selectedColor: Theme.of(context).colorScheme.secondary,
                    checkmarkColor: Colors.white,
                    backgroundColor: Theme.of(context).scaffoldBackgroundColor,
                    labelStyle: TextStyle(
                      color: selected ? Colors.white : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                    ),
                    onSelected: (_) => setState(() => _flipDir = dir),
                  ),
                );
              }).toList(),
            ),
            const Divider(height: 32),
            _buildSliderItem(
              title: 'Speed Multiplier',
              description: 'Scale animation play durations.',
              value: _speed,
              min: 0.2,
              max: 3.0,
              display: '${_speed.toStringAsFixed(1)}x',
              onChanged: (val) => setState(() => _speed = val),
            ),
            const SizedBox(height: 12),
            _buildSliderItem(
              title: 'Start Delay',
              description: 'Initial wait interval before scroll.',
              value: _delayMs,
              min: 0.0,
              max: 1500.0,
              display: '${_delayMs.round()}ms',
              onChanged: (val) => setState(() => _delayMs = val),
            ),
            const SizedBox(height: 12),
            _buildSliderItem(
              title: 'Digit Stagger Delay',
              description: 'Delay between contiguous columns.',
              value: _staggerMs,
              min: 0.0,
              max: 300.0,
              display: '${_staggerMs.round()}ms',
              onChanged: (val) => setState(() => _staggerMs = val),
            ),
            const SizedBox(height: 12),
            Row(
              children: [
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text('Stagger direction', style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
                      Text(
                        'Sequence of staggered digits.',
                        style: TextStyle(
                          fontSize: 11,
                          color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                        ),
                      ),
                    ],
                  ),
                ),
                ChoiceChip(
                  label: const Text('L ➔ R', style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold)),
                  selected: _staggerDir == StaggerDirection.leftToRight,
                  selectedColor: Theme.of(context).colorScheme.primary,
                  backgroundColor: Theme.of(context).scaffoldBackgroundColor,
                  checkmarkColor: Colors.white,
                  labelStyle: TextStyle(
                    color: _staggerDir == StaggerDirection.leftToRight ? Colors.white : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                  ),
                  onSelected: (selected) => setState(() => _staggerDir = StaggerDirection.leftToRight),
                ),
                const SizedBox(width: 8),
                ChoiceChip(
                  label: const Text('R ➔ L', style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold)),
                  selected: _staggerDir == StaggerDirection.rightToLeft,
                  selectedColor: Theme.of(context).colorScheme.primary,
                  backgroundColor: Theme.of(context).scaffoldBackgroundColor,
                  checkmarkColor: Colors.white,
                  labelStyle: TextStyle(
                    color: _staggerDir == StaggerDirection.rightToLeft ? Colors.white : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                  ),
                  onSelected: (selected) => setState(() => _staggerDir = StaggerDirection.rightToLeft),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildSliderItem({
    required String title,
    required String description,
    required double value,
    required double min,
    required double max,
    required String display,
    required ValueChanged<double> onChanged,
  }) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
                  Text(
                    description,
                    style: TextStyle(
                      fontSize: 11,
                      color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                    ),
                  ),
                ],
              ),
            ),
            Text(
              display,
              style: TextStyle(
                fontSize: 14,
                fontWeight: FontWeight.bold,
                color: Theme.of(context).colorScheme.primary,
              ),
            ),
          ],
        ),
        Slider(
          value: value,
          min: min,
          max: max,
          onChanged: onChanged,
        ),
      ],
    );
  }
}

/// ==========================================
/// TAB 2: FORMATS & LOCALIZATIONS
/// ==========================================
class FormatsAndLocalesTab extends StatefulWidget {
  const FormatsAndLocalesTab({super.key});

  @override
  State<FormatsAndLocalesTab> createState() => _FormatsAndLocalesTabState();
}

class _FormatsAndLocalesTabState extends State<FormatsAndLocalesTab> {
  double _value = 1452589.67;
  bool _customAbbr = false;
  bool _hideLeading = false;
  int _fractionDigits = 2;
  int _compactDecimals = 1;
  List<int> _grouping = [3];
  NumeralSystem _numeralSystem = NumeralSystem.latin;
  bool _useCustomMapper = false;

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;
    int crossAxisCount = 1;
    if (width >= 1100) {
      crossAxisCount = 3;
    } else if (width >= 700) {
      crossAxisCount = 2;
    }

    return SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // Live display card
          _buildLiveDisplayCard(),
          const SizedBox(height: 24),

          // Control configurations
          _buildConfigurationSection(),
          const SizedBox(height: 24),

          // Grid display
          const Text(
            'REGIONAL PRESET GRID',
            style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold, color: Color(0xFF94A3B8), letterSpacing: 1),
          ),
          const SizedBox(height: 12),
          GridView.count(
            crossAxisCount: crossAxisCount,
            crossAxisSpacing: 16,
            mainAxisSpacing: 16,
            childAspectRatio: 1.5,
            shrinkWrap: true,
            physics: const NeverScrollableScrollPhysics(),
            children: [
              _buildGridItem('British Pound (GBP Preset)', _buildGBPWidget()),
              _buildGridItem('Japanese Yen (JPY Preset)', _buildJPYWidget()),
              _buildGridItem('Percentage Formatter', _buildPercentWidget()),
              _buildGridItem('Compact Presets', _buildCompactWidget()),
              _buildGridItem('Regional Numeral System', _buildNumeralSystemWidget()),
              _buildGridItem('Custom Symbol Mapping', _buildMapperWidget()),
            ],
          ),
        ],
      ),
    );
  }

  Widget _buildLiveDisplayCard() {
    return Container(
      padding: const EdgeInsets.all(24),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surface,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: Theme.of(context).dividerTheme.color ?? const Color(0xFF334155), width: 1),
      ),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'FORMATTER MONITOR',
                  style: TextStyle(
                    fontSize: 10,
                    fontWeight: FontWeight.bold,
                    color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                  ),
                ),
                const SizedBox(height: 8),
                Text(
                  'Real-time raw value: ${_value.toStringAsFixed(2)}',
                  style: TextStyle(
                    fontSize: 12,
                    color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                  ),
                ),
              ],
            ),
          ),
          Row(
            children: [
              IconButton(
                icon: const Icon(Icons.remove_circle_outline, color: Color(0xFFEF4444)),
                onPressed: () => setState(() => _value = (_value - 250000.5).clamp(0, double.infinity)),
              ),
              const SizedBox(width: 8),
              IconButton(
                icon: const Icon(Icons.add_circle_outline, color: Color(0xFF10B981)),
                onPressed: () => setState(() => _value += 250000.5),
              ),
            ],
          ),
        ],
      ),
    );
  }

  Widget _buildConfigurationSection() {
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'DYNAMIC FORMAT CONFIGURATIONS',
              style: TextStyle(
                fontSize: 11,
                fontWeight: FontWeight.bold,
                color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                letterSpacing: 1,
              ),
            ),
            const SizedBox(height: 16),
            Row(
              children: [
                Expanded(
                  child: _buildToggleControl(
                    'Hide leading zeros',
                    'Removes padding display.',
                    _hideLeading,
                    (val) => setState(() => _hideLeading = val),
                  ),
                ),
                const SizedBox(width: 16),
                Expanded(
                  child: _buildToggleControl(
                    'Custom Abbreviations',
                    'replaces M, B, T with custom words.',
                    _customAbbr,
                    (val) => setState(() => _customAbbr = val),
                  ),
                ),
              ],
            ),
            const Divider(height: 24),
            Row(
              children: [
                Expanded(
                  child: _buildNumberInputControl(
                    'Fraction Digits',
                    'Adjust decimal precision.',
                    _fractionDigits,
                    0,
                    4,
                    (val) => setState(() => _fractionDigits = val),
                  ),
                ),
                const SizedBox(width: 16),
                Expanded(
                  child: _buildNumberInputControl(
                    'Compact Decimals',
                    'Fraction values for compact notation.',
                    _compactDecimals,
                    0,
                    3,
                    (val) => setState(() => _compactDecimals = val),
                  ),
                ),
              ],
            ),
            const Divider(height: 24),
            const Text('Digit Spacing Patterns', style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
            const SizedBox(height: 8),
            Row(
              children: [
                _buildGroupingChip('Western [3]', [3]),
                const SizedBox(width: 8),
                _buildGroupingChip('Indian Lakh [3, 2]', [3, 2]),
                const SizedBox(width: 8),
                _buildGroupingChip('East Asian [4]', [4]),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildToggleControl(String title, String desc, bool value, ValueChanged<bool> onChanged) {
    return Row(
      children: [
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
              Text(
                desc,
                style: TextStyle(
                  fontSize: 10,
                  color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                ),
              ),
            ],
          ),
        ),
        Switch(value: value, onChanged: onChanged),
      ],
    );
  }

  Widget _buildNumberInputControl(String title, String desc, int value, int min, int max, ValueChanged<int> onChanged) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
              Text(
                desc,
                style: TextStyle(
                  fontSize: 10,
                  color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                ),
              ),
            ],
          ),
        ),
        Row(
          children: [
            IconButton(
              icon: const Icon(Icons.remove, size: 16),
              onPressed: value > min ? () => onChanged(value - 1) : null,
            ),
            Text('$value', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14)),
            IconButton(
              icon: const Icon(Icons.add, size: 16),
              onPressed: value < max ? () => onChanged(value + 1) : null,
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildGroupingChip(String label, List<int> pattern) {
    final selected = _listsEqual(_grouping, pattern);
    return ChoiceChip(
      label: Text(label, style: const TextStyle(fontSize: 11)),
      selected: selected,
      selectedColor: Theme.of(context).colorScheme.primary,
      checkmarkColor: Colors.white,
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      labelStyle: TextStyle(
        color: selected ? Colors.white : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
      ),
      onSelected: (sel) {
        if (sel) setState(() => _grouping = pattern);
      },
    );
  }

  bool _listsEqual(List<int> a, List<int> b) {
    if (a.length != b.length) return false;
    for (int i = 0; i < a.length; i++) {
      if (a[i] != b[i]) return false;
    }
    return true;
  }

  Widget _buildGridItem(String title, Widget counter) {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surface,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: Theme.of(context).dividerTheme.color ?? const Color(0xFF334155), width: 1),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(title, style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.primary)),
          Center(child: counter),
          const SizedBox(height: 2),
        ],
      ),
    );
  }

  Widget _buildGBPWidget() {
    return AnimatedFlipCounter.gbp(
      value: _value,
      hideLeadingZeroes: _hideLeading,
      textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: Color(0xFF10B981)),
    );
  }

  Widget _buildJPYWidget() {
    return AnimatedFlipCounter.jpy(
      value: _value,
      hideLeadingZeroes: _hideLeading,
      textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: Color(0xFF06B6D4)),
    );
  }

  Widget _buildPercentWidget() {
    return AnimatedFlipCounter.percentage(
      value: (_value / 100000) % 100,
      fractionDigits: _fractionDigits,
      hideLeadingZeroes: _hideLeading,
      textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: Color(0xFFF59E0B)),
    );
  }

  Widget _buildCompactWidget() {
    final customMap = {
      1e3: ' kilo',
      1e6: ' millions',
      1e9: ' billions',
    };
    return AnimatedFlipCounter.compact(
      value: _value,
      compactFractionDigits: _compactDecimals,
      compactAbbreviations: _customAbbr ? customMap : null,
      hideLeadingZeroes: _hideLeading,
      textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: Color(0xFFEC4899)),
    );
  }

  Widget _buildNumeralSystemWidget() {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        AnimatedFlipCounter(
          value: _value,
          fractionDigits: _fractionDigits,
          groupingPattern: _grouping,
          thousandSeparator: ',',
          numeralSystem: _numeralSystem,
          hideLeadingZeroes: _hideLeading,
          textStyle: TextStyle(
            fontSize: 22,
            fontWeight: FontWeight.bold,
            color: Theme.of(context).colorScheme.onSurface,
          ),
        ),
        const SizedBox(height: 8),
        DropdownButton<NumeralSystem>(
          value: _numeralSystem,
          isDense: true,
          dropdownColor: Theme.of(context).colorScheme.surface,
          underline: const SizedBox.shrink(),
          onChanged: (sys) {
            if (sys != null) setState(() => _numeralSystem = sys);
          },
          items: NumeralSystem.values.map((sys) {
            return DropdownMenuItem(
              value: sys,
              child: Text(sys.name.toUpperCase(), style: const TextStyle(fontSize: 10, fontWeight: FontWeight.bold)),
            );
          }).toList(),
        ),
      ],
    );
  }

  Widget _buildMapperWidget() {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        AnimatedFlipCounter(
          value: _value % 100000,
          fractionDigits: 0,
          numeralMapper: _useCustomMapper ? _emojiMapper : null,
          hideLeadingZeroes: _hideLeading,
          textStyle: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.amber),
        ),
        const SizedBox(height: 4),
        ChoiceChip(
          label: const Text('Emoji Mapper', style: TextStyle(fontSize: 9, fontWeight: FontWeight.bold)),
          selected: _useCustomMapper,
          selectedColor: Colors.amber,
          checkmarkColor: Colors.black,
          backgroundColor: Theme.of(context).scaffoldBackgroundColor,
          labelStyle: TextStyle(
            color: _useCustomMapper ? Colors.black : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
          ),
          onSelected: (sel) => setState(() => _useCustomMapper = sel),
        ),
      ],
    );
  }

  String _emojiMapper(int val) {
    const emojis = ['⓪', '①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨'];
    return emojis[val];
  }
}

/// ==========================================
/// TAB 3: CUSTOM STYLINGS
/// ==========================================
class CustomStylingsTab extends StatelessWidget {
  const CustomStylingsTab({super.key});

  @override
  Widget build(BuildContext context) {
    return const _CustomStylingsTabContent();
  }
}

class _CustomStylingsTabContent extends StatefulWidget {
  const _CustomStylingsTabContent();

  @override
  State<_CustomStylingsTabContent> createState() => _CustomStylingsTabContentState();
}

class _CustomStylingsTabContentState extends State<_CustomStylingsTabContent> {
  double _value = 45.0;
  Timer? _timer;

  @override
  void initState() {
    super.initState();
    _timer = Timer.periodic(const Duration(seconds: 3), (timer) {
      if (mounted) {
        setState(() {
          _value = _value == 45.0 ? 82.0 : 45.0;
        });
      }
    });
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;
    int crossAxisCount = 1;
    if (width >= 1100) {
      crossAxisCount = 3;
    } else if (width >= 700) {
      crossAxisCount = 2;
    }

    return SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Text(
            'CUSTOM WRAPPERS & TRANSITION BUILDERS',
            style: TextStyle(
              fontSize: 11,
              fontWeight: FontWeight.bold,
              color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
              letterSpacing: 1,
            ),
          ),
          const SizedBox(height: 16),
          GridView.count(
            crossAxisCount: crossAxisCount,
            crossAxisSpacing: 16,
            mainAxisSpacing: 16,
            childAspectRatio: 1.4,
            shrinkWrap: true,
            physics: const NeverScrollableScrollPhysics(),
            children: [
              _buildFlipClockCard(),
              _buildNeonGlowCard(),
              _buildNixieTubeCard(),
              _buildWoodTileCard(),
              _buildPillBadgeCard(),
              _buildCustomTransitionCard(),
            ],
          ),
        ],
      ),
    );
  }

  Widget _buildStyleCard({
    required String title,
    required String description,
    required Widget counter,
  }) {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surface,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: Theme.of(context).dividerTheme.color ?? const Color(0xFF334155), width: 1),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                title,
                style: TextStyle(
                  fontSize: 13,
                  fontWeight: FontWeight.bold,
                  color: Theme.of(context).colorScheme.onSurface,
                ),
              ),
              const SizedBox(height: 2),
              Text(
                description,
                style: TextStyle(
                  fontSize: 10,
                  color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                ),
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
              ),
            ],
          ),
          const SizedBox(height: 8),
          Center(child: counter),
          const SizedBox(height: 4),
        ],
      ),
    );
  }

  Widget _buildFlipClockCard() {
    return _buildStyleCard(
      title: 'Retro Flip Clock',
      description: 'Digits inside dark cards with split line.',
      counter: AnimatedFlipCounter(
        value: _value,
        wholeDigits: 3,
        duration: const Duration(milliseconds: 600),
        curve: Curves.easeOutBack,
        digitWrapperBuilder: (context, index, child) {
          return Container(
            margin: const EdgeInsets.symmetric(horizontal: 2),
            padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
            decoration: BoxDecoration(
              color: Colors.black,
              borderRadius: BorderRadius.circular(6),
              border: Border.all(color: const Color(0xFF475569), width: 1),
            ),
            child: Stack(
              alignment: Alignment.center,
              children: [
                child,
                Positioned(
                  left: 0,
                  right: 0,
                  child: Container(
                    height: 1,
                    color: const Color(0xFF334155),
                  ),
                ),
              ],
            ),
          );
        },
        textStyle: const TextStyle(
          fontSize: 26,
          fontWeight: FontWeight.bold,
          color: Color(0xFFFFB000),
        ),
      ),
    );
  }

  Widget _buildNeonGlowCard() {
    return _buildStyleCard(
      title: 'Cyberpunk Neon',
      description: 'Deep visual glowing shadow drop effect.',
      counter: AnimatedFlipCounter(
        value: _value,
        duration: const Duration(milliseconds: 500),
        curve: Curves.easeInOut,
        textStyle: const TextStyle(
          fontSize: 32,
          fontWeight: FontWeight.w900,
          color: Color(0xFF06B6D4),
          shadows: [
            Shadow(color: Color(0xFF06B6D4), blurRadius: 10),
            Shadow(color: Color(0xFFEC4899), blurRadius: 20),
          ],
        ),
      ),
    );
  }

  Widget _buildNixieTubeCard() {
    return _buildStyleCard(
      title: 'Glass Nixie Tube',
      description: 'Filament indicators with warm orange hue.',
      counter: AnimatedFlipCounter(
        value: _value,
        wholeDigits: 2,
        textStyle: const TextStyle(
          fontSize: 26,
          fontFamily: 'monospace',
          fontWeight: FontWeight.bold,
          color: Color(0xFFFF5500),
          shadows: [
            Shadow(color: Color(0xFFFF5500), blurRadius: 8),
            Shadow(color: Color(0xFFFF0000), blurRadius: 16),
          ],
        ),
        digitWrapperBuilder: (context, index, child) {
          return Container(
            margin: const EdgeInsets.symmetric(horizontal: 2),
            padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
            decoration: BoxDecoration(
              color: const Color(0xFF1E0A03),
              borderRadius: BorderRadius.circular(6),
              border: Border.all(color: const Color(0xFFFF5500).withValues(alpha: 0.5), width: 1.2),
            ),
            child: child,
          );
        },
      ),
    );
  }

  Widget _buildWoodTileCard() {
    return _buildStyleCard(
      title: 'Wood Game Tile',
      description: 'Scrabble-like tiles with point score subscript.',
      counter: AnimatedFlipCounter(
        value: _value,
        wholeDigits: 2,
        textStyle: const TextStyle(
          fontSize: 22,
          fontWeight: FontWeight.bold,
          color: Color(0xFF5D4037),
        ),
        digitWrapperBuilder: (context, index, child) {
          return Container(
            margin: const EdgeInsets.symmetric(horizontal: 3),
            padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
            decoration: BoxDecoration(
              color: const Color(0xFFFFECB3),
              borderRadius: BorderRadius.circular(4),
              border: Border.all(color: const Color(0xFFD7CCC8), width: 1.2),
              boxShadow: const [
                BoxShadow(
                  color: Colors.black12,
                  offset: Offset(1, 1),
                  blurRadius: 1.5,
                ),
              ],
            ),
            child: Stack(
              clipBehavior: Clip.none,
              children: [
                child,
                Positioned(
                  right: -4,
                  bottom: -4,
                  child: Text(
                    '${(index + 1) * 2}',
                    style: const TextStyle(fontSize: 7, fontWeight: FontWeight.bold, color: Color(0xFF8D6E63)),
                  ),
                ),
              ],
            ),
          );
        },
      ),
    );
  }

  Widget _buildPillBadgeCard() {
    final color = Theme.of(context).colorScheme.primary;
    return _buildStyleCard(
      title: 'Pill Capsule Badge',
      description: 'Curved gradient borders and translucent backdrop.',
      counter: AnimatedFlipCounter(
        value: _value,
        wholeDigits: 2,
        textStyle: TextStyle(
          fontSize: 22,
          fontWeight: FontWeight.bold,
          color: color,
        ),
        digitWrapperBuilder: (context, index, child) {
          return Container(
            margin: const EdgeInsets.symmetric(horizontal: 3),
            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 3),
            decoration: BoxDecoration(
              color: color.withValues(alpha: 0.1),
              borderRadius: BorderRadius.circular(16),
              border: Border.all(color: color.withValues(alpha: 0.4), width: 1.2),
            ),
            child: child,
          );
        },
      ),
    );
  }

  Widget _buildCustomTransitionCard() {
    return _buildStyleCard(
      title: 'Morph Transition',
      description: 'Opacity/Translation sliding layouts.',
      counter: AnimatedFlipCounter(
        value: _value,
        duration: const Duration(milliseconds: 700),
        digitTransitionBuilder: (context, currentDigit, nextDigit, progress, size) {
          return Stack(
            alignment: Alignment.center,
            children: [
              Transform.translate(
                offset: Offset(0, -progress * size.height),
                child: Opacity(
                  opacity: (1 - progress).clamp(0.0, 1.0),
                  child: currentDigit,
                ),
              ),
              Transform.translate(
                offset: Offset(0, (1 - progress) * size.height),
                child: Opacity(
                  opacity: progress.clamp(0.0, 1.0),
                  child: nextDigit,
                ),
              ),
            ],
          );
        },
        textStyle: TextStyle(
          fontSize: 26,
          fontWeight: FontWeight.w900,
          color: Theme.of(context).colorScheme.onSurface,
        ),
      ),
    );
  }
}

/// ==========================================
/// TAB 4: CONTROLLER & LIFECYCLE
/// ==========================================
class ControllerTab extends StatefulWidget {
  const ControllerTab({super.key});

  @override
  State<ControllerTab> createState() => _ControllerTabState();
}

class _ControllerTabState extends State<ControllerTab> {
  late CounterController _controller;
  bool _haptics = true;
  String _status = 'Dismissed';
  final List<String> _logs = [];
  final ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    _controller = CounterController(initialValue: 10.0);
    _controller.addStatusListener(_onStatusChange);
  }

  void _onStatusChange(AnimationStatus status) {
    if (mounted) {
      setState(() {
        _status = status.name.toUpperCase();
        _addLog('Status changed to: $_status');
      });
    }
  }

  void _addLog(String text) {
    final now = DateTime.now();
    final timestamp = '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}.${(now.millisecond).toString().padLeft(3, '0')}';
    _logs.add('[$timestamp] $text');
    if (_logs.length > 50) {
      _logs.removeAt(0);
    }
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (_scrollController.hasClients) {
        _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
      }
    });
  }

  @override
  void dispose() {
    _controller.removeStatusListener(_onStatusChange);
    _controller.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;
    final isDesktop = width >= 800;

    return Padding(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          _buildMonitorCard(),
          const SizedBox(height: 24),
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  if (isDesktop)
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Expanded(child: _buildControlsCard()),
                        const SizedBox(width: 24),
                        Expanded(child: _buildTerminalCard()),
                      ],
                    )
                  else ...[
                    _buildControlsCard(),
                    const SizedBox(height: 24),
                    _buildTerminalCard(),
                  ],
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildMonitorCard() {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 24),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surface,
        borderRadius: BorderRadius.circular(20),
        border: Border.all(color: Theme.of(context).dividerTheme.color ?? const Color(0xFF334155)),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha: Theme.of(context).brightness == Brightness.dark ? 0.3 : 0.05),
            blurRadius: 10,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                'CONTROLLER VALUE DISPLAY',
                style: TextStyle(
                  fontSize: 10,
                  fontWeight: FontWeight.bold,
                  color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                ),
              ),
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                decoration: BoxDecoration(
                  color: _status == 'FORWARD' || _status == 'REVERSE'
                      ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.2)
                      : Theme.of(context).scaffoldBackgroundColor,
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Text(
                  _status,
                  style: TextStyle(
                    fontSize: 10,
                    fontWeight: FontWeight.bold,
                    color: _status == 'FORWARD' || _status == 'REVERSE'
                        ? Theme.of(context).colorScheme.primary
                        : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
                  ),
                ),
              ),
            ],
          ),
          const SizedBox(height: 32),
          AnimatedFlipCounter(
            controller: _controller,
            fractionDigits: 1,
            triggerHaptics: _haptics,
            duration: const Duration(milliseconds: 1000),
            onAnimationStart: () => _addLog('Animation event: onAnimationStart'),
            onAnimationEnd: () => _addLog('Animation event: onAnimationEnd'),
            onPause: () => _addLog('Animation event: onPause'),
            onResume: () => _addLog('Animation event: onResume'),
            onRepeat: () => _addLog('Animation event: onRepeat'),
            onReverse: () => _addLog('Animation event: onReverse'),
            onReset: () => _addLog('Animation event: onReset'),
            textStyle: TextStyle(
              fontSize: 60,
              fontWeight: FontWeight.w900,
              color: Theme.of(context).colorScheme.onSurface,
            ),
          ),
          const SizedBox(height: 12),
          Text(
            'Target logic: ${_controller.value.toStringAsFixed(1)}',
            style: TextStyle(
              fontSize: 12,
              color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildControlsCard() {
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'LIFECYCLE TRIGGERS',
              style: TextStyle(
                fontSize: 11,
                fontWeight: FontWeight.bold,
                color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                letterSpacing: 1,
              ),
            ),
            const SizedBox(height: 16),
            const Text('Value Jumps / Animations', style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
            const SizedBox(height: 8),
            Row(
              children: [
                Expanded(
                  child: ElevatedButton(
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Theme.of(context).colorScheme.primary,
                      foregroundColor: Colors.white,
                    ),
                    onPressed: () {
                      final target = math.Random().nextDouble() * 100;
                      _addLog('Trigger: animateTo(${target.toStringAsFixed(1)})');
                      _controller.animateTo(target);
                    },
                    child: const Text('Animate To Random', style: TextStyle(fontSize: 12)),
                  ),
                ),
                const SizedBox(width: 8),
                Expanded(
                  child: OutlinedButton(
                    onPressed: () {
                      final target = math.Random().nextDouble() * 100;
                      _addLog('Trigger: jumpTo(${target.toStringAsFixed(1)})');
                      _controller.jumpTo(target);
                    },
                    child: const Text('Jump To Random', style: TextStyle(fontSize: 12)),
                  ),
                ),
              ],
            ),
            const Divider(height: 24),
            const Text('Animation Playback Controls', style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
            const SizedBox(height: 12),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                _buildPlaybackIconBtn(Icons.pause_rounded, 'Pause', () {
                  _addLog('Action: pause()');
                  _controller.pause();
                }),
                _buildPlaybackIconBtn(Icons.play_arrow_rounded, 'Resume', () {
                  _addLog('Action: resume()');
                  _controller.resume();
                }),
                _buildPlaybackIconBtn(Icons.compare_arrows_rounded, 'Reverse', () {
                  _addLog('Action: reverse()');
                  _controller.reverse();
                }),
                _buildPlaybackIconBtn(Icons.loop_rounded, 'Repeat', () {
                  _addLog('Action: repeat(reverse: true)');
                  _controller.repeat(reverse: true);
                }),
                _buildPlaybackIconBtn(Icons.replay_rounded, 'Reset', () {
                  _addLog('Action: jumpTo(10.0)');
                  _controller.jumpTo(10.0);
                }),
              ],
            ),
            const Divider(height: 24),
            Row(
              children: [
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text('Digit selection haptics', style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
                      Text(
                        'Triggers haptic ticks during rolls.',
                        style: TextStyle(
                          fontSize: 11,
                          color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                        ),
                      ),
                    ],
                  ),
                ),
                Switch(
                  value: _haptics,
                  onChanged: (val) => setState(() => _haptics = val),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildPlaybackIconBtn(IconData icon, String tooltip, VoidCallback onPressed) {
    return Tooltip(
      message: tooltip,
      child: IconButton(
        icon: Icon(icon),
        onPressed: onPressed,
        style: IconButton.styleFrom(
          backgroundColor: Theme.of(context).scaffoldBackgroundColor,
          foregroundColor: Theme.of(context).colorScheme.primary,
          padding: const EdgeInsets.all(12),
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        ),
      ),
    );
  }

  Widget _buildTerminalCard() {
    return Container(
      height: 310,
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: Colors.black,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: Theme.of(context).dividerTheme.color ?? const Color(0xFF1E293B)),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              const Text(
                'LIFECYCLE EVENTS CONSOLE',
                style: TextStyle(fontFamily: 'Courier', fontSize: 11, fontWeight: FontWeight.bold, color: Colors.greenAccent),
              ),
              GestureDetector(
                onTap: () => setState(() => _logs.clear()),
                child: const Text(
                  'CLEAR',
                  style: TextStyle(fontFamily: 'Courier', fontSize: 10, color: Colors.grey, fontWeight: FontWeight.bold),
                ),
              ),
            ],
          ),
          const SizedBox(height: 12),
          Expanded(
            child: _logs.isEmpty
                ? const Center(
                    child: Text(
                      'No events logged yet.\nInteract with the controls above.',
                      textAlign: TextAlign.center,
                      style: TextStyle(fontFamily: 'Courier', fontSize: 11, color: Colors.grey),
                    ),
                  )
                : ListView.builder(
                    controller: _scrollController,
                    itemCount: _logs.length,
                    itemBuilder: (context, index) {
                      return Padding(
                        padding: const EdgeInsets.only(bottom: 6),
                        child: Text(
                          _logs[index],
                          style: const TextStyle(
                            fontFamily: 'Courier',
                            fontSize: 11,
                            color: Colors.greenAccent,
                          ),
                        ),
                      );
                    },
                  ),
          ),
        ],
      ),
    );
  }
}

/// ==========================================
/// TAB 5: STOCK MARKET TICKER SIMULATOR
/// ==========================================
class StockMarketTab extends StatefulWidget {
  const StockMarketTab({super.key});

  @override
  State<StockMarketTab> createState() => _StockMarketTabState();
}

class _StockMarketTabState extends State<StockMarketTab> {
  final List<Map<String, dynamic>> _stocks = [
    {
      'symbol': 'AAPL',
      'name': 'Apple Inc.',
      'price': 182.41,
      'change': 1.25,
      'changePercent': 0.69,
      'volume': '52.4M',
      'high': 183.50,
      'low': 180.80,
    },
    {
      'symbol': 'GOOGL',
      'name': 'Alphabet Inc.',
      'price': 142.10,
      'change': -0.85,
      'changePercent': -0.60,
      'volume': '28.1M',
      'high': 143.90,
      'low': 141.20,
    },
    {
      'symbol': 'TSLA',
      'name': 'Tesla Inc.',
      'price': 175.50,
      'change': 4.15,
      'changePercent': 2.42,
      'volume': '91.8M',
      'high': 178.00,
      'low': 170.20,
    },
    {
      'symbol': 'NVDA',
      'name': 'NVIDIA Corp.',
      'price': 875.20,
      'change': 15.30,
      'changePercent': 1.78,
      'volume': '44.2M',
      'high': 885.00,
      'low': 852.10,
    },
    {
      'symbol': 'MSFT',
      'name': 'Microsoft Corp.',
      'price': 420.35,
      'change': -2.15,
      'changePercent': -0.51,
      'volume': '22.9M',
      'high': 425.00,
      'low': 418.50,
    },
  ];

  int _selectedStockIndex = 0;
  String _marketTrend = 'volatile'; // bullish, bearish, volatile
  double _tickSpeedSeconds = 1.0;
  bool _isLive = true;
  Timer? _tickerTimer;

  // Trading variables
  String _tradingType = 'BUY'; // BUY, SELL
  int _sharesCount = 10;
  final List<String> _tradeHistory = [];
  final ScrollController _tradeScrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    _startTicker();
  }

  @override
  void dispose() {
    _tickerTimer?.cancel();
    _tradeScrollController.dispose();
    super.dispose();
  }

  void _startTicker() {
    _tickerTimer?.cancel();
    if (!_isLive) return;
    _tickerTimer = Timer.periodic(
      Duration(milliseconds: (_tickSpeedSeconds * 1000).round()),
      (timer) {
        if (!mounted) return;
        _simulateTicks();
      },
    );
  }

  void _simulateTicks() {
    setState(() {
      final random = math.Random();
      for (var i = 0; i < _stocks.length; i++) {
        final stock = _stocks[i];
        final currentPrice = stock['price'] as double;

        double changeDirection;
        if (_marketTrend == 'bullish') {
          // 75% chance up
          changeDirection = random.nextDouble() < 0.75 ? 1.0 : -1.0;
        } else if (_marketTrend == 'bearish') {
          // 75% chance down
          changeDirection = random.nextDouble() < 0.75 ? -1.0 : 1.0;
        } else {
          // 50/50
          changeDirection = random.nextBool() ? 1.0 : -1.0;
        }

        final volatility = currentPrice > 500 ? 4.5 : 1.2;
        final delta = changeDirection * (random.nextDouble() * volatility);
        final newPrice = math.max(1.0, double.parse((currentPrice + delta).toStringAsFixed(2)));

        final difference = newPrice - currentPrice;
        stock['price'] = newPrice;
        stock['change'] = difference;
        stock['changePercent'] = (difference / currentPrice) * 100;

        if (newPrice > stock['high']) {
          stock['high'] = newPrice;
        }
        if (newPrice < stock['low']) {
          stock['low'] = newPrice;
        }
      }
    });
  }

  void _executeTrade() {
    final stock = _stocks[_selectedStockIndex];
    final price = stock['price'] as double;
    final symbol = stock['symbol'] as String;
    final totalCost = price * _sharesCount;
    final timeStr = DateTime.now().toIso8601String().substring(11, 19);

    setState(() {
      _tradeHistory.insert(
        0,
        '[$timeStr] $_tradingType $_sharesCount sh of $symbol @ \$${price.toStringAsFixed(2)} | Vol: \$${totalCost.toStringAsFixed(2)}',
      );
    });

    // Auto-scroll trade console to top
    if (_tradeScrollController.hasClients) {
      _tradeScrollController.animateTo(
        0.0,
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeOut,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    final isDark = Theme.of(context).brightness == Brightness.dark;
    final width = MediaQuery.sizeOf(context).width;
    final isDesktop = width >= 950;
    
    final activeStock = _stocks[_selectedStockIndex];
    final priceChange = activeStock['change'] as double;
    final percentChange = activeStock['changePercent'] as double;
    final isPositive = priceChange >= 0;

    final containerBg = isDark ? const Color(0xFF151D30) : Colors.white;
    final borderCol = isDark ? const Color(0xFF1E293B) : const Color(0xFFE2E8F0);

    return SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // Header Row
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'LIVE TRADING BOARD',
                    style: TextStyle(
                      fontSize: 10,
                      fontWeight: FontWeight.w900,
                      color: Theme.of(context).colorScheme.primary,
                      letterSpacing: 2.0,
                    ),
                  ),
                  const SizedBox(height: 4),
                  Text(
                    'High Frequency Stock Simulation',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                      color: Theme.of(context).colorScheme.onSurface,
                    ),
                  ),
                ],
              ),
              Row(
                children: [
                  Text(
                    'Simulation Live',
                    style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7)),
                  ),
                  const SizedBox(width: 8),
                  Switch.adaptive(
                    value: _isLive,
                    activeTrackColor: Theme.of(context).colorScheme.primary,
                    onChanged: (val) {
                      setState(() {
                        _isLive = val;
                        _startTicker();
                      });
                    },
                  ),
                ],
              ),
            ],
          ),
          const SizedBox(height: 24),

          // Main Layout
          if (isDesktop)
            Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // Left Column: Stock List & Simulator Config
                Expanded(
                  flex: 2,
                  child: Column(
                    children: [
                      _buildStockSelectorList(),
                      const SizedBox(height: 20),
                      _buildSimulatorConfig(),
                    ],
                  ),
                ),
                const SizedBox(width: 24),
                // Right Column: Large Ticker & Trading Box
                Expanded(
                  flex: 3,
                  child: Column(
                    children: [
                      _buildBigTickerPanel(activeStock, isPositive, priceChange, percentChange, isDark),
                      const SizedBox(height: 20),
                      _buildOrderTradingPanel(activeStock, isDark, containerBg, borderCol),
                    ],
                  ),
                ),
              ],
            )
          else
            Column(
              children: [
                _buildStockSelectorList(),
                const SizedBox(height: 20),
                _buildBigTickerPanel(activeStock, isPositive, priceChange, percentChange, isDark),
                const SizedBox(height: 20),
                _buildOrderTradingPanel(activeStock, isDark, containerBg, borderCol),
                const SizedBox(height: 20),
                _buildSimulatorConfig(),
              ],
            ),
        ],
      ),
    );
  }

  Widget _buildStockSelectorList() {
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'WATCHLIST',
              style: TextStyle(
                fontSize: 11,
                fontWeight: FontWeight.bold,
                color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                letterSpacing: 1,
              ),
            ),
            const SizedBox(height: 12),
            ListView.separated(
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              itemCount: _stocks.length,
              separatorBuilder: (_, __) => const SizedBox(height: 8),
              itemBuilder: (context, index) {
                final stock = _stocks[index];
                final isSelected = _selectedStockIndex == index;
                final isStockPositive = (stock['change'] as double) >= 0;
                final activeBorderColor = isStockPositive ? Colors.green.withValues(alpha: 0.5) : Colors.red.withValues(alpha: 0.5);

                return InkWell(
                  onTap: () => setState(() => _selectedStockIndex = index),
                  borderRadius: BorderRadius.circular(12),
                  child: Container(
                    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                    decoration: BoxDecoration(
                      color: isSelected
                          ? (isStockPositive
                              ? Colors.green.withValues(alpha: 0.1)
                              : Colors.red.withValues(alpha: 0.1))
                          : Colors.transparent,
                      borderRadius: BorderRadius.circular(12),
                      border: Border.all(
                        color: isSelected
                            ? activeBorderColor
                            : (Theme.of(context).dividerTheme.color ?? const Color(0xFF1E293B)),
                        width: 1.5,
                      ),
                    ),
                    child: Row(
                      children: [
                        // Ticker Symbol
                        Container(
                          width: 65,
                          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
                          decoration: BoxDecoration(
                            color: Theme.of(context).scaffoldBackgroundColor,
                            borderRadius: BorderRadius.circular(8),
                          ),
                          child: Center(
                            child: Text(
                              stock['symbol'],
                              style: const TextStyle(fontWeight: FontWeight.w900, fontSize: 13),
                            ),
                          ),
                        ),
                        const SizedBox(width: 12),
                        // Company Name
                        Expanded(
                          child: Text(
                            stock['name'],
                            style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
                            overflow: TextOverflow.ellipsis,
                          ),
                        ),
                        // Mini Animated Counter
                        Column(
                          crossAxisAlignment: CrossAxisAlignment.end,
                          children: [
                            AnimatedFlipCounter.usd(
                              value: stock['price'],
                              textStyle: const TextStyle(
                                fontSize: 14,
                                fontWeight: FontWeight.w900,
                              ),
                              increasingColor: Colors.greenAccent,
                              decreasingColor: Colors.redAccent,
                            ),
                            const SizedBox(height: 2),
                            Row(
                              children: [
                                Icon(
                                  isStockPositive ? Icons.arrow_drop_up_rounded : Icons.arrow_drop_down_rounded,
                                  color: isStockPositive ? Colors.green : Colors.red,
                                  size: 16,
                                ),
                                Text(
                                  '${isStockPositive ? "+" : ""}${((stock['changePercent'] as double)).toStringAsFixed(2)}%',
                                  style: TextStyle(
                                    fontSize: 10,
                                    fontWeight: FontWeight.bold,
                                    color: isStockPositive ? Colors.green : Colors.red,
                                  ),
                                ),
                              ],
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildSimulatorConfig() {
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'SIMULATION PARAMETERS',
              style: TextStyle(
                fontSize: 11,
                fontWeight: FontWeight.bold,
                color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                letterSpacing: 1,
              ),
            ),
            const SizedBox(height: 16),
            // Market Trend
            const Text('Market Trend Bias', style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)),
            const SizedBox(height: 8),
            Row(
              children: [
                _buildTrendButton('Bullish (↗)', 'bullish', Colors.green),
                const SizedBox(width: 8),
                _buildTrendButton('Bearish (↘)', 'bearish', Colors.red),
                const SizedBox(width: 8),
                _buildTrendButton('Volatile (⇄)', 'volatile', Colors.amber),
              ],
            ),
            const SizedBox(height: 20),
            // Ticking Speed Slider
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                const Text('Tick Frequency', style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)),
                Text(
                  'Every ${_tickSpeedSeconds.toStringAsFixed(1)}s',
                  style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.primary, fontWeight: FontWeight.bold),
                ),
              ],
            ),
            Slider(
              min: 0.2,
              max: 3.0,
              divisions: 28,
              value: _tickSpeedSeconds,
              onChanged: (val) {
                setState(() {
                  _tickSpeedSeconds = val;
                  _startTicker();
                });
              },
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildTrendButton(String label, String value, Color activeColor) {
    final isSelected = _marketTrend == value;
    return Expanded(
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          elevation: isSelected ? 2 : 0,
          backgroundColor: isSelected ? activeColor : Theme.of(context).scaffoldBackgroundColor,
          foregroundColor: isSelected ? Colors.white : Theme.of(context).colorScheme.onSurface,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10),
            side: BorderSide(
              color: isSelected ? Colors.transparent : (Theme.of(context).dividerTheme.color ?? const Color(0xFF1E293B)),
            ),
          ),
          padding: const EdgeInsets.symmetric(vertical: 12),
        ),
        onPressed: () {
          setState(() {
            _marketTrend = value;
          });
        },
        child: Text(label, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.bold)),
      ),
    );
  }

  Widget _buildBigTickerPanel(
    Map<String, dynamic> stock,
    bool isPositive,
    double priceChange,
    double percentChange,
    bool isDark,
  ) {
    final gradientColors = isDark
        ? [const Color(0xFF161C2C), const Color(0xFF0F1424)]
        : [const Color(0xFFF1F5F9), const Color(0xFFE2E8F0)];
    final borderCol = isDark ? const Color(0xFF1E293B) : const Color(0xFFE2E8F0);

    return Container(
      padding: const EdgeInsets.all(28),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: gradientColors,
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(24),
        border: Border.all(
          color: isPositive ? Colors.green.withValues(alpha: 0.3) : Colors.red.withValues(alpha: 0.3),
          width: 2.0,
        ),
        boxShadow: [
          BoxShadow(
            color: (isPositive ? Colors.green : Colors.red).withValues(alpha: 0.08),
            blurRadius: 20,
            spreadRadius: 1,
          )
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // Ticker & Name
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    stock['name'].toUpperCase(),
                    style: TextStyle(
                      fontSize: 11,
                      fontWeight: FontWeight.bold,
                      color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                    ),
                  ),
                  const SizedBox(height: 4),
                  Row(
                    children: [
                      Text(
                        stock['symbol'],
                        style: const TextStyle(fontSize: 26, fontWeight: FontWeight.w900, letterSpacing: -0.5),
                      ),
                      const SizedBox(width: 8),
                      Container(
                        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                        decoration: BoxDecoration(
                          color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.1),
                          borderRadius: BorderRadius.circular(6),
                        ),
                        child: const Text(
                          'NASDAQ',
                          style: TextStyle(fontSize: 9, fontWeight: FontWeight.bold, letterSpacing: 0.5),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
              Icon(
                isPositive ? Icons.trending_up_rounded : Icons.trending_down_rounded,
                color: isPositive ? Colors.greenAccent : Colors.redAccent,
                size: 36,
              ),
            ],
          ),
          const SizedBox(height: 28),

          // LARGE FLIP COUNTER
          Center(
            child: AnimatedFlipCounter.usd(
              value: stock['price'],
              textStyle: TextStyle(
                fontSize: 56,
                fontWeight: FontWeight.w900,
                color: Theme.of(context).colorScheme.onSurface,
                letterSpacing: -1.0,
              ),
              increasingColor: Colors.greenAccent,
              decreasingColor: Colors.redAccent,
            ),
          ),
          const SizedBox(height: 12),

          // Price change badges
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
                decoration: BoxDecoration(
                  color: isPositive ? Colors.green.withValues(alpha: 0.15) : Colors.red.withValues(alpha: 0.15),
                  borderRadius: BorderRadius.circular(12),
                  border: Border.all(
                    color: isPositive ? Colors.green.withValues(alpha: 0.3) : Colors.red.withValues(alpha: 0.3),
                    width: 1.0,
                  ),
                ),
                child: Row(
                  children: [
                    Icon(
                      isPositive ? Icons.arrow_upward_rounded : Icons.arrow_downward_rounded,
                      color: isPositive ? Colors.green : Colors.red,
                      size: 14,
                    ),
                    const SizedBox(width: 4),
                    Text(
                      '${isPositive ? "+" : ""}${priceChange.toStringAsFixed(2)} (${isPositive ? "+" : ""}${percentChange.toStringAsFixed(2)}%)',
                      style: TextStyle(
                        fontSize: 12,
                        fontWeight: FontWeight.bold,
                        color: isPositive ? Colors.green : Colors.red,
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
          const SizedBox(height: 24),

          // Ticker stats row
          Divider(color: borderCol),
          const SizedBox(height: 12),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              _buildStockStat('Daily Vol', stock['volume']),
              _buildStockStat('24h High', '\$${(stock['high'] as double).toStringAsFixed(2)}'),
              _buildStockStat('24h Low', '\$${(stock['low'] as double).toStringAsFixed(2)}'),
            ],
          ),
        ],
      ),
    );
  }

  Widget _buildStockStat(String title, String value) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: TextStyle(fontSize: 10, color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.4)),
        ),
        const SizedBox(height: 4),
        Text(
          value,
          style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
        ),
      ],
    );
  }

  Widget _buildOrderTradingPanel(
    Map<String, dynamic> stock,
    bool isDark,
    Color bg,
    Color border,
  ) {
    final symbol = stock['symbol'] as String;
    final price = stock['price'] as double;
    final totalCost = price * _sharesCount;

    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(
              'ORDER EXECUTION TERMINAL',
              style: TextStyle(
                fontSize: 11,
                fontWeight: FontWeight.bold,
                color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
                letterSpacing: 1,
              ),
            ),
            const SizedBox(height: 16),

            // Trade Type selector
            Row(
              children: [
                Expanded(
                  child: GestureDetector(
                    onTap: () => setState(() => _tradingType = 'BUY'),
                    child: Container(
                      padding: const EdgeInsets.symmetric(vertical: 12),
                      decoration: BoxDecoration(
                        color: _tradingType == 'BUY' ? Colors.green : bg,
                        borderRadius: const BorderRadius.horizontal(left: Radius.circular(10)),
                        border: Border.all(color: _tradingType == 'BUY' ? Colors.green : border),
                      ),
                      child: Center(
                        child: Text(
                          'BUY (LONG)',
                          style: TextStyle(
                            fontSize: 12,
                            fontWeight: FontWeight.bold,
                            color: _tradingType == 'BUY' ? Colors.white : Theme.of(context).colorScheme.onSurface,
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
                Expanded(
                  child: GestureDetector(
                    onTap: () => setState(() => _tradingType = 'SELL'),
                    child: Container(
                      padding: const EdgeInsets.symmetric(vertical: 12),
                      decoration: BoxDecoration(
                        color: _tradingType == 'SELL' ? Colors.red : bg,
                        borderRadius: const BorderRadius.horizontal(right: Radius.circular(10)),
                        border: Border.all(color: _tradingType == 'SELL' ? Colors.red : border),
                      ),
                      child: Center(
                        child: Text(
                          'SELL (SHORT)',
                          style: TextStyle(
                            fontSize: 12,
                            fontWeight: FontWeight.bold,
                            color: _tradingType == 'SELL' ? Colors.white : Theme.of(context).colorScheme.onSurface,
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 20),

            // Stepper and Total Row
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('Quantity (Shares)', style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold)),
                    const SizedBox(height: 6),
                    Row(
                      children: [
                        IconButton.filledTonal(
                          icon: const Icon(Icons.remove, size: 14),
                          onPressed: _sharesCount > 1
                              ? () => setState(() => _sharesCount--)
                              : null,
                        ),
                        Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 16),
                          child: Text(
                            '$_sharesCount',
                            style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                          ),
                        ),
                        IconButton.filledTonal(
                          icon: const Icon(Icons.add, size: 14),
                          onPressed: () => setState(() => _sharesCount++),
                        ),
                      ],
                    ),
                  ],
                ),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: [
                    const Text('Estimated Cost', style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold)),
                    const SizedBox(height: 8),
                    // Flip Counter for Total Cost
                    AnimatedFlipCounter.usd(
                      value: totalCost,
                      textStyle: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.w900,
                        color: Theme.of(context).colorScheme.primary,
                      ),
                    ),
                  ],
                ),
              ],
            ),
            const SizedBox(height: 24),

            // Submit Button
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                backgroundColor: _tradingType == 'BUY' ? Colors.green : Colors.red,
                foregroundColor: Colors.white,
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
                padding: const EdgeInsets.symmetric(vertical: 16),
              ),
              onPressed: _executeTrade,
              child: Text(
                'EXECUTE $_tradingType ORDER FOR $symbol',
                style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13, letterSpacing: 0.5),
              ),
            ),
            const SizedBox(height: 20),

            // Trade Console Log
            const Divider(),
            const SizedBox(height: 10),
            const Text(
              'ORDER CONFIRMATION JOURNAL',
              style: TextStyle(fontSize: 9, fontWeight: FontWeight.bold, letterSpacing: 0.5, color: Colors.grey),
            ),
            const SizedBox(height: 10),
            Container(
              height: 110,
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.black,
                borderRadius: BorderRadius.circular(10),
              ),
              child: _tradeHistory.isEmpty
                  ? const Center(
                      child: Text(
                        'No order transactions executed.',
                        style: TextStyle(fontFamily: 'Courier', fontSize: 10, color: Colors.grey),
                      ),
                    )
                  : ListView.builder(
                      controller: _tradeScrollController,
                      itemCount: _tradeHistory.length,
                      itemBuilder: (context, index) {
                        return Padding(
                          padding: const EdgeInsets.only(bottom: 4),
                          child: Text(
                            _tradeHistory[index],
                            style: const TextStyle(
                              fontFamily: 'Courier',
                              fontSize: 10,
                              color: Colors.greenAccent,
                            ),
                          ),
                        );
                      },
                    ),
            ),
          ],
        ),
      ),
    );
  }
}
2
likes
160
points
--
downloads
screenshot

Documentation

Documentation
API reference

Publisher

verified publisheritsxhadi.fun

Weekly Downloads

An implicit animation widget that flips from one number to another, with support for customize styles, decimals and negative values.

Repository (GitHub)
View/report issues

Funding

Consider supporting this project:

www.paypal.com

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flip_counter_plus