Search code examples
flutterreal-time-updates

Flutter get user location real time error: The setter 'latitude' was called on null


I am new to fultter. I am trying to get real time of user's location. For this, I used the location plugin (https://pub.dev/packages/location).

The issue is that the map is being populated before the location get assigned, and returning an error:

The setter 'latitude' was called on null.

This is the main dart:

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:location/location.dart';
import './models/userLocation.dart';

void main() => runApp(Maps());

class Maps extends StatefulWidget {
  @override
  _MapsState createState() => _MapsState();
}

class _MapsState extends State<Maps> {

  LocationData currentLocation;

  UserLocation _currentLocation;



  var location = new Location();


  String error;


  void _onMapCreated(GoogleMapController controller) {
    mapController = controller;
  }
  GoogleMapController mapController;

  void initState() {
    super.initState();

    initMapState();


  }

  initMapState() async {
    try {
      var userLocation = await location.getLocation();
      _currentLocation = UserLocation(
        latitude: userLocation.latitude,
        longitude: userLocation.longitude,
      );

    } on PlatformException catch (e) {
      if (e.code == 'PERMISSION_DENIED') {
        error = 'Permission denied';
      }
      return _currentLocation;

    }

    location.onLocationChanged().listen((LocationData currentLocation) {
      print(currentLocation.latitude);
      print(currentLocation.longitude);

    });
  }

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Create new trip'),
          backgroundColor: Colors.green[700],
        ),
        body: GoogleMap(
          onMapCreated: _onMapCreated,
          initialCameraPosition: CameraPosition(
            target:
                LatLng(_currentLocation?.latitude,_currentLocation?.longitude),
            zoom: 11.0,
          ),
        ),
      ),
    );
  }
}

Below is the location model:

class UserLocation {
   double latitude;
   double longitude;
  UserLocation({this.latitude, this.longitude});
}

Appreciate your support.

Thanks,

Edit:

Updated Working file:

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:location/location.dart';
import './models/userLocation.dart';

void main() => runApp(Maps());

class Maps extends StatefulWidget {
  @override
  _MapsState createState() => _MapsState();
}

class _MapsState extends State<Maps> {


  Location location = new Location();
  UserLocation userLocation;
  StreamSubscription<LocationData> positionSubscription;

  UserLocation _currentLocation;


  String error;


  void _onMapCreated(GoogleMapController controller) {
    mapController = controller;
  }
  GoogleMapController mapController;

  @override
  void initState() {
  super.initState();
  positionSubscription = location
      .onLocationChanged()
      .handleError((error) => print(error))
      .listen(
        (newPosition) => setState(() {
          _currentLocation = UserLocation(
            latitude: newPosition.latitude,
            longitude: newPosition.longitude,
          );
        }),
      );
}

@override
void dispose() {
  positionSubscription?.cancel();
  super.dispose();
}


  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Create new trip'),
          backgroundColor: Colors.green[700],
        ),
        body: GoogleMap(
          onMapCreated: _onMapCreated,
          initialCameraPosition: CameraPosition(
            target:
                LatLng(_currentLocation.latitude,_currentLocation.longitude),
            zoom: 11.0,
          ),
        ),
      ),
    );
  }
}

Solution

    • So, because of your initState is not using @override this method is not called

    • You can't use async calls in onInit callback. It is wrong. Again, you missed @override.

    • According to docs You don't need to call getLocation because you already have a subscription for locations.

    Ideally, you need something like this:

    UserLocation userPosition;
    StreamSubscription<LocationData> positionSubscription;
    
    @override
    void initState() {
      super.initState();
      positionSubscription = location
          .onLocationChanged()
          .handleError((error) => print(error))
          .listen(
            (newPosition) => setState(() {
              _currentLocation = UserLocation(
                latitude: newPosition.latitude,
                longitude: newPosition.longitude,
              );
            }),
          );
    }
    
    @override
    void dispose() {
      positionSubscription?.cancel();
      super.dispose();
    }