如何检测用户位置并在 flutter web 中显示地址

How to detect user location and show address in flutter web

我的 Flutter Web 应用的地理定位和地理编码不起作用。我在从这个网站得到一些答案后编码来检测用户的位置,显示地址并将地址信息传递到下一页。我做了2页。在我的 'geolocating' 页面上,我制作了 2 个按钮。我想在用户单击第一个按钮时显示用户的地址。当he/she点击第二个按钮时,我想保存地址信息并传递到下一页。我怎么做?如果你了解 flutter web,请给我一些提示。 这是我的一个页面 '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});
  }

其次,我对屏幕页面进行了编码,如下所示。在屏幕上,点击第一个按钮是获取经纬度和显示用户地址,点击第二个按钮是保存信息和进入下一页。我应该写些什么来做到这一点?;

   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)));
  }
}

我还上传了带有 2 个按钮的屏幕截图;https://i.stack.imgur.com/gGNyo.jpg

我希望这个解决方案可能有所帮助 - 我相信它可以得到改进。我猜下面的图片描述了你想要做什么。

主屏幕如下。

您想要按 2 个按钮之一显示地址,如下所示。

最后你想按第二个按钮转到下一页,并显示如下地址:

解决步骤如下:

  1. 利用locatioJS.dart但对其进行调整,使其可以return纬度和经度

  2. 使用 Future 将经度和纬度保存在列表中。

  3. 使用地图框 API 检索给定经纬度的地址(反向地理编码)。

在最后一步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 似乎与 flutter web 不兼容。

我会尽快在这里提供完整的代码。(享受吧!)

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);
}

请随意调整它以适应您想要实现的目标。

最佳。