Flutter中无返回值函数修改传入Map致UI更新的原因咨询
我遇到了一个令我困惑的异常现象,已花费3小时仍未理清原因。
问题场景
从Provider中获取名为“idea”的实例,并在Widget中使用该实例的属性。
实例获取代码
Widget build(BuildContext context) { var idea = Provider.of<Idea>(context, listen: false).allideas.firstWhere( (element) => element.id == widget.idea_id, );
随后调用另一个Provider的两个函数:
- HTTP请求函数:
final connectionData = await Provider.of<IdeaConnection>(context, listen: false) .createIdeaConnection( idea.userId, idea.id, ); final ideaProvider = Provider.of<IdeaConnection>(context, listen: false); await ideaProvider.uppdeaIdeaConnectionsprov( idea.connections, connectionData, );
该函数实现:
Future<dynamic> createIdeaConnection(int resiver_user_id, int idea_id) async { final url = Uri.parse('http://10.0.2.2:3000/ideaconnetion'); var jwt = await storage.read(key: "token"); var userid = await storage.read(key: "id"); var response; if (jwt != null) { final Map<String, String> tokenData = { "Content-Type": "application/json", "token": jwt }; try { response = await http.post(url, headers: tokenData, body: json.encode({ "resiver_user_id": resiver_user_id, "idea_id": idea_id, })); response = jsonDecode(response.body); } catch (error) { print(error); rethrow; } } return response; }
- 本地数据更新函数:
Future<void> uppdeaIdeaConnectionsprov( Map<int, IdeaConnectionsModel> connections, data) async { var id = await storage.read(key: "id") as String; var userData = await getLogedinUserData(); connections.putIfAbsent( data['user_id'], () => IdeaConnectionsModel( id: data['id'], uppdatedAtt: DateTime.parse(data['updatedAt']), fName: userData['fName'] as String, imge: stringToByteList(userData['image']!), lNmae: userData['lName'] as String, userId: int.parse(id), userName: userData['userName'] as String, accepted: false, idea_Id: data['idea_id'], )); }
令我疑惑的是,uppdeaIdeaConnectionsprov()函数没有返回任何Map,但我传入的idea.connections却被修改了,且Text组件中显示的连接数也发生了变化,请问这是如何实现的?
Text组件代码
Text( "${idea.connections.length} Connections", style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12), ),
这是Dart语言引用类型传递的特性导致的:
Map是引用类型:在Dart中,
Map、List这类集合属于引用类型。当你把idea.connections作为参数传入uppdeaIdeaConnectionsprov函数时,传递的不是Map的副本,而是指向内存中同一个Map对象的引用。所以函数内部调用connections.putIfAbsent()时,直接修改的就是原idea.connections指向的那个Map实例,不需要返回就能改变原对象。Text组件更新的原因:虽然获取
idea时用了listen: false,但如果后续idea实例被Provider标记为更新(比如Idea类继承了ChangeNotifier并调用了notifyListeners()),Widget会重新build,此时读取idea.connections.length就会拿到修改后的值。如果没有手动调用notifyListeners(),也可能是父Widget重建等间接触发了Text组件的重新渲染,从而显示最新长度。
优化建议
这种直接修改Provider对象属性的方式不符合状态管理的最佳实践,容易导致状态更新不可控:
- 让
Idea类继承ChangeNotifier,在修改connections后调用notifyListeners(),确保依赖该状态的Widget能及时、可控地更新。 - 尽量通过Provider提供的方法修改状态,而非直接操作从Provider获取的对象属性,保持状态管理的一致性。
内容的提问来源于stack exchange,提问作者Mouayad

