Flutter中for循环内Text组件全部显示相同值的问题排查
这是Dart开发中很常见的闭包陷阱——你在循环里创建的Marker的builder是一个闭包,它捕获的是_markersDistanceCount这个变量的引用,而不是循环迭代时该变量的具体值。
当整个for循环执行完毕后,_markersDistanceCount的值已经累加到25了。等到Flutter需要渲染这些Marker的UI时,所有闭包才会去读取这个变量的当前值,自然就全部显示25了。
为什么用i.toString()就正常?因为从Dart 2.2版本开始,for循环的迭代变量i在每次循环迭代时都会创建一个新的实例,闭包捕获的是每次迭代的i本身,所以每个Text组件拿到的都是对应循环步骤的正确值。
方案一:创建局部变量保存当前值
在循环内部定义一个局部变量,把当前的_markersDistanceCount值存进去,让闭包捕获这个局部变量(它的作用域仅限于当前循环迭代):
var _markersDistanceCount = 0; for (var i = 0; i < 25; i++) { _markersDistanceCount++; final currentCount = _markersDistanceCount; // 关键:局部变量保存当前值 print(_markersDistanceCount); markersDistance.add( Marker( point: LatLng(point.latitude, point.longitude), builder: (context) => CircleAvatar( radius: 50, backgroundColor: Colors.red, child: Text(currentCount.toString()) ), ), ); }
方案二:直接复用循环变量i(适合你的场景)
既然你已经发现用i.toString()可以解决问题,这其实是最简洁的方式——因为Dart的循环迭代变量特性,每个闭包捕获的都是对应迭代的i实例,值自然正确:
for (var i = 0; i < 25; i++) { print(i + 1); // 和你的_markersDistanceCount逻辑一致 markersDistance.add( Marker( point: LatLng(point.latitude, point.longitude), builder: (context) => CircleAvatar( radius: 50, backgroundColor: Colors.red, child: Text((i + 1).toString()) // 直接用i+1替代自增变量 ), ), ); }
方案三:用函数封装捕获值
如果业务逻辑更复杂,可以把Text的创建封装成一个函数,通过参数传递当前值,利用函数参数的作用域来确保捕获的是正确值:
Widget buildCountText(int count) { // 函数参数会创建独立的作用域,闭包捕获的是当前传入的count值 return Text(count.toString()); } // 循环内调用: var _markersDistanceCount = 0; for (var i = 0; i < 25; i++) { _markersDistanceCount++; print(_markersDistanceCount); markersDistance.add( Marker( point: LatLng(point.latitude, point.longitude), builder: (context) => CircleAvatar( radius: 50, backgroundColor: Colors.red, child: buildCountText(_markersDistanceCount) ), ), ); }
你尝试的序列化反序列化操作,本质上只是把变量的值复制了一份,但如果没有把这个复制后的值存到一个局部变量里,而是直接在Text中使用jsonDecode(jsonEncode(_markersDistanceCount)),那么闭包执行时还是会去读取_markersDistanceCount的当前值(也就是循环结束后的25),自然无法解决问题。如果把序列化后的值存到局部变量再使用,其实就和方案一的逻辑一致了。
内容的提问来源于stack exchange,提问作者Bas van Dijk

