我的应用程序包含一个谷歌地图屏幕,我需要从 api 获取位置, 我的 viewModel 如下所示:
class MapViewModel extends ChangeNotifier {
MapRepo repo = MapRepo();
List<VendorBranchModel> branches = [];
Set<Marker> markers = {};
bool isMapLoading = true;
setMapLoading(bool val) {
isMapLoading = val;
notifyListeners();
}
void prepareMarkers() {
markers = branches.map((branch) {
return Marker(
markerId: MarkerId(branch.id ?? ""),
position: LatLng(branch.latitude ?? 0.0, branch.longitude ?? 0.0),
infoWindow: InfoWindow(
title: branch.englishBranchName,
snippet: branch.phoneNumber,
),
);
}).toSet();
setMapLoading(false);
}
Future<void> getBranchesAPI({ required Function(String?) onError}) async {
setMapLoading(true);
var response = await repo.getBranches(pageNumber: MapConstants.pageNumber,
pageSize: MapConstants.pageSize ,body: {});
response.when( success: (NetworkBaseModel response) async {
if (response.isSuccess == true) {
branches = response.data;
prepareMarkers();
}
}, failure: (NetworkExceptions error) {
onError(error.message);
setMapLoading(false);
});
}
}
正在更新的屏幕如下所示:
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => viewModel
..getBranchesAPI(
onError: (error) => ToastManager.showToast(error ?? "")
),
child: Scaffold(
body: Column(children: [
CustomSearchView(
onSearch: (keyword) {},
onFilter: (filters) {
filterRequest.orderFilter = filters;
AppLogs.debugLog(filters.toJson());
},
),
Expanded(
child: Consumer<MapViewModel>(
builder: (context, viewModel, child) {
return viewModel.isMapLoading
? Center(child: CircularProgressIndicator())
: GoogleMapsScreen(markers: viewModel.markers);
},
),
)
])));
谷歌地图小部件如下:
class GoogleMapsScreen extends StatefulWidget {
final Set<Marker> markers;
GoogleMapsScreen({super.key, required this.markers});
@override
State<GoogleMapsScreen> createState() => _GoogleMapsScreenState();
}
class _GoogleMapsScreenState extends State<GoogleMapsScreen> {
@override
Widget build(BuildContext context) {
return
GoogleMap(initialCameraPosition:
CameraPosition(target: widget.markers.isNotEmpty ? widget.markers.first.position : LatLng(33.8938, 35.5018)),
markers: widget.markers,
);
}
}
每当 api 加载并且地图获取标记时,应用程序就会崩溃,并且不会出现错误消息。 而当发送空标记时,地图显示为空状态。
任何帮助都会很棒。
根据您的描述,听起来崩溃可能是由于在构建或渲染小部件本身时更新 GoogleMapsScreen 小部件中的标记造成的。如果更新 widget.markers 然后重新渲染,就会发生这种情况,从而导致小部件状态不一致。以下是一些步骤和改进,可帮助排查并可能解决此问题:
设置标记的初始状态:初始化 GoogleMapsScreen 时,确保标记集具有默认值,以避免任何意外的空值。此外,在更新标记时使用 setState 显式更新标记可能会很有用。
为 API 响应添加错误处理:在继续创建标记之前,确保来自 getBranchesAPI 的响应数据结构良好且有效。有时,格式错误的数据可能会导致意外问题,而没有明确的错误消息。
使用键处理标记状态:向 GoogleMapsScreen 添加唯一键,以在标记集更新时强制小部件正确重建。
以下是代码的调整部分以及这些建议:
修改 GoogleMapsScreen 以确保正确的标记状态处理 使用带有唯一键的 widget.markers 来强制重建,并在标记为空时设置默认位置。
类 GoogleMapsScreen 扩展 StatefulWidget { 最终设置标记; GoogleMapsScreen({super.key, 必需 this.markers});
@覆盖 状态 createState() => _GoogleMapsScreenState(); }
类 _GoogleMapsScreenState 扩展状态 {
@覆盖 小部件构建(BuildContext上下文){ 返回谷歌地图( 初始相机位置:相机位置( 目标:widget.markers.isNotEmpty ? widget.markers.first.position : LatLng(33.8938, 35.5018), // 如果没有标记则默认位置 Zoom: 10, // 根据需要调整缩放级别 ), 标记:widget.markers, key: ValueKey(widget.markers), // 使用 ValueKey 在标记更改时强制重建 ); } }
更新MapViewModel中的prepareMarkers以验证标记数据 在添加标记之前检查每个分支是否具有有效坐标,以避免因空数据或无效数据而崩溃。
无效的prepareMarkers() { 标记 = 分支.where((分支) => 分支.纬度!= null && 分支.经度!= null).map((分支) { 返回标记( markerId: MarkerId(branch.id ?? ""), 位置:LatLng(分支.纬度??0.0,分支.经度??0.0), 信息窗口: 信息窗口( 标题:branch.englishBranchName ?? 《无名》, 片段:branch.phoneNumber ?? “没有电话”, ), ); }).toSet(); setMapLoading(假); }
添加调试以跟踪错误 在 getBranchesAPI 中添加调试行以捕获和记录否则可能不明显的意外问题。
Future getBranchesAPI({ required Function(String?) onError}) async { setMapLoading(true); 尝试 { var 响应 = 等待 repo.getBranches( 页码:MapConstants.pageNumber, pageSize:MapConstants.pageSize, 身体: {}, ); 响应.when( 成功:(NetworkBaseModel 响应)异步 { if (response.isSuccess == true) { 分支=响应.数据; 准备标记(); } 别的 { onError("加载数据失败"); } }, 失败:(网络异常错误){ onError(错误消息); setMapLoading(假); }, ); } 捕获 (e) { onError("发生意外错误:$e"); setMapLoading(假); } }
检查消费者中的标记 在显示地图之前使用 Consumer 确保 viewModel.markers 不为空:
展开( 孩子:消费者( 构建器:(上下文,视图模型,子级){ 如果(viewModel.isMapLoading){ 返回中心(子:CircularProgressIndicator()); } else if (viewModel.markers.isEmpty) { return Center(child: Text("没有可用的位置")); } 别的 { 返回 GoogleMapsScreen(标记:viewModel.markers); } }, ),
) 这种方法将有助于追踪任何问题并确保 GoogleMapsScreen 在渲染标记时具有一致且有效的数据。