countman 0.2.2 copy "countman: ^0.2.2" to clipboard
countman: ^0.2.2 copied to clipboard

High-performance counter, countdown & stopwatch animations for Flutter. One shared vsync ticker drives every instance — no per-widget timers, built for high concurrency.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'counter_page.dart';
import 'countdown_page.dart';
import 'elapsed_page.dart';
import 'provider_page.dart';
import 'card_demo_page.dart';
import 'countdown_demo_page.dart';
import 'perf_page.dart';
// Web can't compile benchmark_page (dart:io); fall back to a stub there.
//
// web 无法编译 benchmark_page(dart:io),此时回退到桩。
import 'benchmark_stub.dart' if (dart.library.io) 'benchmark_page.dart';
import 'digit_test_page.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Countman Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF5C6BC0)),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF5C6BC0),
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
      ),
      themeMode: ThemeMode.system,
      initialRoute: '/',
      routes: {
        '/': (_) => const HomePage(),
        '/counter': (_) => const CounterPage(),
        '/countdown': (_) => const CountdownPage(),
        '/elapsed': (_) => const ElapsedPage(),
        '/providers': (_) => const ProviderPage(),
        '/card': (_) => const CardDemoPage(),
        '/countdown-demo': (_) => const CountdownDemoPage(),
        '/perf': (_) => const PerfPage(),
        '/benchmark': (_) => const BenchmarkPage(),
        '/digit-test': (_) => const DigitTestPage(),
      },
    );
  }
}

// ── Home ──────────────────────────────────────────────────────────────────────

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Countman')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _PluginCard(
            icon: Icons.timer_outlined,
            title: 'Counter',
            subtitle: 'TextCounter · CounterBuilder · OdometerCounter\n'
                'RingCounter · BarCounter · AnimatedCounter',
            route: '/counter',
            color: const Color(0xFF5C6BC0),
          ),
          const SizedBox(height: 12),
          _PluginCard(
            icon: Icons.hourglass_empty_rounded,
            title: 'Countdown',
            subtitle: 'CountdownBuilder · TextCountdown · RingCountdown\n'
                'BarCountdown · CardCountdown',
            route: '/countdown',
            color: const Color(0xFF26A69A),
          ),
          const SizedBox(height: 12),
          _PluginCard(
            icon: Icons.access_time_rounded,
            title: 'Elapsed',
            subtitle: 'TextElapsed · ElapsedBuilder · ElapsedProvider',
            route: '/elapsed',
            color: const Color(0xFFEF5350),
          ),
          const SizedBox(height: 24),
          Padding(
            padding: const EdgeInsets.only(left: 4, bottom: 4),
            child: Text('进阶 / More',
                style: Theme.of(context)
                    .textTheme
                    .titleSmall
                    ?.copyWith(fontWeight: FontWeight.w700)),
          ),
          const SizedBox(height: 8),
          _PluginCard(
            icon: Icons.account_tree_rounded,
            title: 'Providers',
            subtitle: 'CountmanProvider (聚合三家族) · CardCountdownProvider',
            route: '/providers',
            color: const Color(0xFF7E57C2),
          ),
          const SizedBox(height: 12),
          _PluginCard(
            icon: Icons.style_rounded,
            title: 'CardCountdown 效果矩阵',
            subtitle: 'calendar / slide / flip · scale / opacity / perspective',
            route: '/card',
            color: const Color(0xFF26A69A),
          ),
          const SizedBox(height: 12),
          _PluginCard(
            icon: Icons.hourglass_bottom_rounded,
            title: 'Countdown 全家速览',
            subtitle: 'to 的四种输入 · formatter · controller · 并发',
            route: '/countdown-demo',
            color: const Color(0xFF26A69A),
          ),
          const SizedBox(height: 12),
          _PluginCard(
            icon: Icons.speed_rounded,
            title: '性能压测',
            subtitle: '并发 AnimatedCounter / Odometer, 0→10 亿 / 10s',
            route: '/perf',
            color: const Color(0xFF5C6BC0),
          ),
          const SizedBox(height: 12),
          _PluginCard(
            icon: Icons.insights_rounded,
            title: '基准对比',
            subtitle: 'countman vs slide_countdown / stop_watch_timer',
            route: '/benchmark',
            color: const Color(0xFF5C6BC0),
          ),
          const SizedBox(height: 12),
          _PluginCard(
            icon: Icons.bug_report_rounded,
            title: '数字调试 (onUpdate)',
            subtitle: 'AnimatedCounter / CounterBuilder 逐帧日志',
            route: '/digit-test',
            color: const Color(0xFFEF5350),
          ),
        ],
      ),
    );
  }
}

class _PluginCard extends StatelessWidget {
  const _PluginCard({
    required this.icon,
    required this.title,
    required this.subtitle,
    required this.route,
    required this.color,
  });

  final IconData icon;
  final String title;
  final String subtitle;
  final String route;
  final Color color;

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    return Card(
      elevation: 0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
        side: BorderSide(color: cs.outlineVariant),
      ),
      child: InkWell(
        borderRadius: BorderRadius.circular(16),
        onTap: () => Navigator.pushNamed(context, route),
        child: Padding(
          padding: const EdgeInsets.all(20),
          child: Row(
            children: [
              Container(
                width: 52,
                height: 52,
                decoration: BoxDecoration(
                  color: color.withValues(alpha: 0.12),
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Icon(icon, color: color, size: 26),
              ),
              const SizedBox(width: 16),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(title,
                        style: Theme.of(context)
                            .textTheme
                            .titleMedium
                            ?.copyWith(fontWeight: FontWeight.w700)),
                    const SizedBox(height: 4),
                    Text(subtitle,
                        style: Theme.of(context).textTheme.bodySmall?.copyWith(
                            color: cs.onSurface.withValues(alpha: 0.6))),
                  ],
                ),
              ),
              Icon(Icons.chevron_right_rounded,
                  color: cs.onSurface.withValues(alpha: 0.35)),
            ],
          ),
        ),
      ),
    );
  }
}
0
likes
150
points
37
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

High-performance counter, countdown & stopwatch animations for Flutter. One shared vsync ticker drives every instance — no per-widget timers, built for high concurrency.

Homepage
Repository (GitHub)
View/report issues

Topics

#counter #countdown #stopwatch #timer #animation

License

MIT (license)

Dependencies

flutter

More

Packages that depend on countman