使用GetX时Flutter UI仅热重载后才刷新的问题求助
问题分析与解决方案
你遇到的UI不自动刷新问题,核心原因有两个:控制器实例可能不唯一,以及主题颜色的获取方式错误导致Flutter无法正确识别状态变化。以下是具体的修复步骤和代码调整:
修复步骤
1. 确保控制器全局唯一
在页面中直接使用Get.put()可能会重复创建控制器实例(尤其是页面被多次打开时),导致页面监听的实例与状态变更的实例不一致。正确的做法是在应用入口初始化控制器,页面中用Get.find()获取已存在的实例。
2. 修正主题颜色获取方式
直接调用ThemeData.dark()会每次生成新的ThemeData实例,即使颜色值相同,Flutter也可能判定为“未变化”,从而不触发UI更新。应使用当前上下文的主题颜色。
3. 统一状态更新方法
使用update()(GetXController的标准更新方法)替代refresh(),确保状态变更能正确通知到UI。
修改后的代码
1. 应用入口初始化控制器(main.dart)
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'path_to_your_controller/expenses_controller.dart'; import 'path_to_your_screen/new_expense_screen.dart'; void main() { // 全局初始化控制器,确保唯一实例 Get.put(ExpensesController()); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return GetMaterialApp( title: 'Budget App', theme: ThemeData.dark(), home: const NewExpenseScreen(), ); } }
2. 修改ExpensesController的状态更新方法
import 'package:annual_budget/backend/model/spending_categories.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; class ExpensesController extends GetxController { var expenseCategories = [].obs; var expenseSubcategories = [].obs; var selectedCategoryName = ''.obs; var selectedSubcategoryName = ''.obs; @override void onInit() { super.onInit(); expenseCategories.value = [ Category( icon: Icons.all_inclusive_rounded, name: 'Everyday', subcategories: { SubCategory(icon: Icons.local_grocery_store, name: 'Groceries'), SubCategory(icon: Icons.restaurant, name: 'Restaurant'), SubCategory(icon: Icons.delivery_dining, name: 'Food Order'), }, ), Category( icon: Icons.travel_explore, name: 'Travel', subcategories: { SubCategory(icon: Icons.airplane_ticket_outlined, name: 'Transportation'), SubCategory(icon: Icons.food_bank_rounded, name: 'Food'), SubCategory(icon: Icons.house, name: 'Accomodation'), }, ), Category( icon: Icons.pets_rounded, name: 'Pet', subcategories: { SubCategory(icon: Icons.food_bank, name: 'Food'), SubCategory(icon: Icons.medical_services, name: 'Veterinary'), }, ), ]; } void setSubcategories(Set<SubCategory> subcategories, String categoryName) { selectedCategoryName.value = categoryName; expenseSubcategories.value = subcategories.toList(); update(); // 通知UI更新,GetBuilder和Obx都能识别 } }
3. 修改NewExpenseScreen的控制器获取与颜色逻辑
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'path_to_your_controller/expenses_controller.dart'; import 'path_to_your_model/spending_categories.dart'; class NewExpenseScreen extends StatelessWidget { static const String route = 'new_expense_screen2'; // 获取全局唯一的控制器实例 final ExpensesController exc = Get.find<ExpensesController>(); @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( leading: MaterialButton( onPressed: () => Navigator.of(context).pop(), child: const Icon(Icons.arrow_back_ios_new_rounded), ), title: const Text('Add a new Expense'), ), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Expanded( child: Obx( () => ListView.builder( itemCount: exc.expenseCategories.length, itemBuilder: (context, index) { Category category = exc.expenseCategories[index]; final isSelected = category.name == exc.selectedCategoryName.value; return MaterialButton( color: isSelected ? Theme.of(context).colorScheme.secondary : Theme.of(context).colorScheme.background, onPressed: () { exc.setSubcategories(category.subcategories, category.name); FocusManager.instance.primaryFocus?.unfocus(); }, child: Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Column( children: [ Icon( category.icon, color: isSelected ? Colors.black : Colors.white, ), Text( category.name, style: TextStyle( color: isSelected ? Colors.black : Colors.white, ), ) ], ), ), ); }), ), ), ], ), ), ); } }
内容的提问来源于stack exchange,提问作者Headake
相关产品推荐
相关产品推荐

