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
相关产品推荐
相关产品推荐

