如何在Flutter中实现秒表?求UI设计与动画添加指导
Flutter 秒表实现指南(含UI设计与动画)
一、UI布局调整
参考你提供的预期UI,在现有基础上补全控制区域、优化排版:
- 顶部日期文本保留现有样式,添加上下间距让布局更舒展
- 中间计时文本放大至48号字,强化视觉核心地位
- 底部用Row排列开始/暂停/重置按钮,设置圆角和间距,提升交互质感
二、核心计时逻辑实现
秒表需要实时更新状态,必须使用StatefulWidget配合Timer实现计时:
- 定义状态变量:
int _totalSeconds = 0; Timer? _timer; bool _isRunning = false;
- 编写计时控制方法:
// 切换开始/暂停状态 void _toggleTimer() { if (_isRunning) { _timer?.cancel(); } else { _timer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { _totalSeconds++; }); }); } setState(() { _isRunning = !_isRunning; }); } // 重置计时 void _resetTimer() { _timer?.cancel(); setState(() { _totalSeconds = 0; _isRunning = false; }); } // 格式化时间为 HH:MM:SS 格式 String _formatTime(int seconds) { int hours = seconds ~/ 3600; int minutes = (seconds % 3600) ~/ 60; int secs = seconds % 60; return "${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${secs.toString().padLeft(2, '0')}"; }
三、添加动画效果
1. 计时数字切换动画
用AnimatedSwitcher实现数字变化时的淡入淡出效果,替换原有静态Text:
AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (Widget child, Animation<double> animation) { return FadeTransition(opacity: animation, child: child); }, child: Text( _formatTime(_totalSeconds), key: ValueKey<int>(_totalSeconds), style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold), ), )
2. 按钮交互动画
使用InkWell包裹按钮,自带水波纹点击动画,同时设置按钮样式:
InkWell( onTap: _toggleTimer, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), color: _isRunning ? Colors.red : Colors.green, child: Text( _isRunning ? '暂停' : '开始', style: const TextStyle(color: Colors.white, fontSize: 16), ), ), )
完整代码示例
import 'dart:async'; import 'package:flutter/material.dart'; class StopwatchPage extends StatefulWidget { const StopwatchPage({super.key}); @override State<StopwatchPage> createState() => _StopwatchPageState(); } class _StopwatchPageState extends State<StopwatchPage> { int _totalSeconds = 0; Timer? _timer; bool _isRunning = false; void _toggleTimer() { if (_isRunning) { _timer?.cancel(); } else { _timer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { _totalSeconds++; }); }); } setState(() { _isRunning = !_isRunning; }); } void _resetTimer() { _timer?.cancel(); setState(() { _totalSeconds = 0; _isRunning = false; }); } String _formatTime(int seconds) { int hours = seconds ~/ 3600; int minutes = (seconds % 3600) ~/ 60; int secs = seconds % 60; return "${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${secs.toString().padLeft(2, '0')}"; } @override void dispose() { _timer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('秒表')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 日期显示 Text( DateTime.now().toLocal().toString().split('.')[0], style: const TextStyle(fontSize: 18), ), const SizedBox(height: 32), // 带动画的计时数字 AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (Widget child, Animation<double> animation) { return FadeTransition(opacity: animation, child: child); }, child: Text( _formatTime(_totalSeconds), key: ValueKey<int>(_totalSeconds), style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold), ), ), const SizedBox(height: 48), // 控制按钮组 Row( mainAxisAlignment: MainAxisAlignment.center, gap: 16, children: [ InkWell( onTap: _toggleTimer, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), color: _isRunning ? Colors.red : Colors.green, child: Text( _isRunning ? '暂停' : '开始', style: const TextStyle(color: Colors.white, fontSize: 16), ), ), ), InkWell( onTap: _resetTimer, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), color: Colors.grey, child: const Text( '重置', style: TextStyle(color: Colors.white, fontSize: 16), ), ), ), ], ), ], ), ), ); } } // 运行示例 void main() { runApp(const MaterialApp(home: StopwatchPage())); }
内容的提问来源于stack exchange,提问作者Farhan Sadiq
相关产品推荐
相关产品推荐

