google-mapsgoogle-cloud-platformgoogle-geocoding-api

`geocoding` Flutter: `locationFromAddress` throws "Unexpected null value"


/// Searches for an address and moves the map
Future<void> _searchAddress() async {
    String query = _searchController.text.trim();
    if (query.isEmpty) {
      debugPrint("Search query is empty.");
      return;
    }

    try {
      debugPrint("Calling locationFromAddress for: $query");
      List<Location> locations = await locationFromAddress(query);
      debugPrint("locationFromAddress response: $locations");
      if (locations.isEmpty) {
        throw Exception("No locations found for the query.");
      }
    } catch (e) {
      debugPrint("locationFromAddress error: $e"); // Outputs "locationFromAddress error: Unexpected null value."
    }
  }
}

Here's the full screen if it helps:

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

class FindAddressScreen extends StatefulWidget {
  const FindAddressScreen({super.key});

  @override
  State<FindAddressScreen> createState() => _FindAddressScreenState();
}

class _FindAddressScreenState extends State<FindAddressScreen> {
  LatLng _selectedLocation =
      LatLng(37.7749, -122.4194);
  final TextEditingController _searchController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Select Address'),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.pop(context, _selectedLocation);
            },
            child: Text("Conferma"),
          )
        ],
      ),
      body: Stack(
        children: [
          GoogleMap(
            initialCameraPosition: CameraPosition(
              target: _selectedLocation,
              zoom: 14,
            ),
            onMapCreated: (controller) {

            },
            onLongPress: _updateLocation,
            markers: {
              Marker(
                markerId: MarkerId("selected-location"),
                position: _selectedLocation,
              )
            },
          ),
          Positioned(
            top: 10,
            left: 10,
            right: 10,
            child: Card(
              child: Row(
                children: [
                  Expanded(
                    child: TextField(
                      controller: _searchController,
                      decoration: InputDecoration(
                        hintText: "Search address",
                        contentPadding: EdgeInsets.symmetric(
                          vertical: 10,
                          horizontal: 15,
                        ),
                        border: InputBorder.none,
                      ),
                    ),
                  ),
                  IconButton(
                    icon: Icon(Icons.search),
                    onPressed: _searchAddress,
                  ),
                ],
              ),
            ),
          )
        ],
      ),
    );
  }

  /// Updates the selected location
  Future<void> _updateLocation(LatLng position) async {
    setState(() {
      _selectedLocation = position;
    });
  }

  /// Searches for an address and moves the map
  Future<void> _searchAddress() async {
    String query = _searchController.text.trim();
    if (query.isEmpty) {
      debugPrint("Search query is empty.");
      return;
    }

    try {
      debugPrint("Calling locationFromAddress for: $query");
      List<Location> locations = await locationFromAddress(query);
      debugPrint("locationFromAddress response: $locations");
      if (locations.isEmpty) {
        throw Exception("No locations found for the query.");
      }
    } catch (e) {
      debugPrint("locationFromAddress error: $e");
    }
  }
}

I'm running the app in Chrome with flutter run.

I've enabled the Geocoding API for my project and put my API key in the index.html, which has appropriate default restrictions (Geocoding API is enabled).

Searching an address with a direct http call DOES WORK instead:

import 'package:http/http.dart' as http;
import 'dart:convert';

// ...

  Future<void> _searchAddress() async {
    String query = _searchController.text.trim();
    if (query.isEmpty) {
      debugPrint("Search query is empty.");
      return;
    }

    final apiKey = "YOUR_API_KEY"; // Replace with your API key
    final url = Uri.parse(
      "https://maps.googleapis.com/maps/api/geocode/json?address=${Uri.encodeComponent(query)}&key=$apiKey");

    try {
      final response = await http.get(url);
      if (response.statusCode == 200) {
        final data = json.decode(response.body);
        if (data['results'] != null && data['results'].isNotEmpty) {
          final location = data['results'][0]['geometry']['location'];
          LatLng target = LatLng(location['lat'], location['lng']);
          _moveCamera(target);
          _updateLocation(target);
          debugPrint("Address found: $location");
        } else {
          throw Exception("No results found.");
        }
      } else {
        throw Exception("Failed to fetch geocoding data.");
      }
    } catch (e) {
      debugPrint("Google Maps search error: $e");
    }
  }
}

What should I check?

google_maps_flutter 2.10.0 geocoding 3.0.0 http 1.2.2

Chrome 131.0.6778.86. Flutter 3.24.4. Dart 3.5.4. VS Code 1.95.3. macOS 15.0, MacBook Air M1 8GB.


Solution

  • It turns out, the package does not work for web.

    The words "web" and "Chrome" do not appear in the docs, although it can be read that "This plugin uses the free Geocoding services provided by the iOS and Android platforms. This means that there are restrictions to their use.", which means that "The current geocoding plugin is only for iOS and Android available.", as can be read in this proposal.