Flutter如何通过SharedPreferences按商品ID存储星级评分
问题根源
- 目前
RatingController默认是全局单例,所有商品详情页共用同一个Controller实例,currentRating字段仅保留一份数据 - 存储评分时使用固定key
rating,未和商品ID绑定,所有商品读写的都是同一个存储值
修改方案
第一步:改造RatingController,支持绑定商品ID
修改Controller构造接收商品ID参数,存储、读取的key都和商品ID绑定,避免数据冲突:
class RatingController extends GetxController { // 接收当前商品ID作为必传参数 final String productId; RatingController({required this.productId}); int currentRating = 0; late SharedPreferences prefs; // 存储用的key,和商品ID绑定,保证每个商品对应独立的存储位置 String get _ratingKey => 'rating_$productId'; @override void onInit() { super.onInit(); initSp(); // 初始化时读取当前商品的历史评分 } Future<void> initSp() async { prefs = await SharedPreferences.getInstance(); // 仅读取当前商品对应的评分,无历史评分则返回0 currentRating = prefs.getInt(_ratingKey) ?? 0; update(); } void updateAndStoreRating(int rating) { currentRating = rating; // 存储到当前商品对应的key下 prefs.setInt(_ratingKey, rating); update(); } Widget buildRatingStar(int index) { return Icon( Icons.star, color: index < currentRating ? Colors.yellow : Colors.white, ); } }
如果你要继续使用GetStorage存储,只需要把读写的key也替换为_ratingKey即可,逻辑和SharedPreferences完全一致。
第二步:商品详情页初始化Controller时绑定商品ID
不要使用全局共享的Get.find<RatingController>(),进入商品详情页时用商品ID作为tag,创建对应商品的独立Controller实例:
// 商品详情页代码示例 late RatingController controller; @override void initState() { super.initState(); // widget.productId为当前商品的ID,根据你自己的业务代码替换即可 controller = Get.put(RatingController(productId: widget.productId), tag: widget.productId); } // 页面销毁时销毁对应Controller释放资源,避免内存泄漏 @override void dispose() { Get.delete<RatingController>(tag: widget.productId); super.dispose(); }
第三步:修改Rating Widget的Controller获取逻辑
获取Controller时携带商品ID作为tag,保证读取的是当前商品对应的Controller实例:
Widget _buildBody() { final stars = List<Widget>.generate(5, (index) { return GetBuilder<RatingController>( // 指定tag获取对应商品的Controller tag: widget.productId, builder: (controller) => Expanded( child: GestureDetector( child: controller.buildRatingStar(index), onTap: () { controller.updateAndStoreRating(index + 1); }, ), ), ); }); return Row( children: [ Expanded( child: Row(children: stars), ), Expanded( child: TextButton( onPressed: () => controller.updateAndStoreRating(0), child: const Text("Clear", style: TextStyle(color: Colors.white)), ), ), ], ); }
其他可选方案
如果不想每个商品都创建独立的Controller实例,也可以使用全局单例Controller,新增切换当前商品ID的方法,每次进入商品详情页调用方法切换当前商品ID、重新加载对应评分即可,不过独立实例的隔离性更好,不会出现状态串改的问题。
内容的提问来源于stack exchange,提问作者Pramod Yadav
相关产品推荐
相关产品推荐

