flutter_easy_timer 0.0.3 copy "flutter_easy_timer: ^0.0.3" to clipboard
flutter_easy_timer: ^0.0.3 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),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
3
likes
160
points
141
downloads

Publisher

unverified uploader

Weekly Downloads

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

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_easy_timer