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

Flutter实现每秒自动更新时间文本值的正确方法

问题解答

1. 页面文本每秒自动更新的正确实现方案

  • 核心使用Dart自带的Timer.periodic构造1秒间隔的周期性定时器,每次触发时获取当前时间,调用setState刷新页面显示即可
  • 定时器必须交由对应页面的State类管理:在页面初始化(initState生命周期)时创建定时器,页面销毁(dispose生命周期)时必须调用timer.cancel()释放资源,避免内存泄漏、后台无谓运行消耗性能
  • 时间、当前选中下标这类页面私有状态,不要定义成全局变量,统一放在对应State类内部管理,避免多页面状态错乱
  • 不要用RefreshIndicator做这个功能,这个组件是做下拉刷新交互的,和定时自动更新场景完全不匹配

2. 关于在bottomNavigationBar逻辑中写递归调setState实现更新的可行性

  • 完全不推荐,属于错误实现思路
  • 首先bottomNavigationBar的onTap回调只有用户点击底部栏时才会触发,把逻辑写在这里根本不会自动按秒执行
  • 其次递归如果没有严格的终止、节流控制,极容易触发栈溢出错误,你也没法精准控制更新频率
  • 最后写在底部栏点击逻辑里的话,你没法在页面销毁时停止递归/定时任务,会导致页面退出后代码还在运行,轻则性能浪费,重则触发空报错

原有代码问题修正

你原有代码除了没有定时更新逻辑外,还存在几个常见的初级错误:

  1. 整个应用只需要在根节点放一个MaterialApp即可,不需要每个页面都嵌套一个,会导致路由、主题配置失效
  2. 页面返回不要用push新页面的方式,会让路由栈无限堆叠,返回上一页直接用Navigator.pop(context)即可
  3. 路由跳转操作不需要包裹在setState内部,路由跳转本身不依赖当前页面的状态刷新

修正后的可运行完整代码如下:

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const FirstPage(),
    );
  }
}

class FirstPage extends StatefulWidget {
  const FirstPage({Key? key}) : super(key: key);

  @override
  State<FirstPage> createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  // 页面私有状态,不要定义为全局变量
  int currentIndex = 0;
  DateTime nowTime = DateTime.now();
  Timer? _timeTimer;

  // 时间格式化方法,补零显示更规范
  String _formatTime() {
    final h = nowTime.hour.toString().padLeft(2, '0');
    final m = nowTime.minute.toString().padLeft(2, '0');
    final s = nowTime.second.toString().padLeft(2, '0');
    return "$h : $m : $s";
  }

  @override
  void initState() {
    super.initState();
    // 初始化1秒间隔的定时器
    _timeTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      // 仅选中时间页时才刷新,减少不必要的性能消耗
      if (currentIndex == 1) {
        setState(() {
          nowTime = DateTime.now();
        });
      }
    });
  }

  @override
  void dispose() {
    // 页面销毁时取消定时器,避免内存泄漏
    _timeTimer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: currentIndex == 0
            ? Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                    onPressed: () {
                      Navigator.of(context).push(MaterialPageRoute(
                        builder: (BuildContext context) {
                          return const SecondPage();
                        },
                      ));
                    },
                    child: const Text("Click me"),
                  )
                ],
              )
            : currentIndex == 1
                ? Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text(
                        _formatTime(),
                        style: const TextStyle(fontSize: 80),
                      ),
                    ],
                  )
                : const SizedBox(),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(
              label: "Icecream",
              icon: Icon(
                Icons.icecream,
                color: Colors.white,
              )),
          BottomNavigationBarItem(
              label: "Time",
              icon: Icon(
                Icons.access_time,
                color: Colors.white,
              ))
        ],
        backgroundColor: Colors.blue,
        currentIndex: currentIndex,
        onTap: (int index) {
          setState(() {
            currentIndex = index;
            // 切到时间页立刻刷新一次时间,避免第一秒显示旧值
            if (index == 1) {
              nowTime = DateTime.now();
            }
          });
        },
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  const SecondPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey,
      appBar: AppBar(
        title: const Text("Cool"),
        backgroundColor: Colors.transparent,
      ),
      body: Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () {
                // 直接pop返回上一页,不要重复push首页
                Navigator.pop(context);
              },
              style: ElevatedButton.styleFrom(
                primary: Colors.yellow,
                onPrimary: Colors.orange,
              ),
              child: const Text("Go back"),
            )
          ],
        ),
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Star Jinx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:42:18