You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

切换Firebase数据库后Google Map页面Phenotype.API错误求助

切换Firebase数据库后Google Map页面出现DEVELOPER_ERROR错误

切换Firebase数据库后,Google Map的homepage.dart页面出现错误。已完成Firebase和Maps API的全部配置,包括使用flutterfire configure初始化项目及Android应用、配置SHA1密钥、重新生成Maps API密钥,但问题仍未解决。

错误信息:

W/MobStoreFlagStore(10088): java.util.concurrent.ExecutionException: m.few: 17: 17: API: Phenotype.API is not available on this device. Connection failed with: cmc{statusCode=DEVELOPER_ERROR, resolution=null, message=null}

相关代码:

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:geocoding/geocoding.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final Completer<GoogleMapController> googleMapCompleterController =
      Completer<GoogleMapController>();
  GoogleMapController? controllerGoogleMap;
  Position? currentPositionOfUser;
  late List<dynamic> _fletesList = [];
  Set<Marker> _markers = {};

  double calculateDistance(LatLng pos1, LatLng pos2) {
    return Geolocator.distanceBetween(
        pos1.latitude, pos1.longitude, pos2.latitude, pos2.longitude);
  }

  void updateMapTheme(GoogleMapController controller) {
    getJsonFileFromThemes("themes/night_style.json")
        .then((value) => setGoogleMapStyle(value, controller));
  }

  Future<String> getJsonFileFromThemes(String mapStylePath) async {
    ByteData byteData = await rootBundle.load(mapStylePath);
    var list = byteData.buffer
        .asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
    return utf8.decode(list);
  }

  setGoogleMapStyle(String setGoogleMapStyle, GoogleMapController controller) {
    controller.setMapStyle(setGoogleMapStyle);
  }

  Future<LatLng?> getLatLngFromAddress(String address) async {
    try {
      List<Location> locations = await locationFromAddress(address);
      if (locations.isNotEmpty) {
        double latitude = locations.first.latitude;
        double longitude = locations.first.longitude;
        return LatLng(latitude, longitude);
      }
    } catch (e) {
      print("Error obtaining coordinates: $e");
    }
    return null;
  }

  Future<void> _getFletesFromFirebase() async {
    DatabaseReference databaseReference =
        FirebaseDatabase.instance.ref().child('fletes');

    DatabaseEvent event = await databaseReference.once();
    DataSnapshot dataSnapshot = event.snapshot;

    if (dataSnapshot.value != null) {
      if (dataSnapshot.value is Map) {
        Map<dynamic, dynamic> mapData =
            dataSnapshot.value as Map<dynamic, dynamic>;

        if (mounted) {
          setState(() {
            mapData.forEach((key, value) async {
              String startAddress = value['startAddress'];
              String endAddress = value['endAddress'];

              LatLng? startCoords = await getLatLngFromAddress(startAddress);
              LatLng? endCoords = await getLatLngFromAddress(endAddress);

              if (startCoords != null) {
                _markers.add(
                  Marker(
                    markerId: MarkerId(key.toString()),
                    position: startCoords,
                    infoWindow: InfoWindow(
                      title: 'Inicio del Viaje',
                    ),
                    onTap: () {
                      _showFleteInfoModal(value);
                    },
                  ),
                );
              }

              if (endCoords != null) {
                _markers.add(
                  Marker(
                    markerId: MarkerId(key.toString() + "_end"),
                    position: endCoords,
                    infoWindow: InfoWindow(
                      title: 'Entrega del Viaje',
                    ),
                  ),
                );
              }
            });

            _addMarkersToMap();
          });
        }
      }
    }
  }

  void _showFleteInfoModal(Map<dynamic, dynamic> fleteInfo) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Detalles del Flete'),
          content: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                _buildInfoRow('Descripción', fleteInfo['description'] ?? ""),
                _buildInfoRow('Fecha', fleteInfo['date'] ?? ""),
                _buildInfoRow(
                    'Dirección de inicio', fleteInfo['startAddress'] ?? ""),
                _buildInfoRow(
                    'Dirección de entrega', fleteInfo['endAddress'] ?? ""),
                _buildInfoRow('Hora', fleteInfo['time'] ?? ""),
                _buildInfoRow(
                    'Tipo de vehículo', fleteInfo['vehicleType'] ?? ""),
                _buildInfoRow('Pago', fleteInfo['offerRate'] ?? ""),
              ],
            ),
          ),
          actions: [
            ElevatedButton(
              onPressed: () {
                // Lógica para aceptar el flete si es necesario
                Navigator.of(context).pop();
              },
              child: Text('Aceptar Flete'),
            ),
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: Text('Cerrar'),
            ),
          ],
        );
      },
    );
  }

  Widget _buildInfoRow(String title, String value) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 8.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            title,
            style: TextStyle(
              fontWeight: FontWeight.bold,
            ),
          ),
          SizedBox(height: 4),
          Text(value),
          Divider(
            color: Colors.grey,
            thickness: 0.5,
          ),
        ],
      ),
    );
  }

  void _addMarkersToMap() {
    _fletesList.forEach((flete) {
      double latitude = double.parse(flete['latitud'].toString());
      double longitude = double.parse(flete['longitud'].toString());
      LatLng fleteLocation = LatLng(latitude, longitude);

      _markers.add(
        Marker(
          markerId: MarkerId(
              flete['idflete'].toString()), // ID único del marcador
          position: fleteLocation,
          infoWindow: InfoWindow(
            title: flete['description'], // Muestra la descripción del flete
          ),
        ),
      );
    });
  }

  @override
  void initState() {
    super.initState();
    _getFletesFromFirebase();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          GoogleMap(
            padding: EdgeInsets.only(top: 100),
            mapType: MapType.normal,
            myLocationEnabled: true,
            initialCameraPosition: CameraPosition(
              target: LatLng(0, 0), // Coordenadas iniciales del mapa
              zoom: 12.0,
            ),
            onMapCreated: (GoogleMapController mapController) {
              controllerGoogleMap = mapController;

              updateMapTheme(controllerGoogleMap!);

              googleMapCompleterController.complete(controllerGoogleMap);

              // Llama a la función para obtener los marcadores después de que el mapa se haya creado
              _getCurrentLiveLocationOfDriver();
            },
            markers: _markers,
          ),
          Positioned(
            top: 16.0,
            right: 16.0,
            child: ElevatedButton(
              onPressed: () {
                // Llama a la función para obtener los marcadores al presionar el botón
                _getFletesFromFirebase();
              },
              child: Text('Mostrar viajes disponibles'),
            ),
          ),
        ],
      ),
    );
  }

  void _getCurrentLiveLocationOfDriver() async {
    Position positionOfUser = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.bestForNavigation);
    currentPositionOfUser = positionOfUser;

    LatLng positionOfUserInLatLng = LatLng(
        currentPositionOfUser!.latitude, currentPositionOfUser!.longitude);

    CameraPosition cameraPosition =
        CameraPosition(target: positionOfUserInLatLng, zoom: 15);

    controllerGoogleMap!
        .animateCamera(CameraUpdate.newCameraPosition(cameraPosition));
  }
}

解决步骤

  1. 验证Firebase配置文件
    确认新Firebase项目的google-services.json已完全替换旧文件,文件中的包名、SHA1/SHA256密钥与新项目控制台注册的信息完全匹配。

  2. 检查Maps API密钥限制

    • 确保API密钥绑定了正确的包名和SHA1值;
    • 确认已启用Maps SDK for Android和Geocoding API(代码中用到了地址转坐标功能)。
  3. 清理项目缓存
    执行flutter clean命令,删除旧构建缓存后重新编译项目,避免旧配置残留。

  4. 检查数据库权限
    临时将新Firebase数据库规则设置为公开读写(仅开发阶段验证),确认是否是权限导致的访问失败:

    {
      "rules": {
        ".read": true,
        ".write": true
      }
    }
    
  5. 修复代码异步逻辑问题
    _getFletesFromFirebase中mapData.forEach的异步操作无法在setState内正确等待完成,导致标记添加不及时,修改后的方法如下:

    Future<void> _getFletesFromFirebase() async {
      DatabaseReference databaseReference =
          FirebaseDatabase.instance.ref().child('fletes');
    
      DatabaseEvent event = await databaseReference.once();
      DataSnapshot dataSnapshot = event.snapshot;
    
      if (dataSnapshot.value != null && dataSnapshot.value is Map) {
        Map<dynamic, dynamic> mapData = dataSnapshot.value as Map<dynamic, dynamic>;
        Set<Marker> newMarkers = {};
    
        await Future.wait(mapData.entries.map((entry) async {
          String key = entry.key.toString();
          Map<dynamic, dynamic> value = entry.value;
    
          String startAddress = value['startAddress'];
          String endAddress = value['endAddress'];
    
          LatLng? startCoords = await getLatLngFromAddress(startAddress);
          LatLng? endCoords = await getLatLngFromAddress(endAddress);
    
          if (startCoords != null) {
            newMarkers.add(
              Marker(
                markerId: MarkerId(key),
                position: startCoords,
                infoWindow: const InfoWindow(title: 'Inicio del Viaje'),
                onTap: () => _showFleteInfoModal(value),
              ),
            );
          }
    
          if (endCoords != null) {
            newMarkers.add(
              Marker(
                markerId: MarkerId('${key}_end'),
                position: endCoords,
                infoWindow: const InfoWindow(title: 'Entrega del Viaje'),
              ),
            );
          }
        }));
    
        if (mounted) {
          setState(() {
            _markers = newMarkers;
            _addMarkersToMap();
          });
        }
      }
    }
    

内容的提问来源于stack exchange,提问作者Sergio Sanchez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:25:17