flutter_easy_timer 0.0.5 copy "flutter_easy_timer: ^0.0.5" to clipboard
flutter_easy_timer: ^0.0.5 copied to clipboard

A beautiful, customizable countdown timer widget with glass UI support for Flutter applications.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_easy_timer/flutter_easy_timer.dart';
import 'package:flutter_animate/flutter_animate.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Easy Timer',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.deepPurple,
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
      ),
      home: const DemoPage(),
    );
  }
}

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

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  int _currentIndex = 0;
  final PageController _pageController = PageController();

  final List<Widget> _pages = const [
    MinimalTimerDemo(),
    WorkoutTimerDemo(),
    GlassMorphismDemo(),
    InteractiveTimerDemo(),
  ];

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBody: true,
      body: PageView(
        controller: _pageController,
        onPageChanged: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
        children: _pages,
      ),
      bottomNavigationBar:
          Container(
                margin: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(30),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withValues(alpha: 0.3),
                      blurRadius: 20,
                      offset: const Offset(0, 10),
                    ),
                  ],
                ),
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(30),
                  child: NavigationBar(
                    selectedIndex: _currentIndex,
                    onDestinationSelected: (index) {
                      setState(() {
                        _currentIndex = index;
                      });
                      _pageController.animateToPage(
                        index,
                        duration: const Duration(milliseconds: 400),
                        curve: Curves.easeInOut,
                      );
                    },
                    elevation: 0,
                    backgroundColor: Colors.grey.shade900.withValues(
                      alpha: 0.9,
                    ),
                    indicatorColor: Colors.deepPurple.withValues(alpha: 0.3),
                    destinations: const [
                      NavigationDestination(
                        icon: Icon(Icons.timer_outlined),
                        selectedIcon: Icon(Icons.timer),
                        label: 'Minimal',
                      ),
                      NavigationDestination(
                        icon: Icon(Icons.fitness_center_outlined),
                        selectedIcon: Icon(Icons.fitness_center),
                        label: 'Workout',
                      ),
                      NavigationDestination(
                        icon: Icon(Icons.blur_circular_outlined),
                        selectedIcon: Icon(Icons.blur_circular),
                        label: 'Glass',
                      ),
                      NavigationDestination(
                        icon: Icon(Icons.touch_app_outlined),
                        selectedIcon: Icon(Icons.touch_app),
                        label: 'Interactive',
                      ),
                    ],
                  ),
                ),
              )
              .animate()
              .slideY(begin: 1, end: 0, duration: 600.ms, curve: Curves.easeOut)
              .fadeIn(duration: 400.ms),
    );
  }
}

// 1. Minimal Modern Timer
class MinimalTimerDemo extends StatelessWidget {
  const MinimalTimerDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [
            const Color(0xFF0F0F0F),
            const Color(0xFF1A1A1A),
            Colors.deepPurple.shade900.withValues(alpha: 0.3),
          ],
        ),
      ),
      child: SafeArea(
        child: Column(
          children: [
            const SizedBox(height: 60),
            Text(
                  'Focus Time',
                  style: Theme.of(context).textTheme.headlineMedium?.copyWith(
                    color: Colors.white,
                    fontWeight: FontWeight.w300,
                    letterSpacing: 2,
                  ),
                )
                .animate()
                .fadeIn(duration: 800.ms, delay: 200.ms)
                .slideY(begin: -0.3, end: 0),
            const SizedBox(height: 20),
            Text(
                  'Deep work session',
                  style: Theme.of(context).textTheme.bodyLarge?.copyWith(
                    color: Colors.white.withValues(alpha: 0.5),
                  ),
                )
                .animate()
                .fadeIn(duration: 800.ms, delay: 400.ms)
                .slideY(begin: -0.3, end: 0),
            const Spacer(),
            FlutterEasyTimerWidget(
                  durationSeconds: 150, // 25 minutes
                  size: 280,
                  timerColor: Colors.deepPurple.shade300,
                  boundaryColor: Colors.white.withValues(alpha: 0.1),
                  boundaryWidth: 1.5,
                  textColor: Colors.white,
                  timerFontSize: 64,
                  durationTextColor: Colors.white.withValues(alpha: 0.4),
                  durationFontSize: 24,
                  onFinished: () {
                    debugPrint('Focus session complete!');
                  },
                )
                .animate()
                .scale(
                  begin: const Offset(0.8, 0.8),
                  end: const Offset(1, 1),
                  duration: 800.ms,
                  delay: 300.ms,
                  curve: Curves.easeOutBack,
                )
                .fadeIn(duration: 600.ms, delay: 300.ms),
            const Spacer(),
            Container(
                  margin: const EdgeInsets.symmetric(horizontal: 40),
                  padding: const EdgeInsets.all(20),
                  decoration: BoxDecoration(
                    color: Colors.white.withValues(alpha: 0.05),
                    borderRadius: BorderRadius.circular(20),
                    border: Border.all(
                      color: Colors.white.withValues(alpha: 0.1),
                    ),
                  ),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    children: [
                      _buildStatItem(
                        'Sessions',
                        '3',
                        Icons.check_circle_outline,
                      ),
                      _buildStatItem(
                        'Streak',
                        '7 days',
                        Icons.local_fire_department_outlined,
                      ),
                      _buildStatItem('Total', '4.5h', Icons.timer_outlined),
                    ],
                  ),
                )
                .animate()
                .fadeIn(duration: 800.ms, delay: 600.ms)
                .slideY(begin: 0.3, end: 0),
            const SizedBox(height: 40),
          ],
        ),
      ),
    );
  }

  Widget _buildStatItem(String label, String value, IconData icon) {
    return Column(
      children: [
        Icon(icon, color: Colors.white.withValues(alpha: 0.6), size: 24),
        const SizedBox(height: 8),
        Text(
          value,
          style: const TextStyle(
            color: Colors.white,
            fontSize: 16,
            fontWeight: FontWeight.bold,
          ),
        ),
        const SizedBox(height: 4),
        Text(
          label,
          style: TextStyle(
            color: Colors.white.withValues(alpha: 0.5),
            fontSize: 12,
          ),
        ),
      ],
    );
  }
}

// 2. Workout Timer with Vibrant Colors
class WorkoutTimerDemo extends StatelessWidget {
  const WorkoutTimerDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [
            const Color(0xFFFF6B6B),
            const Color(0xFFFF8E53),
            const Color(0xFFFFA07A),
          ],
        ),
      ),
      child: SafeArea(
        child: Column(
          children: [
            const SizedBox(height: 10),
            Container(
                  padding: const EdgeInsets.symmetric(
                    horizontal: 16,
                    vertical: 10,
                  ),
                  decoration: BoxDecoration(
                    color: Colors.white.withValues(alpha: 0.2),
                    borderRadius: BorderRadius.circular(30),
                  ),
                  child: const Text(
                    '💪 HIIT WORKOUT',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                      letterSpacing: 1.5,
                    ),
                  ),
                )
                .animate()
                .fadeIn(duration: 600.ms, delay: 200.ms)
                .scale(begin: const Offset(0.8, 0.8), end: const Offset(1, 1)),
            const Spacer(),
            Column(
              children: [
                const Text(
                      'Round 1',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 20,
                        fontWeight: FontWeight.w300,
                      ),
                    )
                    .animate(onPlay: (controller) => controller.repeat())
                    .fadeIn(duration: 1000.ms)
                    .then()
                    .fadeOut(duration: 1000.ms),
                const SizedBox(height: 16),
                FlutterEasyTimerWidget(
                      durationSeconds: 45,
                      title: 'BURPEES',
                      size: 280,
                      timerColor: Colors.white,
                      boundaryColor: Colors.white,
                      boundaryWidth: 6.0,
                      textColor: Colors.white,
                      titleColor: Colors.white,
                      durationTextColor: Colors.white.withValues(alpha: 0.8),
                      timerFontSize: 56,
                      titleFontSize: 26,
                      durationFontSize: 28,
                      onFinished: () {
                        debugPrint('Exercise complete!');
                      },
                    )
                    .animate()
                    .scale(
                      begin: const Offset(0.9, 0.9),
                      end: const Offset(1, 1),
                      duration: 600.ms,
                      curve: Curves.easeOut,
                    )
                    .fadeIn(duration: 400.ms),
              ],
            ),
            const Spacer(),
            Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    _buildWorkoutButton(Icons.skip_previous, 'Previous'),
                    const SizedBox(width: 20),
                    _buildWorkoutButton(Icons.pause, 'Pause', isPrimary: true),
                    const SizedBox(width: 20),
                    _buildWorkoutButton(Icons.skip_next, 'Next'),
                  ],
                )
                .animate()
                .fadeIn(duration: 800.ms, delay: 400.ms)
                .slideY(begin: 0.5, end: 0),
            const SizedBox(height: 60),
          ],
        ),
      ),
    );
  }

  Widget _buildWorkoutButton(
    IconData icon,
    String label, {
    bool isPrimary = false,
  }) {
    return Column(
      children: [
        Container(
          width: isPrimary ? 70 : 60,
          height: isPrimary ? 70 : 60,
          decoration: BoxDecoration(
            color: isPrimary
                ? Colors.white
                : Colors.white.withValues(alpha: 0.3),
            shape: BoxShape.circle,
            boxShadow: isPrimary
                ? [
                    BoxShadow(
                      color: Colors.white.withValues(alpha: 0.3),
                      blurRadius: 20,
                      spreadRadius: 2,
                    ),
                  ]
                : null,
          ),
          child: Icon(
            icon,
            color: isPrimary ? const Color(0xFFFF6B6B) : Colors.white,
            size: isPrimary ? 32 : 28,
          ),
        ),
        const SizedBox(height: 8),
        Text(
          label,
          style: TextStyle(
            color: Colors.white.withValues(alpha: 0.9),
            fontSize: 12,
            fontWeight: FontWeight.w500,
          ),
        ),
      ],
    );
  }
}

// 3. GlassMorphism Premium Design
class GlassMorphismDemo extends StatelessWidget {
  const GlassMorphismDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [Color(0xFF667eea), Color(0xFF764ba2), Color(0xFFf093fb)],
        ),
      ),
      child: Stack(
        children: [
          // Animated background circles
          Positioned(
            top: -100,
            right: -100,
            child:
                Container(
                      width: 300,
                      height: 300,
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        color: Colors.white.withValues(alpha: 0.1),
                      ),
                    )
                    .animate(onPlay: (controller) => controller.repeat())
                    .scale(
                      begin: const Offset(1, 1),
                      end: const Offset(1.2, 1.2),
                      duration: 3000.ms,
                    )
                    .then()
                    .scale(
                      begin: const Offset(1.2, 1.2),
                      end: const Offset(1, 1),
                      duration: 3000.ms,
                    ),
          ),
          Positioned(
            bottom: -150,
            left: -150,
            child:
                Container(
                      width: 400,
                      height: 400,
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        color: Colors.white.withValues(alpha: 0.1),
                      ),
                    )
                    .animate(onPlay: (controller) => controller.repeat())
                    .scale(
                      begin: const Offset(1, 1),
                      end: const Offset(1.3, 1.3),
                      duration: 4000.ms,
                    )
                    .then()
                    .scale(
                      begin: const Offset(1.3, 1.3),
                      end: const Offset(1, 1),
                      duration: 4000.ms,
                    ),
          ),
          SafeArea(
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Icon(
                        Icons.self_improvement,
                        color: Colors.white,
                        size: 48,
                      )
                      .animate()
                      .fadeIn(duration: 800.ms)
                      .scale(
                        begin: const Offset(0.5, 0.5),
                        end: const Offset(1, 1),
                      ),
                  const SizedBox(height: 20),
                  const Text(
                        'Meditation Session',
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 32,
                          fontWeight: FontWeight.w300,
                          letterSpacing: 1,
                        ),
                      )
                      .animate()
                      .fadeIn(duration: 800.ms, delay: 200.ms)
                      .slideY(begin: -0.2, end: 0),
                  const SizedBox(height: 60),
                  FlutterEasyTimerWidget(
                        durationSeconds: 90, // 10 minutes
                        enableGlassUI: true,
                        size: 280,
                        glassBlurIntensity: 20.0,
                        glassOpacity: 0.25,
                        glassGradientColors: [
                          Colors.white.withValues(alpha: 0.25),
                          Colors.white.withValues(alpha: 0.1),
                        ],
                        timerColor: Colors.white,
                        boundaryColor: Colors.white.withValues(alpha: 0.4),
                        boundaryWidth: 3.0,
                        textColor: Colors.white,
                        timerFontSize: 68,
                        durationTextColor: Colors.white,
                        durationFontSize: 26,
                      )
                      .animate()
                      .scale(
                        begin: const Offset(0.85, 0.85),
                        end: const Offset(1, 1),
                        duration: 1000.ms,
                        delay: 300.ms,
                        curve: Curves.easeOutBack,
                      )
                      .fadeIn(duration: 800.ms, delay: 300.ms),
                  const SizedBox(height: 60),
                  Container(
                        padding: const EdgeInsets.symmetric(
                          horizontal: 32,
                          vertical: 16,
                        ),
                        decoration: BoxDecoration(
                          color: Colors.white.withValues(alpha: 0.2),
                          borderRadius: BorderRadius.circular(30),
                          border: Border.all(
                            color: Colors.white.withValues(alpha: 0.3),
                          ),
                        ),
                        child: const Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Icon(
                              Icons.music_note,
                              color: Colors.white,
                              size: 20,
                            ),
                            SizedBox(width: 12),
                            Text(
                              'Calm Piano Mix',
                              style: TextStyle(
                                color: Colors.white,
                                fontSize: 16,
                                fontWeight: FontWeight.w500,
                              ),
                            ),
                          ],
                        ),
                      )
                      .animate()
                      .fadeIn(duration: 800.ms, delay: 600.ms)
                      .slideY(begin: 0.3, end: 0),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// 4. Interactive Timer with Manual Control
class InteractiveTimerDemo extends StatefulWidget {
  const InteractiveTimerDemo({super.key});

  @override
  State<InteractiveTimerDemo> createState() => _InteractiveTimerDemoState();
}

class _InteractiveTimerDemoState extends State<InteractiveTimerDemo> {
  final GlobalKey _timerKey = GlobalKey();
  bool _isStarted = false;
  int _selectedMinutes = 5;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              const Color(0xFF1e3c72),
              const Color(0xFF2a5298),
              Colors.cyan.shade700,
            ],
          ),
        ),
        child: SafeArea(
          child: SingleChildScrollView(
            physics: const BouncingScrollPhysics(),
            padding: const EdgeInsets.only(bottom: 40),
            child: Column(
              children: [
                const SizedBox(height: 40),

                /// Title
                Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: const [
                        Icon(Icons.alarm, color: Colors.white, size: 28),
                        SizedBox(width: 12),
                        Text(
                          'Custom Timer',
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 28,
                            fontWeight: FontWeight.w600,
                          ),
                        ),
                      ],
                    )
                    .animate()
                    .fadeIn(duration: 600.ms, delay: 100.ms)
                    .slideY(begin: -0.3, end: 0),

                const SizedBox(height: 60),

                /// Duration Selector
                if (!_isStarted)
                  Column(
                        children: [
                          const Text(
                            'Select Duration',
                            style: TextStyle(
                              color: Colors.white,
                              fontSize: 18,
                              fontWeight: FontWeight.w300,
                            ),
                          ),
                          const SizedBox(height: 24),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [1, 5, 10, 15, 30].map((minutes) {
                              final isSelected = _selectedMinutes == minutes;
                              return GestureDetector(
                                onTap: () {
                                  setState(() {
                                    _selectedMinutes = minutes;
                                  });
                                },
                                child:
                                    Container(
                                          margin: const EdgeInsets.symmetric(
                                            horizontal: 8,
                                          ),
                                          width: 60,
                                          height: 60,
                                          decoration: BoxDecoration(
                                            color: isSelected
                                                ? Colors.white
                                                : Colors.white.withValues(
                                                    alpha: 0.2,
                                                  ),
                                            borderRadius: BorderRadius.circular(
                                              15,
                                            ),
                                            border: Border.all(
                                              color: Colors.white.withValues(
                                                alpha: 0.3,
                                              ),
                                              width: 2,
                                            ),
                                            boxShadow: isSelected
                                                ? [
                                                    BoxShadow(
                                                      color: Colors.white
                                                          .withValues(
                                                            alpha: 0.3,
                                                          ),
                                                      blurRadius: 15,
                                                      spreadRadius: 2,
                                                    ),
                                                  ]
                                                : null,
                                          ),
                                          child: Center(
                                            child: Text(
                                              '$minutes',
                                              style: TextStyle(
                                                color: isSelected
                                                    ? Colors.cyan.shade700
                                                    : Colors.white,
                                                fontSize: 20,
                                                fontWeight: FontWeight.bold,
                                              ),
                                            ),
                                          ),
                                        )
                                        .animate()
                                        .scale(
                                          begin: const Offset(0.8, 0.8),
                                          end: const Offset(1, 1),
                                          duration: 400.ms,
                                        )
                                        .fadeIn(duration: 400.ms),
                              );
                            }).toList(),
                          ),
                          const SizedBox(height: 16),
                          const Text(
                            'minutes',
                            style: TextStyle(
                              color: Colors.white70,
                              fontSize: 14,
                            ),
                          ),
                        ],
                      )
                      .animate()
                      .fadeIn(duration: 600.ms, delay: 300.ms)
                      .slideY(begin: -0.2, end: 0),

                const SizedBox(height: 40),

                /// Timer
                FlutterEasyTimerWidget(
                      key: _timerKey,
                      durationSeconds: _selectedMinutes * 60,
                      autoStart: false,
                      size: 320,
                      timerColor: Colors.cyanAccent,
                      boundaryColor: Colors.white.withValues(alpha: 0.3),
                      boundaryWidth: 4.0,
                      textColor: Colors.white,
                      timerFontSize: 64,
                      durationTextColor: Colors.white.withValues(alpha: 0.7),
                      durationFontSize: 28,
                      onStart: () => setState(() => _isStarted = true),
                      onFinished: () => setState(() => _isStarted = false),
                    )
                    .animate()
                    .scale(
                      begin: const Offset(0.9, 0.9),
                      end: const Offset(1, 1),
                      duration: 800.ms,
                      curve: Curves.easeOutBack,
                    )
                    .fadeIn(duration: 600.ms),

                const SizedBox(height: 40),

                /// Start Button
                Container(
                  width: 200,
                  height: 200,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    gradient: RadialGradient(
                      colors: [
                        _isStarted
                            ? Colors.red.withValues(alpha: 0.3)
                            : Colors.green.withValues(alpha: 0.3),
                        Colors.transparent,
                      ],
                    ),
                  ),
                  child: Center(
                    child: GestureDetector(
                      onTap: _isStarted
                          ? null
                          : () {
                              (_timerKey.currentState as dynamic)?.start();
                            },
                      child: Container(
                        width: 140,
                        height: 140,
                        decoration: BoxDecoration(
                          shape: BoxShape.circle,
                          color: _isStarted
                              ? Colors.white.withValues(alpha: 0.2)
                              : Colors.white,
                          boxShadow: _isStarted
                              ? null
                              : [
                                  BoxShadow(
                                    color: Colors.white.withValues(alpha: 0.4),
                                    blurRadius: 30,
                                    spreadRadius: 5,
                                  ),
                                ],
                        ),
                        child: Icon(
                          _isStarted ? Icons.timer : Icons.play_arrow_rounded,
                          size: 60,
                          color: _isStarted
                              ? Colors.white
                              : Colors.cyan.shade700,
                        ),
                      ),
                    ),
                  ),
                ).animate().scale(
                  begin: const Offset(0.95, 0.95),
                  end: const Offset(1, 1),
                  duration: 600.ms,
                ),

                const SizedBox(height: 20),

                /// Status Text
                Text(
                  _isStarted ? 'Timer Running...' : 'Tap to Start',
                  style: TextStyle(
                    color: Colors.white.withValues(alpha: 0.8),
                    fontSize: 16,
                    fontWeight: FontWeight.w500,
                  ),
                ).animate().fadeIn(duration: 600.ms).slideY(begin: 0.2, end: 0),

                const SizedBox(height: 40),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
5
likes
160
points
209
downloads

Documentation

API reference

Publisher

verified publisherinheritx.com

Weekly Downloads

A beautiful, customizable countdown timer widget with glass UI support for Flutter applications.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_easy_timer