Flutter中Radio按钮变更的变量值无法传递至Database类
问题描述
我在Flutter项目中需要将Home类里存储Radio按钮组选中值的currentOption变量传递给Database类。目前的问题是:在Home类中切换Radio按钮时,currentOption通过setState能正常更新,在Home类内打印能得到正确的变更后的值;但在Database类中获取该变量时,打印出来的始终是Home类里的初始值,无法获取最新的变更值。
Home.dart(翻译注释后的代码)
import 'package:flutter/material.dart'; import 'package:watch_hub/services/auth.dart'; import 'package:watch_hub/services/database.dart'; import 'package:provider/provider.dart'; import 'package:watch_hub/screens/home/watch_list.dart'; import 'package:watch_hub/models/watch.dart'; List<dynamic> options = [0, 'Rolex', 'Longines']; class Home extends StatefulWidget { Home({super.key}); dynamic currentOption = options[0]; // 这是我想要传递至另一个类的变量 @override State<Home> createState() => _HomeState(); } class _HomeState extends State<Home> { final AuthService _auth = AuthService(); @override Widget build(BuildContext context) { return StreamProvider<List<Watch>>.value( value: DatabaseService().watches, initialData: const [], child: Scaffold( backgroundColor: Colors.brown[50], drawer: Drawer( elevation: 20.0, child: Column( children: <Widget>[ UserAccountsDrawerHeader( accountName: Text("Flutter"), accountEmail: null, ), // 切换这些单选按钮时currentOption的值会变更 ListTile( title: const Text('无'), leading: Radio<dynamic>( value: options[0], groupValue: widget.currentOption, onChanged: (value) { setState(() { widget.currentOption = value; print("当前值为 ${widget.currentOption}"); }); }, ), ), Divider( height: 0.1, ), ListTile( title: const Text('Rolex'), leading: Radio( value: options[1], groupValue: widget.currentOption, onChanged: (value) { setState(() { widget.currentOption = value; print("当前值为 ${widget.currentOption}"); }); }, )), ListTile( title: const Text('Longines'), leading: Radio( value: options[2], groupValue: widget.currentOption, onChanged: (value) { setState(() { widget.currentOption = value; print("当前值为 ${widget.currentOption}"); }); }, )), ListTile( title: Text("促销"), leading: Icon(Icons.local_offer), ), ], ), ), appBar: AppBar( title: Text("Watch Hub"), backgroundColor: Colors.brown[400], elevation: 0.0, actions: <Widget>[ TextButton.icon( icon: Icon(Icons.person), label: Text("退出登录"), onPressed: () async { await _auth.signOut(); }, ) ], ), body: const WatchList(), ), ); } }
Database.dart(翻译注释后的代码)
import "package:cloud_firestore/cloud_firestore.dart"; import "package:watch_hub/models/watch.dart"; import 'package:watch_hub/screens/home/home.dart'; class DatabaseService { final String? uid; DatabaseService({this.uid}); // 集合引用 final CollectionReference users = FirebaseFirestore.instance.collection("Users"); final CollectionReference watch_list = FirebaseFirestore.instance.collection("watch_hub"); dynamic brand = "Longines"; dynamic model = "Aviation Big Eye"; Future setUserData( String email, String address, String fullName, String phone) async { return await users.doc(uid).set({ "email": email, "address": address, "fullName": fullName, "phone": phone, }); } // 从快照生成手表列表 List<Watch> _watchListFromSnapshot(QuerySnapshot snapshot) { print("无选项 $option"); return snapshot.docs.map((doc) { return Watch( id: doc['id'] ?? '', brand: doc['brand'] ?? '', model: doc['model'] ?? '', type: doc['type'] ?? '', popularity: doc['popularity'] ?? 0, price: doc['price'].toString(), image: doc['image'] ?? '', ); }).toList(); } // 获取手表列表 Stream<List<Watch>> get watches { dynamic option = Home().currentOption; /* 打印此值时,option始终为0,因为这是它在home.dart中的初始值 */ print("选项值为 $option"); late Query filter; option is String ? filter = watch_list.where("brand", isEqualTo: option) : filter = watch_list.where("brand", isNotEqualTo: true); print(watch_list); return filter.snapshots().map(_watchListFromSnapshot); } }
解决方案
问题根源
你在Database类里通过Home().currentOption获取值时,实际是创建了一个全新的Home实例,这个新实例的currentOption自然是初始值0,而非界面上正在使用的Home实例的最新值。
修复步骤
- 修改DatabaseService,接收外部传入的过滤参数
把watchesgetter改成带参数的方法,让外部可以主动传入最新的选项:
// 修改DatabaseService中的watches部分 Stream<List<Watch>> getWatches(dynamic option) { print("选项值为 $option"); late Query filter; option is String ? filter = watch_list.where("brand", isEqualTo: option) : filter = watch_list.where("brand", isNotEqualTo: true); return filter.snapshots().map(_watchListFromSnapshot); }
- 在Home类中动态传递最新的currentOption
使用StreamProvider.builder替代StreamProvider.value,这样每次setState后都能重新构建包含最新选项的Stream:
// 修改Home类的build方法中的StreamProvider部分 @override Widget build(BuildContext context) { return StreamProvider<List<Watch>>.builder( initialData: const [], builder: (context, child) { return DatabaseService().getWatches(widget.currentOption); }, child: Scaffold( // 原有的Scaffold内容保持不变 ... ), ); }
- 优化变量存储(可选但推荐)
按照Flutter设计规范,将状态变量移到State类中(StatefulWidget本身应保持不可变):
class Home extends StatefulWidget { Home({super.key}); @override State<Home> createState() => _HomeState(); } class _HomeState extends State<Home> { final AuthService _auth = AuthService(); dynamic currentOption = options[0]; // 变量移至State类 @override Widget build(BuildContext context) { return StreamProvider<List<Watch>>.builder( initialData: const [], builder: (context, child) { return DatabaseService().getWatches(currentOption); }, child: Scaffold( drawer: Drawer( child: Column( children: <Widget>[ // 修改Radio的groupValue和onChanged逻辑 ListTile( title: const Text('无'), leading: Radio<dynamic>( value: options[0], groupValue: currentOption, onChanged: (value) { setState(() { currentOption = value; print("当前值为 ${currentOption}"); }); }, ), ), // 其余Radio按钮同理修改 ... ], ), ), // 其他内容保持不变 ... ), ); } }
原理说明
- 通过带参数的
getWatches方法,主动将最新过滤选项传递给DatabaseService,避免创建新的Home实例获取初始值。 StreamProvider.builder会在每次setState后重新生成Stream,确保Provider监听的是最新的过滤结果流。- 将状态变量移至State类符合Flutter的状态管理规范,保证StatefulWidget的不可变性。
内容的提问来源于stack exchange,提问作者Abdullahi Abbas
相关产品推荐
相关产品推荐

