ListView.builder构建的ModalBottomSheet空白问题(关联Google Places API)
问题原因及修复方案
核心原因分析
弹窗空白最可能的原因是_placeModel!.length为0,导致ListView.builder没有生成任何列表项。另外,点击Marker通常对应单个地点,用ListView.builder包裹单个地点的信息不符合逻辑,反而容易引发数据匹配问题。
具体修复步骤
1. 先确认数据是否正确加载
在调用showModalBottomSheet前,打印_placeModel的长度和内容,验证数据是否从Google Places API成功获取并赋值:
_onMarkerTapped() { print("Place model length: ${_placeModel!.length}"); print("Place model data: ${_placeModel}"); // 后续弹窗代码 }
如果打印结果显示长度为0,需要检查API请求逻辑:
- 确认API密钥、请求参数是否正确
- 检查数据解析代码,确保返回的JSON正确映射到
_placeModel - 确认请求完成后才调用
_onMarkerTapped,避免在数据未加载时触发弹窗
2. 调整弹窗内容结构(针对单个地点场景)
因为点击Marker对应单个地点,无需使用ListView.builder,直接返回包含信息的Column即可,同时添加高度约束避免弹窗高度异常:
_onMarkerTapped() { // 先判断_placeModel是否有数据,避免空指针 if (_placeModel == null || _placeModel!.isEmpty) return; showModalBottomSheet<void>( enableDrag: true, elevation: 10, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(50.0)) ), context: context, builder: (BuildContext context) { // 取第一个地点(如果是单个地点的场景) final place = _placeModel![0]; return SizedBox( // 设置弹窗最大高度,避免内容过长溢出 height: MediaQuery.of(context).size.height * 0.4, child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ ListTile( leading: const Icon(Icons.web), title: const Text('Website'), onTap: () => Navigator.pop(context), ), ListTile( leading: const Icon(Icons.home), title: Text('Address: ${place.address}'), onTap: () => Navigator.pop(context), ), ListTile( leading: const Icon(Icons.schedule), title: const Text('Hours of Operation'), onTap: () => Navigator.pop(context), ), ListTile( leading: const Icon(Icons.share), title: const Text('Share'), onTap: () => Navigator.pop(context), ), ], ), ); } ); }
3. 若确实是多地点场景的优化
如果_placeModel是多个地点的列表,需要确保长度大于0,同时调整每个列表项的结构,避免重复添加相同的ListTile:
_onMarkerTapped() { if (_placeModel == null || _placeModel!.isEmpty) return; showModalBottomSheet<void>( enableDrag: true, elevation: 10, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(50.0)) ), context: context, builder: (BuildContext context) { return SizedBox( height: MediaQuery.of(context).size.height * 0.6, child: ListView.builder( itemCount: _placeModel!.length, itemBuilder: (BuildContext context, int index) { final place = _placeModel![index]; // 每个地点对应一组信息,添加分割线区分 return Column( children: [ ListTile( leading: const Icon(Icons.place), title: Text('地点 ${index+1}'), ), ListTile( leading: const Icon(Icons.home), title: Text('Address: ${place.address}'), onTap: () => Navigator.pop(context), ), const Divider(height: 1), ], ); }, ), ); } ); }
内容的提问来源于stack exchange,提问作者Smiles4U
相关产品推荐
相关产品推荐

