flutter - 如何在 Flutter Web 中检测用户位置并显示地址
问题描述
我的 Flutter Web 应用程序的地理定位和地理编码不起作用。我在这个站点的一些答案后编码以检测用户的位置,显示地址并将地址信息传递到下一页。我做了2页。在我的“地理定位”页面上,我制作了 2 个按钮。我想在用户单击第一个按钮时显示用户的地址。当他/她点击第二个按钮时,我想保存地址信息并传递到下一页。我怎么做?如果你知道 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 个按钮之一来显示地址,如下所示。
最后,您要按第二个按钮转到下一页,并显示如下地址:
解决步骤如下:
利用
locatioJS.dart
但对其进行调整,使其可以返回经纬度使用 Future 将经度和纬度保存在列表中。
使用 Map Box API 检索给定纬度和经度的地址(反向地理编码)。
在最后一步中, mapbox-Geocoding和mapbox-Search被用于反向地理编码,尽管它们Mapbox-Geocoding
似乎运作良好 -map-box search
为了完整起见,反向地理编码也包含在解决方案中,也许它可能适用于其他任何人。请注意,在编写此解决方案时,Geocoder和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);
}
随意调整它以适应您打算实现的更多目标。
最好的。
推荐阅读
- linux - 如何禁用 HAProxy?
- lua - 为什么 Roblox Studio 中的地点之间的传送功能不接受字符串表?
- ios - 发布视频数据 Swift
- json - 如何将 JSON 文件解析为 Ruby 对象
- react-native - 当我使用键盘时,我的组件正在压缩
- python - 如何使用参数(http 请求)作为条件进行查询 - 语法错误:意外的“<”
- haskell - 在 newtype 中包装一个类型
- python - 在 Cython 中更清洁的嵌套 for 循环而不损失效率
- excel - 如何使用 Application.OnKey 阻止用户保存文件?
- node.js - 使用 mongoose 查找与数组之一匹配的文档