使用GetX解决Flutter底部标签切换时按钮状态重置问题
问题:底部标签切换时保存按钮状态重置,如何用GetX解决?
我尝试管理底部标签切换时按钮的状态,但每次切换标签后,按钮都会回到初始状态。以下是我的代码片段:
卡片列表函数
// 返回包含待管理状态按钮的卡片列表的函数 browseCard(List<AllJobsResult> itemList) { final jobOverviewController = Get.put(JobApplicationOverviewController()); return ListView.builder( controller: jobPreferenceController.scrollController.value, shrinkWrap: true, itemCount: itemList.length, itemBuilder: (context, index) { var datas = itemList[index]; var splitDate = datas.dateCreated.toString().split("."); var dateCreated = splitDate[0].split(" "); return itemList.length == 0 ? CircularLoader() : GestureDetector( onTap: () async { ... }, child: Container( child: Padding( padding: const EdgeInsets.symmetric( horizontal: 10.0, vertical: 5.0, ), child:SaveJobBtn( isSaved: datas.isSaved, jobID: datas.id, showText: true, ) ) }
自定义保存按钮组件
class SaveJobBtn extends StatefulWidget { bool isSaved; final int jobID; final bool showText; SaveJobBtn({Key key, this.isSaved, this.jobID, this.showText}) : super(key: key); @override _SaveJobBtnState createState() => _SaveJobBtnState(); } class _SaveJobBtnState extends State<SaveJobBtn> { bool isSavedJOb = false; @override void initState() { isSavedJOb = widget.isSaved; super.initState(); } var ctr = Get.put(BrowseAllJobsController()); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { setState(() { ctr.saveJob(widget.jobID); isSavedJOb = !isSavedJOb; // 切换按钮状态 }); }, child: Container( padding: EdgeInsets.all(6), decoration: BoxDecoration( color: isSavedJOb == false ? Colors.transparent : Color(0xffF6FCFF), borderRadius: BorderRadius.circular(4)), child: Row(children: [ isSavedJOb == false ? SvgPicture.asset( 'assets/svgs/bookmark.svg', color: isSavedJOb == false ? enumGrey3 : kPrimaryColor, height: 20, ) : Container( height: 20, padding: EdgeInsets.symmetric(horizontal: 4), child: SvgPicture.asset( 'assets/svgs/bookmark_colored.svg', height: 16, ), ), widget.showText ? Text( isSavedJOb == false ? ' Save' : ' Saved', style: TextStyle( fontFamily: 'GoogleFonts.dmSans', fontWeight: FontWeight.w400, color: isSavedJOb == false ? enumGrey3 : kPrimaryColor, fontSize: 14, ), ) : SizedBox(), ]), ), ); } }
解决方案
核心问题是你将按钮状态存在了StatefulWidget的本地状态中,切换标签触发组件重建时,initState会重新从widget.isSaved赋值,而数据源itemList里的datas.isSaved并没有同步更新,导致状态重置。结合GetX的状态管理,需要把保存状态的逻辑移到GetX Controller中,而非组件本地。
步骤1:改造GetX Controller
将已保存职位的状态维护在Controller里,用Rx集合实现自动响应:
class BrowseAllJobsController extends GetxController { // 用RxSet存储已保存的jobID,状态变化自动通知UI更新 final savedJobIds = <int>{}.obs; @override void onInit() { super.onInit(); // 初始化时可从本地存储/接口加载已保存的职位ID // savedJobIds.addAll(loadSavedJobsFromLocal()); } void saveJob(int jobId) { if (savedJobIds.contains(jobId)) { savedJobIds.remove(jobId); // 调用取消保存的接口逻辑 } else { savedJobIds.add(jobId); // 调用保存职位的接口逻辑 } // 可选:同步更新数据源中的isSaved状态,确保数据一致性 // itemList.forEach((item) { // if(item.id == jobId) item.isSaved = !item.isSaved; // }); } // 判断指定职位是否已保存 bool isJobSaved(int jobId) { return savedJobIds.contains(jobId); } }
步骤2:重构SaveJobBtn为无状态组件
移除本地状态,直接依赖Controller的全局状态:
class SaveJobBtn extends StatelessWidget { final int jobID; final bool showText; const SaveJobBtn({Key? key, required this.jobID, required this.showText}) : super(key: key); @override Widget build(BuildContext context) { final ctr = Get.find<BrowseAllJobsController>(); return GestureDetector( onTap: () { ctr.saveJob(jobID); }, child: Obx(() { bool isSavedJob = ctr.isJobSaved(jobID); return Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: isSavedJob ? const Color(0xffF6FCFF) : Colors.transparent, borderRadius: BorderRadius.circular(4)), child: Row(children: [ isSavedJob ? Container( height: 20, padding: const EdgeInsets.symmetric(horizontal: 4), child: SvgPicture.asset( 'assets/svgs/bookmark_colored.svg', height: 16, ), ) : SvgPicture.asset( 'assets/svgs/bookmark.svg', color: enumGrey3, height: 20, ), if (showText) Text( isSavedJob ? ' Saved' : ' Save', style: TextStyle( fontFamily: 'GoogleFonts.dmSans', fontWeight: FontWeight.w400, color: isSavedJob ? kPrimaryColor : enumGrey3, fontSize: 14, ), ) else const SizedBox(), ]), ); }), ); } }
步骤3:更新卡片列表的按钮调用
不再传递isSaved参数,组件直接从Controller获取最新状态:
// 返回包含待管理状态按钮的卡片列表的函数 Widget browseCard(List<AllJobsResult> itemList) { final jobOverviewController = Get.put(JobApplicationOverviewController()); return ListView.builder( controller: jobPreferenceController.scrollController.value, shrinkWrap: true, itemCount: itemList.length, itemBuilder: (context, index) { var datas = itemList[index]; var splitDate = datas.dateCreated.toString().split("."); var dateCreated = splitDate[0].split(" "); return itemList.isEmpty ? const CircularLoader() : GestureDetector( onTap: () async { // ... 原逻辑保持不变 }, child: Container( child: Padding( padding: const EdgeInsets.symmetric( horizontal: 10.0, vertical: 5.0, ), child: SaveJobBtn( jobID: datas.id, showText: true, ), ), ), ); }, ); }
关键说明
- 把保存状态从组件本地迁移到GetX Controller的Rx集合,确保状态全局共享,组件重建时自动获取最新值
- 用
Obx包裹按钮UI,当savedJobIds变化时自动触发UI更新 - 移除
SaveJobBtn的isSaved参数,避免依赖数据源中的旧状态 - 若需要保持数据源和状态一致,可在Controller的
saveJob方法中同步更新itemList的isSaved字段
内容的提问来源于stack exchange,提问作者DOMINIC DANIEL
相关产品推荐
相关产品推荐

