2

Geolocation and geocoding for my flutter web app doesn't work. I coded after some answer from this site to detect user's location, show the address and pass the address information to the next page. I made 2 pages. On my 'geolocating' page, I made 2 buttons. I want to show user's address when the user clicks the first button. When he/she clicks the second button, I want to save the address information and pass to the next page. How do I do that? If you know about flutter web, please give me some tips. Here is one of my pages 'locationjs.dart';

    @JS('navigator.geolocation') // navigator.geolocation 
      namespace
    library jslocation; // library name can be whatever you 
    want

    import "package:js/js.dart";

    @JS('getCurrentPosition') // Accessing method 
     getCurrentPosition from       Geolocation API
     external void getCurrentPosition(Function 
     success(GeolocationPosition pos));

    @JS()
    @anonymous
    class GeolocationCoordinates {
    external double get latitude;
    external double get longitude;
    external double get altitude;
    external double get accuracy;
    external double get altitudeAccuracy;
    
    external factory GeolocationCoordinates(
    {double latitude,
     double longitude,
  double altitude,
  double accuracy,
  double altitudeAccuracy,
  });
  }

  @JS()
  @anonymous
  class GeolocationPosition {
  external GeolocationCoordinates get coords;

  external factory 
  GeolocationPosition({GeolocationCoordinates coords});
  }

Secondly, I coded the screen page like following. On the screen, the first button click is for getting longitude and latitude and showing user's address, and the second button is for saving the information and going to the next page. What should I write to do that?;

   import 'package:flutter/foundation.dart';
   import 'package:practice/models/user.dart';
   import 'package:practice/shared/choice.dart';
   import 'package:practice/shared/locationJs.dart';
   import 'package:flutter/material.dart';
   import 'package:js/js.dart';
   import 'package:geolocator/geolocator.dart';

  class Geolocating extends StatefulWidget {
  @override
  _GeolocatingState createState() => _GeolocatingState();
   }

  class _GeolocatingState extends State<Geolocating> {
 User user = User();
   
  void getCurrentPosition() async {
  Position position = await Geolocator()
    .getCurrentPosition(desiredAccuracy: 
  LocationAccuracy.low);

  print(position);
  }

  @override
  Widget build(BuildContext context) {
   return Scaffold(
     body: Center(
      child: Container(
        child: Column(
          children: [
            RaisedButton(child: Text('GPS coordinates'),
               onPressed: () {
               _getCurrentLocation();//get location and show address
              }),
            RaisedButton(
               child: Text('address confirmed'),
               onPressed: () {//save the info and pass to next page
               userModel.address = 
                _getCurrentLocation.toString();
               Navigator.push(context,
                    MaterialPageRoute(
                        builder: (context) => 
               NextPage(userModel: userModel)));
              }),
             ],
            ),
           ),
          ),
         );
        }
       }

 success(pos) {
  try {
  Text(pos.coords.latitude);
  Text(pos.coords.longitude);
} catch (ex) {
  print("Exception thrown : " + ex.toString());
 }
}

 _getCurrentLocation() {
   if (kIsWeb) {
   getCurrentPosition(allowInterop((pos) => success(pos)));
  }
}

I also upload my screenshot with 2 buttons;https://i.stack.imgur.com/gGNyo.jpg

kh h
  • 59
  • 2
  • 6
  • Are you resolve this problem on web flutter? https://stackoverflow.com/questions/64411270/flutter-web-get-location-on-web-by-location-plugin – Cyrus the Great Oct 18 '20 at 09:18
  • No, I am about to give up showing google map and showing user's address. I am thinking about autocomplete address instead. How about you, sayres? Did you solve yours? – kh h Oct 18 '20 at 11:53
  • No , unfortunately I stuck yet :-( – Cyrus the Great Oct 18 '20 at 11:55

1 Answers1

0

I hope this solution might help - and I am sure it can be improved. I guess the picture below depicts what you want to do.

The Home screen is below.

Home Screen

The you want to press one of the 2 buttons to display address as shown below.

press first Button

Lastly you want to press the second button to go to next page and also display the address as below:

Next page

The solutions steps are listed below:

  1. Make use of locatioJS.dart but adapt it so that it can return the latitude and longitude

  2. Use Future to save longitude and latitude in a list.

  3. Use Map Box API to retrieve the address given the latitude and longitude (Reverse geocoding).

In the last step mapbox-Geocoding and mapbox-Search were used for the reverse geocoding though the Mapbox-Geocoding seem to work well - map-box search reverse geocoding is also included in the solution for the sake of completeness, perhaps it might work for anyone else. Note that as of writing this solution Geocoder and Geolocator do not seem to work well with flutter web.

I will provide here the full code soon.(Enjoy!)

Dependencies

cupertino_icons: ^1.0.0
js: ^0.6.2
mapbox_search: ^2.0.1+1
mapbox_geocoding: ^0.1.3

locatioJS.dart

// Change the line below
external void  getCurrentPosition(success(GeolocationPosition pos));
// To this one 
external void  getCurrentPosition(Future<void> 
success(GeolocationPosition pos));

main.dart

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'locationJs.dart';
import 'package:js/js.dart';
import 'package:mapbox_search/mapbox_search.dart';
import 'package:mapbox_geocoding/mapbox_geocoding.dart';
import 'package:mapbox_geocoding/model/reverse_geocoding.dart';


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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        // Provide a function to handle named routes. Use this function to
        // identify the named route being pushed, and create the correct
        // Screen.
        onGenerateRoute: (settings) {
          // If you push the PassArguments route
          if (settings.name == PassArgumentsScreen.routeName)  {
            // Cast the arguments to the correct type: ScreenArguments.
            final ScreenArguments args = settings.arguments;

            // Then, extract the required data from the arguments and
            // pass the data to the correct screen.
            return MaterialPageRoute(
              builder: (context) {
                return PassArgumentsScreen(
                  title: args.title,
                  message: args.message,
                );
              },
            );
          }
          // The code only supports PassArgumentsScreen.routeName right now.
          // Other values need to be implemented if we add them. The assertion
          // here will help remind us of that higher up in the call stack, since
          // this assertion would otherwise fire somewhere in the framework.
         assert(false, 'Need to implement ${settings.name}');
         return null;
        },
        title: 'Navigation with Arguments',
        home: HomeScreen(),
      
      
        );
  }
}

class HomeScreen extends StatefulWidget {
@override 
_HomeScreen createState() => new _HomeScreen();
}


class _HomeScreen extends State<HomeScreen> {
 
  // variables for keeoing track of state
  bool loading = false;
  List mylatlong ;  // store lat long here
  String currentAddress = 'Not found';  // for the address
  bool pressGeoON = false;  // for keeping track of button state
  bool cmbscritta = false;  // for keeping track of button state
/////////////////////////////////////////////////////////////////////////////////////
     // this sets up the map box api .//
     // open map box account. Then go to your dashboard and copy the APIKey or Token
    //Set up a test api key before running for maboxsearch package
     String apiKey = "paste your Mapbox API key (Token ) here"; 
    // Map-box geocoding requires the initialisation below
     MapboxGeocoding geocoding = MapboxGeocoding("paste your Mapbox API key (Token ) here");     //////////////////////////////////////////////////////////////////////////////////////////
  // set the information needed the lat long here 
  Future _getUserInfo() async {
        setState(() {
          loading = true;
        });
         // call loc here to initiate and get the lat long
         // loc is defined down in the code : line 109
          loc();
        setState(() {
          loading = false;
        });
        
    }

  // function for succesful collecting the lat long 
  success(pos) async   {
          List mypos;
          try {
          //print(pos.coords.latitude); 
          //print(pos.coords.longitude);
          mypos = [pos.coords.latitude, pos.coords.longitude ];                
          } catch (ex) {
            print("Exception thrown : " + ex.toString());
            //user = await GetUserInfo.getInfo(kola);
          }
        // once the lat long is retrieved set the values and sassign to th my lat logn variable
        setState(() {
            mylatlong =   mypos;
            print(mylatlong);
          });       
 }

// define future for getting the lat long
// loc is called in getUserInfo (line 78) which is in turn called in initState
Future<void> loc()  async {
  //check if web.
   if (kIsWeb) {
    // get the the lat long by calling success.  Note allowInterop 
    // which enables Returns a wrapper around a function f  that can be called from JavaScript using package:js 
    // cross check the definition of getCurrentPosition in locatioJS.dart (line 7)
   getCurrentPosition(allowInterop((pos) => success(pos)));
  
  }
 
}

//Reverse geocoding package for getting city name. Get place/city name from latitude and longitude.
  getCity(double lat, double lng) async {
  
    try {
      ReverseGeocoding reverseModel =
          await geocoding.reverseModel(lat, lng, limit: 7, types: 'region'); //types can be region, district, neighborhood see https://docs.mapbox.com/api/search/#data-types
      print(reverseModel.features[0].placeName);
      return (reverseModel.features[0].placeName);
    } catch (Excepetion) {
      return 'Reverse Geocoding Error';
    }
  }

// this future to get getAddressFromLatLng  to return the city name
Future<void> testkey() async {
  await getAddressFromLatLng(apiKey).catchError(print);
  //await placesSearch(apiKey).catchError(print);
}

// the function icalls geocity and uses it to get city name/address
Future getAddressFromLatLng(String apiKey) async {
  
  // *********** using map box search ********************************//
  // this gives empty string sometimes
  // initialise mapbox search api
  var geoCodingService = ReverseGeoCoding(
    apiKey: apiKey,
    //country: "BR",
    limit: 5,
  );
  // get the lat ling stores in the variable mylatlong (line 72) . My latlong is set by loc (line 119)
  var lat = mylatlong[0];
  var long = mylatlong[1];
  // get the address
  var addresses = await geoCodingService.getAddress(Location(
    lat: lat, //-19.984846,
    lng: long , //-43.946852,
  ));
 print(addresses); // print to view resulta
// ***********map box searxh ends here ********************************//

// *********** using map box geocoding ********************************//
// this calls th geoCity function (line 133) and uses map  geocoding to retrieve 
 var newadr = await getCity(lat, long);
  print('the mapbox geocoding api returns $newadr');
// *********** using map box geocoding ********************************//

// set the address to the value returned by mapbox geocoding 
  setState(() {
             currentAddress = newadr ;
          });
/////////////////////////////////////////

}

@override
  void initState() {

    _getUserInfo();
    super.initState();

  }
  @override
  Widget build(BuildContext context) {
   
    
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // A button that navigates to a named route that. The named route
            // extracts the arguments by itself.
            ElevatedButton(
              child: Text("Go to Page 2"),
              onPressed: () {
                 testkey();
                // When the user taps the button, navigate to a named route
                // and provide the arguments as an optional parameter.
                Navigator.pushNamed(

                  
                  context,
                  PassArgumentsScreen.routeName,
                  arguments: ScreenArguments(
                    'Welcome to page 2 ',
                    'This was retrieved from lat long $mylatlong and your adress is $currentAddress',
                  ),
                );
                //getAddressFromLatLng();
               
              },
            ),
            // A button that navigates to a named route. For this route, extract
            // the arguments in the onGenerateRoute function and pass them
            // to the screen.
            ElevatedButton(
              child: cmbscritta ? Text("Geolocation is $mylatlong , address $currentAddress ") : Text("Display lat long"), //Text("Navigate to a named that accepts arguments"),
              onPressed: () {
                // When the user taps the button, navigate to a named route
                // and provide the arguments as an optional parameter.
                testkey();
                setState(() {
                pressGeoON = !pressGeoON;
                cmbscritta = !cmbscritta;
                
                //getAddressFromLatLng();
              });
              
              },
            ),
          ],
        ),
      ),
    );
  }
}
class PassArgumentsScreen extends StatelessWidget {
  static const routeName = '/passArguments';

  final String title;
  final String message;

  // This Widget accepts the arguments as constructor parameters. It does not
  // extract the arguments from the ModalRoute.
  //
  // The arguments are extracted by the onGenerateRoute function provided to the
  // MaterialApp widget.
  const PassArgumentsScreen({
    Key key,
    @required this.title,
    @required this.message,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Text(message),
      ),
    );
  }
}
// You can pass any object to the arguments parameter. In this example,
// create a class that contains both a customizable title and message.
class ScreenArguments {
  final String title;
  final String message;

  ScreenArguments(this.title, this.message);
}

Feel free to adapt this to fit more what you intend to achieve.

Best.

smile
  • 574
  • 5
  • 11
  • 1
    Hi, Smile. It works well. Thanks. But one problem is that the address is not detailed enough. Can I get more detailed address? For example, I want to get at least district name and city name but I have only provice name and country name. – kh h Nov 13 '20 at 02:42
  • Hi, this line: ` await geocoding.reverseModel(lat, lng, limit: 7, types: 'region'); //types can be region, district, neighborhood see https://docs.mapbox.com/api/search/#data-types ` might be relevant. Change region to district and also see information on the link: I do not know if mapbox places any restriction on getting district information though. Best – smile Nov 13 '20 at 02:51
  • Thanks! One last question, how do you show address in Korean, not in English? – kh h Nov 13 '20 at 03:06
  • I dont know if the disussion in the link below might work https://stackoverflow.com/questions/55889889/how-to-change-a-flutter-app-language-without-restarting-the-app or use translator package https://pub.dev/packages/translator. There might be better ways to go about this. Best – smile Nov 13 '20 at 03:35
  • Thanks a lot, Smile. Although translation problem remains unsolved, I appreciate your time and detailed solution :) – kh h Nov 13 '20 at 04:15
  • You are welcome. You can post the translation problem separately so that you can get a lot of perspectives on possible solutions. Best – smile Nov 13 '20 at 10:16