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

Flutter如何实现每日10点起按钮限按10次次日重置功能

该功能完全可实现,无需服务端配合,纯客户端本地逻辑即可满足要求,核心是做好时间节点判断和点击数据的持久化存储。

核心实现逻辑
  • 选用shared_preferences做轻量本地存储,持久化两个核心数据:剩余可点击次数、下一次次数重置的时间戳,避免APP杀掉重开后计数丢失
  • 每次进入页面、触发点击前先做时间校验:如果当前时间晚于记录的重置时间,自动把可点击次数重置为10次,同时计算下一次重置时间(即次日10点)更新到本地
  • 按钮可点击状态直接绑定剩余次数:剩余次数大于0时响应点击,次数为0时直接置为不可用状态
  • 每次有效点击完成后,剩余次数减1并存回本地,次数归0时立刻刷新按钮状态
前置准备

在pubspec.yaml中添加本地存储依赖:

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.2.2 # 适配项目的稳定版本即可
代码改造示例

替换原有按钮逻辑,对应实现代码如下:

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

// 在对应StatefulWidget的State类中补充以下逻辑
class _YourPageState extends State<YourPage> {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  int _remainingClicks = 10;
  late DateTime _nextResetTime;

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

  // 加载本地存储的计数配置,判断是否需要重置次数
  Future<void> _loadClickConfig() async {
    final prefs = await SharedPreferences.getInstance();
    final storedResetTimestamp = prefs.getInt('click_reset_time');
    final now = DateTime.now();
    
    // 计算当日10点的时间节点
    final today10Am = DateTime(now.year, now.month, now.day, 10);
    if (now.isBefore(today10Am)) {
      _nextResetTime = today10Am;
    } else {
      // 已过当日10点,重置节点为次日10点
      _nextResetTime = today10Am.add(const Duration(days: 1));
    }

    // 本地无存储记录或已过重置时间,直接重置次数
    if (storedResetTimestamp == null || 
        now.millisecondsSinceEpoch >= storedResetTimestamp) {
      _remainingClicks = 10;
      await prefs.setInt('click_reset_time', _nextResetTime.millisecondsSinceEpoch);
      await prefs.setInt('remaining_clicks', 10);
    } else {
      // 未到重置时间,读取本地存储的剩余次数
      _remainingClicks = prefs.getInt('remaining_clicks') ?? 10;
      _nextResetTime = DateTime.fromMillisecondsSinceEpoch(storedResetTimestamp);
    }
    setState(() {});
  }

  // 按钮点击逻辑
  Future<void> _onButtonClick() async {
    final now = DateTime.now();
    // 点击前二次校验时间,避免页面长时间挂起错过重置节点
    if (now.isAfter(_nextResetTime)) {
      await _loadClickConfig();
    }

    if (_remainingClicks <= 0) return;

    // 原有点击业务逻辑
    _scaffoldKey.currentState.showSnackBar(
      const SnackBar(content: Text("Welcome"))
    );

    // 次数扣减后存回本地
    _remainingClicks--;
    final prefs = await SharedPreferences.getInstance();
    await prefs.setInt('remaining_clicks', _remainingClicks);
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      // 其他页面组件代码
      // ...
      // 替换原有的actions按钮部分
      actions: <Widget>[
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: RaisedButton(
            elevation: 7,
            // 次数耗尽时按钮置灰
            color: _remainingClicks > 0 ? Colors.black26 : Colors.grey,
            child: Text(_remainingClicks > 0 ? 'Button' : '今日次数已用完'),
            // 次数为0时传null,按钮自动进入不可点击状态
            onPressed: _remainingClicks > 0 ? _onButtonClick : null,
          ),
        ),
      ],
    );
  }
}
注意事项
  • 纯离线场景下该逻辑可直接使用;若需要防范用户修改系统时间作弊,需将时间校验、次数计数逻辑迁移至服务端实现
  • 高版本Flutter已废弃RaisedButton组件,可直接替换为ElevatedButton,状态控制逻辑无需改动
  • 可在按钮文本中追加剩余可点击次数提示优化交互,例如将文本内容改为按钮(剩余$_remainingClicks次)

内容的提问来源于stack exchange,提问作者luca kingsley

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 01:54:34