Flutter中AsyncNotifier结合ref.watch引发UI持续渲染及Firebase问题
一、ContractConfigProvider持续触发、UI重复渲染的原因及修复
核心原因
- 状态更新逻辑冗余:调用
setContractType/setContractName时,先设置loading状态再立刻切换到data状态,两次状态变更都会触发UI重建。 - 模型未实现相等性判断:
ContractConfigModel未重写==和hashCode,Riverpod无法识别属性相同的实例为同一状态,导致不必要的重建。 - build方法未限制重复执行:若
authRepo的依赖发生变化(即使不影响用户数据),build会重新执行Firestore请求,重复更新状态。
修复步骤
1. 优化状态更新逻辑
去掉无意义的loading切换,直接更新数据状态:
void setContractType(String value) { final currentState = state.value; if (currentState == null) return; state = AsyncValue.data( ContractConfigModel( contractType: value, contractName: currentState.contractName, ), ); } void setContractName(String value) { final currentState = state.value; if (currentState == null) return; state = AsyncValue.data( ContractConfigModel( contractType: currentState.contractType, contractName: value, ), ); }
2. 为模型添加相等性判断
重写==和hashCode,让Riverpod能正确识别状态是否变化:
class ContractConfigModel { String contractType; String contractName; ContractConfigModel({ required this.contractType, required this.contractName, }); ContractConfigModel.empty() : contractType = "", contractName = ""; @override bool operator ==(Object other) => identical(this, other) || other is ContractConfigModel && runtimeType == other.runtimeType && contractType == other.contractType && contractName == other.contractName; @override int get hashCode => contractType.hashCode ^ contractName.hashCode; }
3. 限制build方法重复执行
通过监听authRepo的user状态,仅在用户变化时重新请求Firestore:
@override FutureOr<ContractConfigModel> build() async { _authRepository = ref.read(authRepo); // 仅当user变化时,重新执行后续逻辑 final user = await ref.watch(authRepo.select((repo) => repo.user).future); if (user == null) return ContractConfigModel.empty(); final userId = user.uid; final adminData = await _authRepository.getAdminProfile(userId); final adminProfile = AdminProfileModel.fromJson(adminData!); return adminProfile.master ? ContractConfigModel( contractType: adminProfile.contractType, contractName: adminProfile.region, ) : ContractConfigModel( contractType: adminProfile.contractType, contractName: "${adminProfile.region} ${adminProfile.smallRegion}", ); }
二、Firebase初始化错误的解决
错误原因
应用启动时未完成Firebase初始化,路由的redirect逻辑(或其他代码)就提前访问了Firebase服务,导致抛出[core/no-app]异常。
修复步骤
1. 在main函数中优先初始化Firebase
确保所有依赖Firebase的代码都在初始化完成后执行:
void main() async { WidgetsFlutterBinding.ensureInitialized(); // 初始化Firebase,Web平台需添加options参数 await Firebase.initializeApp( // options: DefaultFirebaseOptions.currentPlatform, ); runApp(const ProviderScope(child: MyApp())); }
2. 路由逻辑等待Firebase初始化完成
创建Provider监听Firebase初始化状态,确保redirect逻辑在初始化完成后执行:
final firebaseInitializedProvider = FutureProvider<bool>((ref) async { await Firebase.initializeApp(); return true; });
在GoRouter的redirect中添加等待逻辑:
final router = GoRouter( redirect: (context, state) async { final initialized = await ref.watch(firebaseInitializedProvider.future); if (!initialized) return null; // 等待初始化完成 // 后续的路由跳转逻辑 }, routes: [...], );
内容的提问来源于stack exchange,提问作者Hyejung
相关产品推荐
相关产品推荐

