Flutter定时任务全攻略:从基础到进阶的8种实现方式
在Flutter开发中,定时任务是常见的需求场景,如轮询请求、倒计时、延迟执行等。本文将全面介绍Flutter中实现定时任务的8种方法,并附上详细代码示例和最佳实践。选择合适的方式,并遵循最佳实践,可以创建出高效、稳定的定时功能。定时器在页面切换后仍运行 未处理页面生命周期 在deactivate中暂停,在activate中恢复。· 响应式UI:使用Stream.periodic + Stream
在Flutter开发中,定时任务是常见的需求场景,如轮询请求、倒计时、延迟执行等。本文将全面介绍Flutter中实现定时任务的8种方法,并附上详细代码示例和最佳实践。
📋 目录
- Timer类 - 最基础的定时器
- Future.delayed - 简单的延迟执行
- Stream.periodic - 响应式定时流
- AnimationController - 动画定时器
- Ticker - 帧同步定时器
- WorkManager - 后台定时任务
- cron - 复杂定时表达式
- 完整实战案例
- 性能优化与注意事项
- Timer类 - 最基础的定时器
1.1 基本用法
import 'dart:async';
/// 一次性定时器:3秒后执行
Timer(Duration(seconds: 3), () {
print('3秒后执行的代码');
});
/// 周期性定时器:每秒执行一次
Timer.periodic(Duration(seconds: 1), (timer) {
print('第${timer.tick}次执行'); // tick属性记录执行次数
// 执行5次后取消定时器
if (timer.tick >= 5) {
timer.cancel();
print('定时器已取消');
}
});
1.2 在StatefulWidget中的完整示例
import 'dart:async';
import 'package:flutter/material.dart';
class TimerExample extends StatefulWidget {
_TimerExampleState createState() => _TimerExampleState();
}
class _TimerExampleState extends State<TimerExample> {
Timer? _timer; // 定时器实例
int _counter = 0; // 计数器
bool _isRunning = false; // 定时器运行状态
void initState() {
super.initState();
// 页面加载后延迟2秒开始计时
Future.delayed(Duration(seconds: 2), _startTimer);
}
/// 启动定时器
void _startTimer() {
setState(() {
_isRunning = true;
_counter = 0;
});
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
_counter++;
});
// 10秒后自动停止
if (_counter >= 10) {
_stopTimer();
}
});
}
/// 停止定时器
void _stopTimer() {
_timer?.cancel();
setState(() {
_isRunning = false;
});
}
/// 暂停/继续定时器
void _toggleTimer() {
if (_isRunning) {
_stopTimer();
} else {
_startTimer();
}
}
void dispose() {
/// ⚠️ 重要:组件销毁时必须取消定时器,防止内存泄漏
_timer?.cancel();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Timer示例')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
/// 显示计时结果
Text('计时: $_counter 秒',
style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold)),
SizedBox(height: 30),
/// 控制按钮
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _toggleTimer,
child: Text(_isRunning ? '暂停' : '继续'),
),
SizedBox(width: 20),
ElevatedButton(
onPressed: _startTimer,
child: Text('重置'),
),
],
),
],
),
),
);
}
}
- Future.delayed - 简单的延迟执行
/// 基本延迟执行
Future.delayed(Duration(seconds: 2), () {
print('延迟2秒后执行');
});
/// 链式延迟执行 - 模拟多个延迟任务
Future<void> chainDelayedExample() async {
print('任务开始');
await Future.delayed(Duration(seconds: 1));
print('1秒后执行第一个任务');
await Future.delayed(Duration(milliseconds: 500));
print('0.5秒后执行第二个任务');
await Future.delayed(Duration(seconds: 2));
print('2秒后执行第三个任务');
print('所有延迟任务完成');
}
/// 在build方法中延迟更新UI
Future<void> delayedBuildUpdate() async {
// 等待数据加载
await Future.delayed(Duration(seconds: 3));
// 注意:这里需要检查mounted,防止组件已销毁
if (mounted) {
setState(() {
// 更新UI状态
});
}
}
- Stream.periodic - 响应式定时流
3.1 基本用法
import 'dart:async';
/// 创建每秒发射一个事件的流
final Stream<int> numberStream = Stream<int>.periodic(
Duration(seconds: 1), // 时间间隔
(count) => count + 1, // 每次发射的值
).take(10); // 只取前10个值
// 监听流
numberStream.listen((number) {
print('收到数字: $number');
}, onDone: () {
print('流已完成');
});
3.2 结合StreamBuilder构建响应式UI
import 'package:flutter/material.dart';
class StreamTimerExample extends StatelessWidget {
/// 创建倒计时流:从60秒到0秒
final Stream<int> countdownStream = Stream<int>.periodic(
Duration(seconds: 1),
(x) => 60 - x - 1,
).takeWhile((x) => x >= 0); // 条件过滤
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: countdownStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('准备开始...');
}
if (snapshot.hasError) {
return Text('错误: ${snapshot.error}');
}
final seconds = snapshot.data ?? 0;
return Column(
children: [
// 倒计时显示
Text('$seconds',
style: TextStyle(
fontSize: 72,
color: seconds < 10 ? Colors.red : Colors.black,
)),
// 进度条
LinearProgressIndicator(
value: seconds / 60,
),
// 状态文本
Text(
seconds > 0 ? '倒计时中...' : '时间到!',
style: TextStyle(
fontSize: 24,
color: seconds > 0 ? Colors.grey : Colors.green,
),
),
],
);
},
);
}
}
- AnimationController - 动画定时器
import 'package:flutter/material.dart';
class AnimationTimerExample extends StatefulWidget {
_AnimationTimerExampleState createState() => _AnimationTimerExampleState();
}
class _AnimationTimerExampleState extends State<AnimationTimerExample>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
void initState() {
super.initState();
/// 创建动画控制器,持续10秒
_controller = AnimationController(
duration: Duration(seconds: 10),
vsync: this, // 提供TickerProvider
);
/// 创建动画曲线
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);
/// 添加状态监听器
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
print('动画完成');
} else if (status == AnimationStatus.dismissed) {
print('动画重置');
}
});
/// 开始正向动画
_controller.forward();
}
/// 暂停动画
void _pauseAnimation() {
_controller.stop();
}
/// 继续动画
void _resumeAnimation() {
_controller.forward();
}
/// 重置动画
void _resetAnimation() {
_controller.reset();
_controller.forward();
}
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Column(
children: [
// 进度条
LinearProgressIndicator(value: _animation.value),
// 旋转的加载图标
Transform.rotate(
angle: _animation.value * 2 * 3.14159, // 完整旋转
child: Icon(Icons.refresh, size: 50),
),
// 文本显示进度百分比
Text('进度: ${(_animation.value * 100).toStringAsFixed(1)}%'),
],
);
},
);
}
}
- Ticker - 帧同步定时器
import 'package:flutter/scheduler.dart';
class TickerExample extends StatefulWidget {
_TickerExampleState createState() => _TickerExampleState();
}
class _TickerExampleState extends State<TickerExample>
with SingleTickerProviderStateMixin {
late Ticker _ticker;
int _frameCount = 0;
DateTime? _startTime;
double? _fps;
void initState() {
super.initState();
/// 创建Ticker,每帧都会回调
_ticker = createTicker((elapsed) {
_frameCount++;
// 计算FPS
if (_startTime == null) {
_startTime = DateTime.now();
} else {
final duration = DateTime.now().difference(_startTime!);
if (duration.inMilliseconds > 0) {
_fps = _frameCount / (duration.inMilliseconds / 1000);
}
}
if (mounted) {
setState(() {});
}
});
_ticker.start();
}
void dispose() {
/// 停止并销毁Ticker
_ticker.stop();
_ticker.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Column(
children: [
Text('帧数: $_frameCount'),
if (_fps != null) Text('FPS: ${_fps!.toStringAsFixed(1)}'),
// 一个简单的动画演示
Container(
width: 100 + (_frameCount % 100).toDouble(),
height: 100,
color: Colors.blue,
alignment: Alignment.center,
child: Text('${_frameCount % 100}'),
),
],
);
}
}
- WorkManager - 后台定时任务
6.1 添加依赖
# pubspec.yaml
dependencies:
workmanager: ^0.5.0
flutter_local_notifications: ^9.0.0
6.2 后台任务实现
import 'package:workmanager/workmanager.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
/// 初始化WorkManager
void initWorkManager() {
Workmanager().initialize(
callbackDispatcher, // 后台任务回调
isInDebugMode: true, // 调试模式
);
}
/// 后台任务回调函数
('vm:entry-point') // 重要:保持函数可被后台调用
void callbackDispatcher() {
Workmanager().executeTask((taskName, inputData) async {
print("后台任务执行: $taskName");
// 初始化通知插件
final FlutterLocalNotificationsPlugin notificationsPlugin =
FlutterLocalNotificationsPlugin();
// 显示通知
await notificationsPlugin.show(
0,
'定时任务提醒',
'任务 "$taskName" 已执行',
NotificationDetails(
android: AndroidNotificationDetails(
'channel_id',
'定时任务',
importance: Importance.high,
),
iOS: DarwinNotificationDetails(),
),
);
return Future.value(true); // 返回true表示任务成功
});
}
/// 注册定时任务
void registerBackgroundTasks() {
// 一次性任务:15分钟后执行
Workmanager().registerOneOffTask(
"taskOne",
"simpleTask",
initialDelay: Duration(minutes: 15),
inputData: {"data": "一次性任务"},
);
// 周期性任务:每1小时执行
Workmanager().registerPeriodicTask(
"taskTwo",
"simpleTask",
frequency: Duration(hours: 1),
constraints: Constraints(
networkType: NetworkType.connected, // 需要网络
),
);
// 精确定时:每天上午10点执行
Workmanager().registerPeriodicTask(
"dailyTask",
"simpleTask",
frequency: Duration(hours: 24),
initialDelay: _calculateInitialDelay(10, 0), // 计算到上午10点的延迟
);
}
/// 计算到指定时间的延迟
Duration _calculateInitialDelay(int hour, int minute) {
final now = DateTime.now();
var scheduledTime = DateTime(now.year, now.month, now.day, hour, minute);
if (scheduledTime.isBefore(now)) {
scheduledTime = scheduledTime.add(Duration(days: 1));
}
return scheduledTime.difference(now);
}
- cron - 复杂定时表达式
7.1 添加依赖
dependencies:
cron: ^0.4.0
7.2 使用cron表达式
import 'package:cron/cron.dart';
void cronExample() {
final cron = Cron();
/// 每5秒执行一次
cron.schedule(
Schedule.parse('*/5 * * * * *'), // cron表达式
() async {
print('每5秒执行: ${DateTime.now()}');
},
);
/// 每分钟的第30秒执行
cron.schedule(
Schedule.parse('30 * * * * *'),
() => print('每分钟30秒执行'),
);
/// 每天上午10:15执行
cron.schedule(
Schedule.parse('0 15 10 * * *'),
() => print('每天10:15执行'),
);
/// 工作日(周一到周五)上午9点执行
cron.schedule(
Schedule.parse('0 0 9 * * 1-5'),
() => print('工作日9点执行'),
);
/// 停止所有定时任务
Future.delayed(Duration(minutes: 10), () {
cron.close();
print('所有cron任务已停止');
});
}
- 完整实战案例:倒计时器
import 'package:flutter/material.dart';
import 'dart:async';
class CountdownTimerApp extends StatefulWidget {
_CountdownTimerAppState createState() => _CountdownTimerAppState();
}
class _CountdownTimerAppState extends State<CountdownTimerApp>
with SingleTickerProviderStateMixin {
// 定时器相关
Timer? _timer;
Duration _duration = Duration(seconds: 300); // 5分钟
bool _isRunning = false;
// 动画相关
late AnimationController _animationController;
late Animation<double> _animation;
void initState() {
super.initState();
/// 初始化动画控制器
_animationController = AnimationController(
duration: _duration,
vsync: this,
)..addListener(() {
if (mounted) setState(() {});
});
/// 创建动画
_animation = Tween<double>(begin: 1.0, end: 0.0)
.animate(_animationController);
}
/// 开始/暂停计时
void _toggleTimer() {
if (_isRunning) {
_pauseTimer();
} else {
_startTimer();
}
}
/// 启动计时器
void _startTimer() {
if (_duration.inSeconds <= 0) return;
setState(() => _isRunning = true);
/// 启动动画
_animationController.duration = _duration;
_animationController.forward(from: 1.0 - (_animation.value));
/// 启动定时器
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
_duration = _duration - Duration(seconds: 1);
// 时间到
if (_duration.inSeconds <= 0) {
_timerComplete();
}
});
});
}
/// 暂停计时器
void _pauseTimer() {
setState(() => _isRunning = false);
_timer?.cancel();
_animationController.stop();
}
/// 重置计时器
void _resetTimer() {
_pauseTimer();
setState(() {
_duration = Duration(seconds: 300);
});
_animationController.reset();
}
/// 时间到处理
void _timerComplete() {
_pauseTimer();
// 显示完成对话框
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('时间到!'),
content: Text('5分钟倒计时结束'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('确定'),
),
],
),
);
}
/// 格式化时间显示
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final minutes = twoDigits(duration.inMinutes.remainder(60));
final seconds = twoDigits(duration.inSeconds.remainder(60));
return '$minutes:$seconds';
}
void dispose() {
_timer?.cancel();
_animationController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('倒计时器'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
/// 圆形进度指示器
SizedBox(
width: 200,
height: 200,
child: Stack(
alignment: Alignment.center,
children: [
// 背景圆
CircularProgressIndicator(
value: _animation.value,
strokeWidth: 10,
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(
_isRunning ? Colors.blue : Colors.grey,
),
),
// 时间文本
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
_formatDuration(_duration),
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: _duration.inSeconds < 60 ? Colors.red : Colors.black,
),
),
SizedBox(height: 8),
Text(
_isRunning ? '倒计时中...' : '已暂停',
style: TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
],
),
],
),
),
SizedBox(height: 40),
/// 控制按钮
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FloatingActionButton(
onPressed: _toggleTimer,
child: Icon(_isRunning ? Icons.pause : Icons.play_arrow),
backgroundColor: _isRunning ? Colors.orange : Colors.green,
),
SizedBox(width: 20),
FloatingActionButton(
onPressed: _resetTimer,
child: Icon(Icons.replay),
backgroundColor: Colors.blue,
),
],
),
SizedBox(height: 20),
/// 时间调节按钮
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildTimeAdjustButton('-1分', Duration(minutes: -1)),
SizedBox(width: 10),
_buildTimeAdjustButton('+1分', Duration(minutes: 1)),
SizedBox(width: 10),
_buildTimeAdjustButton('-5分', Duration(minutes: -5)),
SizedBox(width: 10),
_buildTimeAdjustButton('+5分', Duration(minutes: 5)),
],
),
],
),
),
);
}
Widget _buildTimeAdjustButton(String label, Duration adjustment) {
return ElevatedButton(
onPressed: _isRunning ? null : () {
setState(() {
final newDuration = _duration + adjustment;
if (newDuration.inSeconds > 0) {
_duration = newDuration;
}
});
},
child: Text(label),
);
}
}
- 性能优化与注意事项
9.1 内存管理最佳实践
/// 1. 始终在dispose中取消定时器
void dispose() {
_timer?.cancel(); // ✅ 正确做法
super.dispose(); // 注意顺序
}
/// 2. 使用计时器前检查mounted状态
void _safeTimerCallback() {
if (!mounted) return; // 防止组件销毁后调用setState
setState(() {
// 更新UI
});
}
/// 3. 避免在build方法中创建定时器
// ❌ 错误做法 - 每次build都会创建新定时器
Widget build(BuildContext context) {
Timer(Duration(seconds: 1), () {}); // 错误!
return Container();
}
// ✅ 正确做法 - 在initState或回调方法中创建
void initState() {
super.initState();
_startTimer(); // 正确
}
9.2 性能优化建议
/// 1. 使用ValueNotifier减少setState调用
class OptimizedTimerExample extends StatefulWidget {
_OptimizedTimerExampleState createState() => _OptimizedTimerExampleState();
}
class _OptimizedTimerExampleState extends State<OptimizedTimerExample> {
final ValueNotifier<int> _counter = ValueNotifier<int>(0);
Timer? _timer;
void initState() {
super.initState();
// 使用ValueNotifier更新,避免整个widget重建
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
_counter.value++; // 只重建依赖的部分
});
}
Widget build(BuildContext context) {
return ValueListenableBuilder<int>(
valueListenable: _counter,
builder: (context, value, child) {
return Text('计数: $value'); // 只有这部分会重建
},
);
}
}
/// 2. 对于频繁更新,考虑使用Ticker或AnimationController
/// 它们与屏幕刷新率同步,性能更好
9.3 常见问题与解决方案
问题 原因 解决方案
定时器不精确 Dart事件循环延迟 使用Ticker或AnimationController
后台定时器不工作 系统限制 使用WorkManager插件
内存泄漏 未及时取消定时器 确保在dispose()中cancel()
UI卡顿 频繁调用setState 使用ValueNotifier或StreamBuilder
定时器在页面切换后仍运行 未处理页面生命周期 在deactivate中暂停,在activate中恢复
9.4 平台差异注意事项
/// iOS后台限制处理
void handleIOSBackground() {
// iOS后台最多执行10分钟
// 需要配置后台模式:Audio、Location、VoIP等
// 考虑使用推送通知替代定时任务
}
/// Android前台服务
void setupAndroidForegroundService() {
// Android 8.0+需要前台服务
// 使用flutter_foreground_task插件
// 显示持续通知
}
/// 省电模式处理
void handleBatteryOptimization() {
// 检查是否在省电白名单中
// 引导用户关闭省电限制
// 减少定时频率或使用WorkManager
}
📚 总结
Flutter提供了多种定时任务实现方式,各有适用场景:
· 简单延迟:使用Future.delayed
· 基础定时:使用Timer或Timer.periodic
· 响应式UI:使用Stream.periodic + StreamBuilder
· 动画相关:使用AnimationController
· 帧同步:使用Ticker
· 后台任务:使用WorkManager插件
· 复杂调度:使用cron插件
选择合适的方式,并遵循最佳实践,可以创建出高效、稳定的定时功能。记住关键原则:及时清理资源、考虑平台差异、优化性能表现。
更多推荐
所有评论(0)