You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter应用开发:实现从当前日期开始的正向实时天数计数器

实现正向实时天数计数器的方案

核心思路

不用依赖现有倒计时包,自己实现正向计时逻辑更灵活:

  • 记录启动时刻(用户点击启动时的DateTime.now())
  • 用Timer.periodic每秒计算当前时间与启动时刻的时间差
  • 将差值转换为天、时、分、秒格式展示
  • 停止时计算总天数并通过本地存储持久化

具体实现步骤

1. 核心计时逻辑

用StatefulWidget维护计数器状态,关键变量包括启动时间、定时器、累计流逝时长和运行状态:

DateTime? _startTime;
Timer? _timer;
Duration _elapsed = Duration.zero;
bool _isRunning = false;

启动计数器时,记录当前时间并启动定时器:

void _startTimer() {
  if (!_isRunning) {
    _startTime = DateTime.now();
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        _elapsed = DateTime.now().difference(_startTime!);
      });
    });
    setState(() => _isRunning = true);
  }
}

停止计数器时,取消定时器并计算总天数:

void _stopTimer() {
  if (_isRunning) {
    _timer?.cancel();
    setState(() => _isRunning = false);
    // 计算总天数(可根据需求选择取整或保留小数)
    final totalDays = _elapsed.inSeconds / (24 * 60 * 60);
    _saveTotalDays(totalDays);
  }
}

2. 时间格式转换

把Duration对象转换成易读的天、时、分、秒字符串:

String _formatDuration(Duration duration) {
  final days = duration.inDays;
  final hours = duration.inHours.remainder(24);
  final minutes = duration.inMinutes.remainder(60);
  final seconds = duration.inSeconds.remainder(60);
  return '$days天 ${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
}

3. 本地存储总天数

使用shared_preferences实现本地持久化,先在pubspec.yaml添加依赖:

dependencies:
  shared_preferences: ^2.2.2

实现存储和读取方法:

import 'package:shared_preferences/shared_preferences.dart';

Future<void> _saveTotalDays(double days) async {
  final prefs = await SharedPreferences.getInstance();
  // 累加已存储的天数,也可根据需求直接覆盖
  final savedDays = prefs.getDouble('total_days') ?? 0.0;
  await prefs.setDouble('total_days', savedDays + days);
}

// 初始化时读取已存储的总天数
Future<void> _loadSavedDays() async {
  final prefs = await SharedPreferences.getInstance();
  setState(() {
    _savedTotalDays = prefs.getDouble('total_days') ?? 0.0;
  });
}

4. 完整UI示例

整合所有逻辑到Widget中:

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

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

  @override
  State<DaysCounter> createState() => _DaysCounterState();
}

class _DaysCounterState extends State<DaysCounter> {
  DateTime? _startTime;
  Timer? _timer;
  Duration _elapsed = Duration.zero;
  bool _isRunning = false;
  double _savedTotalDays = 0.0;

  @override
  void initState() {
    super.initState();
    _loadSavedDays();
  }

  Future<void> _loadSavedDays() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _savedTotalDays = prefs.getDouble('total_days') ?? 0.0;
    });
  }

  void _startTimer() {
    if (!_isRunning) {
      _startTime = DateTime.now();
      _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
        setState(() {
          _elapsed = DateTime.now().difference(_startTime!);
        });
      });
      setState(() => _isRunning = true);
    }
  }

  void _stopTimer() {
    if (_isRunning) {
      _timer?.cancel();
      setState(() => _isRunning = false);
      final totalDays = _elapsed.inSeconds / (24 * 60 * 60);
      _saveTotalDays(totalDays);
    }
  }

  Future<void> _saveTotalDays(double days) async {
    final prefs = await SharedPreferences.getInstance();
    final updatedDays = _savedTotalDays + days;
    await prefs.setDouble('total_days', updatedDays);
    setState(() {
      _savedTotalDays = updatedDays;
    });
  }

  String _formatDuration(Duration duration) {
    final days = duration.inDays;
    final hours = duration.inHours.remainder(24);
    final minutes = duration.inMinutes.remainder(60);
    final seconds = duration.inSeconds.remainder(60);
    return '$days天 ${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.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(
              _formatDuration(_elapsed),
              style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 20),
            Text(
              '累计总天数: ${_savedTotalDays.toStringAsFixed(2)}',
              style: const TextStyle(fontSize: 18),
            ),
            const SizedBox(height: 40),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: _isRunning ? null : _startTimer,
                  child: const Text('启动'),
                ),
                const SizedBox(width: 20),
                ElevatedButton(
                  onPressed: _isRunning ? _stopTimer : null,
                  child: const Text('停止'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

关键注意点

  • 如果需要暂停后继续计时,不要重置_startTime,而是记录暂停时的_elapsed,下次启动时用DateTime.now().subtract(_elapsed)作为新的启动时间
  • 存储时可根据需求选择取整(_elapsed.inDays)或保留小数(精确到秒转换的天数)
  • 每秒触发一次定时器足够满足实时展示需求,避免更短间隔影响性能

内容的提问来源于stack exchange,提问作者Solomon Girma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 02:31:11