import 'dart:async';
|
import 'dart:io';
|
import 'dart:ui';
|
|
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/material.dart';
|
import 'package:flutter/rendering.dart';
|
import 'package:locations/api/http.dart';
|
import 'package:locations/model/map/location_model.dart';
|
import 'package:locations/model/map/map_model.dart';
|
import 'package:locations/ui/widget/capture.dart';
|
import 'package:locations/ui/widget/map_marker.dart';
|
import 'package:locations/utils/event_bus_util.dart';
|
import 'package:locations/utils/location_util.dart';
|
import 'package:locations/utils/map_js_util.dart';
|
import 'package:locations/utils/string_util.dart';
|
import 'package:locations/utils/ui_constant.dart';
|
import 'package:locations/utils/ui_utils.dart';
|
import 'package:locations/utils/user_util.dart';
|
import 'package:webview_flutter/webview_flutter.dart';
|
import 'package:event_bus/event_bus.dart';
|
|
//控件阴影
|
List<BoxShadow> getViewShadow() {
|
return [
|
const BoxShadow(
|
blurRadius: 6.5,
|
spreadRadius: 1,
|
color: Color(0x4D0E96FF),
|
)
|
];
|
}
|
|
class MapPage extends StatefulWidget {
|
final String title;
|
|
//共享实时位置
|
final bool share;
|
final int? uid;
|
final SimpleLocation? location;
|
|
MapPage(this.title, {this.share = false, this.uid, this.location});
|
|
@override
|
_MapPageState createState() =>
|
_MapPageState(title, share: share, uid: uid, location: location);
|
}
|
|
class _MapPageState extends State<MapPage> with SingleTickerProviderStateMixin {
|
final String title;
|
|
//共享实时位置
|
final bool share;
|
final int? uid;
|
final SimpleLocation? location;
|
|
_MapPageState(this.title, {this.share = false, this.uid, this.location});
|
|
WebViewController? _webViewController;
|
|
//选择地图类型索引(0-2D 1-3D 2-卫星)
|
int selectMapTypeIndex = 0;
|
|
//是否正在录制轨迹
|
bool travelRECIng = false;
|
|
//路径录制管理器
|
TravelRECManager? _travelRECManager;
|
|
//测绘管理器
|
MeasureManager? _measureManager;
|
|
//用戶目前的位置
|
SimpleLocation? _currentMyPosition;
|
|
SimpleLocation? _currentTargetPosition;
|
|
Marker? myMarker;
|
|
Marker? targetUserMarker;
|
|
Marker? myAddressMarker;
|
|
Marker? targetAddressUserMarker;
|
|
//是否显示地址信息
|
bool _showAddress = true;
|
|
//是否需要测距
|
bool _mesure = false;
|
|
String _distance = "";
|
String _distanceUnit = "米";
|
|
Timer? _timer;
|
|
//
|
|
final CaptureController _captureMyController = CaptureController();
|
final CaptureController _captureTargetController = CaptureController();
|
|
final CaptureController _captureMyAddressController = CaptureController();
|
final CaptureController _captureTargetAddressController = CaptureController();
|
|
double? pixelRatio;
|
|
var eventBusMapClick;
|
|
void _init() {
|
if (location != null) {
|
MapUtil.setCenter(_webViewController, location!);
|
}
|
|
eventBusMapClick = eventBus.on<MapClickEventBus>().listen((event) {
|
//测距
|
if (_mesure) {
|
_measureManager!.addPoint(event.location!).then((value) {
|
_showDistance(value);
|
});
|
}
|
});
|
//定位
|
LocationUtil.startLocation(0, (state, map) {
|
if (state == LocationState.success) {
|
setState(() {
|
_currentMyPosition =
|
SimpleLocation.fromBaiDuLocation(BaiduLocation.fromJson(map!));
|
_showMyLocationMarker();
|
});
|
}
|
});
|
|
if (share && uid != null) {
|
Timer(const Duration(seconds: 1), () {
|
_showTargetLocationMarker();
|
//设置地图中心点与缩放等级
|
if (_currentTargetPosition != null && _currentMyPosition != null) {
|
MapUtil.getMapShowParams(
|
[_currentTargetPosition!, _currentMyPosition!]).then((value) {
|
MapUtil.setCenter(_webViewController, value.center);
|
MapUtil.setZoom(_webViewController, value.zoomLevel);
|
});
|
}
|
});
|
_showTargetLocationMarker();
|
//获取自己现在的位置
|
Timer.periodic(const Duration(seconds: 10), (timer) {
|
_timer = timer;
|
LocationApiUtil.getLocation(uid!.toString()).then((value) {
|
if (value == null) {
|
return;
|
}
|
|
setState(() {
|
_currentTargetPosition = value.location;
|
});
|
|
if (travelRECIng) {
|
_travelRECManager!.addLocation(value.location!);
|
}
|
|
//设置现在的位置
|
_showTargetLocationMarker();
|
});
|
});
|
}
|
}
|
|
//显示位置信息的marker
|
void _showTargetLocationMarker() async {
|
if (_currentTargetPosition != null) {
|
//画头像
|
if (targetUserMarker != null) {
|
MapMarkerUtil.update(_webViewController, targetUserMarker!.Id,
|
position: _currentTargetPosition);
|
} else {
|
String value = await _captureTargetController.capturePng();
|
if (StringUtil.isNullOrEmpty(value)) {
|
return;
|
}
|
targetUserMarker = await MapUtil.addMarker(
|
_webViewController, _currentTargetPosition!, value,
|
width: 40, height: 40);
|
}
|
|
//画地址
|
if (targetAddressUserMarker != null) {
|
MapMarkerUtil.update(_webViewController, targetAddressUserMarker!.Id,
|
position: _currentTargetPosition);
|
|
String value = await _captureTargetAddressController.capturePng();
|
await MapMarkerUtil.update(
|
_webViewController, targetAddressUserMarker!.Id,
|
icon: value);
|
} else {
|
String value = await _captureTargetAddressController.capturePng();
|
if (StringUtil.isNullOrEmpty(value)) {
|
return;
|
}
|
targetAddressUserMarker = await MapUtil.addMarker(
|
_webViewController, _currentTargetPosition!, value,
|
offset: Offset(0, 36 + 2), width: 132, height: 36);
|
}
|
|
//只有一个人
|
if (myMarker == null) {
|
MapUtil.setCenter(_webViewController, _currentTargetPosition!);
|
}
|
}
|
}
|
|
//显示位置信息的marker
|
void _showMyLocationMarker() async {
|
int? myUid = await UserUtil.getUid();
|
if (myUid == uid) {
|
return;
|
}
|
|
if (_currentMyPosition != null) {
|
//画头像
|
if (myMarker != null) {
|
MapMarkerUtil.update(_webViewController, myMarker!.Id,
|
position: _currentMyPosition);
|
} else {
|
String value = await _captureMyController.capturePng();
|
myMarker = await MapUtil.addMarker(
|
_webViewController, _currentMyPosition!, value,
|
width: 40, height: 40);
|
}
|
|
//画地址
|
if (myAddressMarker != null) {
|
MapMarkerUtil.update(_webViewController, myAddressMarker!.Id,
|
position: _currentMyPosition);
|
String value = await _captureMyAddressController.capturePng();
|
MapMarkerUtil.update(_webViewController, myAddressMarker!.Id,
|
icon: value);
|
} else {
|
String value = await _captureMyAddressController.capturePng();
|
myAddressMarker = await MapUtil.addMarker(
|
_webViewController, _currentMyPosition!, value,
|
offset: Offset(0, 36 + 2), width: 132, height: 36);
|
}
|
}
|
}
|
|
@override
|
void initState() {
|
super.initState();
|
if (Platform.isAndroid) {
|
WebView.platform = SurfaceAndroidWebView();
|
}
|
_currentTargetPosition = location;
|
}
|
|
@override
|
void dispose() {
|
super.dispose();
|
if (eventBusMapClick != null) {
|
eventBusMapClick.cancel();
|
}
|
if (_timer != null) {
|
_timer!.cancel();
|
}
|
}
|
|
_showDistance(double distance) {
|
print("距离:$distance");
|
|
if (distance > 1000) {
|
setState(() {
|
_distance = (distance / 1000).toStringAsFixed(2);
|
_distanceUnit = "千米";
|
});
|
} else {
|
setState(() {
|
_distance = distance.toStringAsFixed(0);
|
_distanceUnit = "米";
|
});
|
}
|
}
|
|
WebView? _webView;
|
|
//获取地图视图
|
Widget getMapView() {
|
_webView ??= getMapWebView(context, (controller) {
|
_webViewController = controller;
|
|
//TODO 测量模式
|
|
// _webViewController?.setMapOnClickedMapPoiCallback(
|
// callback: (BMFMapPoi poi) {
|
// //是否为测量模式
|
// if (!_mesure) {
|
// return;
|
// }
|
// _measureManager!.addPoint(poi.pt!).then((value) {
|
// _showDistance(value);
|
// });
|
// });
|
// _webViewController?.setMapOnClickedMapBlankCallback(
|
// callback: (BMFCoordinate coordinate) {
|
// //是否为测量模式
|
// if (!_mesure) {
|
// return;
|
// }
|
//
|
// print("地图点击:${coordinate.toString()}");
|
//
|
// _measureManager!.addPoint(coordinate).then((value) {
|
// _showDistance(value);
|
// });
|
// });
|
}, pageFinish: (url) {
|
_travelRECManager = TravelRECManager(_webViewController);
|
_measureManager = MeasureManager(_webViewController);
|
_init();
|
});
|
return _webView!;
|
}
|
|
@override
|
Widget build(BuildContext context) {
|
pixelRatio ??= DimenUtil.getPixelRatio(context);
|
|
print("pixelRatio:$pixelRatio");
|
|
return Scaffold(
|
resizeToAvoidBottomInset: false,
|
backgroundColor: Colors.white,
|
body: Stack(children: [
|
getMapView(),
|
Flex(direction: Axis.vertical, children: [
|
//搜索框
|
Container(
|
margin: const EdgeInsets.fromLTRB(10, 35, 10, 11),
|
child: //----------搜索框--------
|
Flex(
|
direction: Axis.horizontal,
|
children: [
|
Expanded(
|
child: Container(
|
alignment: Alignment.center,
|
padding: EdgeInsets.fromLTRB(13, 0, 13, 0),
|
height: 45,
|
decoration: BoxDecoration(
|
color: Colors.white,
|
borderRadius: BorderRadius.circular(10),
|
boxShadow: getViewShadow()),
|
child: Stack(alignment: Alignment.center, children: [
|
Row(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
InkWell(
|
onTap: () {
|
print("退出");
|
Navigator.of(context).pop();
|
},
|
child: Row(children: [
|
const Image(
|
image: AssetImage(
|
"assets/images/common/icon_back.png",
|
),
|
height: 17),
|
Container(
|
width: 8,
|
),
|
const Text(
|
"退出",
|
style: TextStyle(
|
fontSize: 16,
|
color: ColorConstant.title),
|
)
|
])),
|
Container(
|
width: 10,
|
),
|
const Expanded(child: Text("")),
|
],
|
),
|
Text(
|
title,
|
style: const TextStyle(
|
fontSize: 16, color: ColorConstant.title),
|
),
|
]))),
|
],
|
),
|
),
|
|
Expanded(child: getContentView())
|
]),
|
_mesure
|
? Align(
|
alignment: Alignment.bottomCenter,
|
child: Container(
|
height: 92,
|
margin: const EdgeInsets.only(left: 10, right: 10),
|
decoration: BoxDecoration(
|
color: Colors.white,
|
boxShadow: getViewShadow(),
|
borderRadius: const BorderRadius.only(
|
topLeft: Radius.circular(10),
|
topRight: Radius.circular(10))),
|
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
children: [
|
Text.rich(TextSpan(
|
children: <TextSpan>[
|
const TextSpan(text: '距离:'),
|
TextSpan(
|
text: "$_distance",
|
style: const TextStyle(
|
fontWeight: FontWeight.bold)),
|
TextSpan(text: _distanceUnit),
|
],
|
style: const TextStyle(
|
color: ColorConstant.theme, fontSize: 15),
|
)),
|
const SizedBox(
|
height: 20,
|
),
|
SizedBox(
|
height: 28,
|
child: Row(
|
children: [
|
Expanded(
|
child: InkWell(
|
onTap: () {
|
_measureManager!
|
.backPoint()
|
.then((value) {
|
_showDistance(value);
|
});
|
},
|
child: Row(
|
mainAxisAlignment:
|
MainAxisAlignment.center,
|
children: [
|
Image.asset(
|
"assets/images/map/icon_map_measure_back.png",
|
height: 15.5,
|
),
|
const SizedBox(
|
width: 9.5,
|
),
|
const Text(
|
"回退",
|
style: TextStyle(
|
fontSize: 15,
|
color: ColorConstant.theme),
|
)
|
],
|
))),
|
Container(
|
height: 28,
|
width: 1,
|
color: ColorConstant.theme,
|
),
|
Expanded(
|
child: InkWell(
|
onTap: () {
|
_measureManager!.clear();
|
_showDistance(0);
|
},
|
child: Row(
|
mainAxisAlignment:
|
MainAxisAlignment.center,
|
children: [
|
Image.asset(
|
"assets/images/map/icon_map_measure_delete.png",
|
height: 15.5,
|
),
|
const SizedBox(
|
width: 9.5,
|
),
|
const Text(
|
"删除",
|
style: TextStyle(
|
fontSize: 15,
|
color: ColorConstant.theme),
|
)
|
],
|
))),
|
],
|
),
|
)
|
],
|
),
|
))
|
: Container(),
|
Positioned(
|
top: -100,
|
child: Row(children: [
|
CaptureWidget(
|
widget: PersonLocationMarker(Image.asset(
|
"assets/images/mine/icon_mine_default_portrait.png",
|
)),
|
captureController: _captureMyController,
|
),
|
CaptureWidget(
|
widget: PersonLocationMarker(
|
Image.asset(
|
"assets/images/mine/icon_mine_default_portrait.png",
|
),
|
borderColor: const Color(0xFFFF1F35),
|
),
|
captureController: _captureTargetController,
|
),
|
])),
|
Positioned(
|
top: -100,
|
child: Column(children: [
|
CaptureWidget(
|
widget: (_currentMyPosition != null &&
|
_currentMyPosition!.addressDetail != null)
|
? Container(
|
width: 122,
|
height: 37,
|
decoration: BoxDecoration(
|
color: ColorConstant.theme.withAlpha(200),
|
borderRadius: BorderRadius.circular(2.5)),
|
child: Column(
|
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
Text(
|
"${_currentMyPosition!.addressDetail!}",
|
overflow: TextOverflow.ellipsis,
|
style: const TextStyle(
|
fontSize: 12, color: Colors.white),
|
),
|
Container(
|
height: 2,
|
),
|
Text(
|
"N ${_currentMyPosition!.latitude}, E ${_currentMyPosition!.longitude}",
|
style: const TextStyle(
|
fontSize: 7, color: Colors.white)),
|
],
|
))
|
: Container(),
|
captureController: _captureMyAddressController,
|
),
|
CaptureWidget(
|
widget: (_currentTargetPosition != null &&
|
_currentTargetPosition!.addressDetail != null)
|
? Container(
|
width: 122,
|
height: 37,
|
decoration: BoxDecoration(
|
color: const Color(0xFFFF1F35).withAlpha(200),
|
borderRadius: BorderRadius.circular(2.5)),
|
child: Column(
|
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
Text(
|
"${_currentTargetPosition!.addressDetail!}",
|
overflow: TextOverflow.ellipsis,
|
style: const TextStyle(
|
fontSize: 12, color: Colors.white),
|
),
|
Container(
|
height: 2,
|
),
|
Text(
|
"N ${_currentTargetPosition!.latitude}, E ${_currentTargetPosition!.longitude}",
|
style: const TextStyle(
|
fontSize: 7, color: Colors.white)),
|
],
|
))
|
: Container(),
|
captureController: _captureTargetAddressController,
|
),
|
]))
|
]));
|
}
|
|
Widget getContentView() {
|
//------------工具栏-----------
|
return share
|
? Stack(children: [
|
Positioned(
|
right: 10,
|
top: 10,
|
child: Container(
|
padding: const EdgeInsets.only(top: 13, bottom: 13),
|
width: 43,
|
decoration: BoxDecoration(
|
color: Colors.white,
|
borderRadius: BorderRadius.circular(7.5)),
|
child: Column(
|
children: [
|
InkWell(
|
onTap: () {
|
if (!travelRECIng) {
|
if (_currentTargetPosition == null) {
|
ToastUtil.toast("尚未获取到对方位置");
|
return;
|
}
|
|
_travelRECManager!
|
.startREC(_currentTargetPosition!);
|
} else {
|
//结束录制
|
_travelRECManager!.stopREC();
|
}
|
|
setState(() {
|
travelRECIng = !travelRECIng;
|
});
|
},
|
child: Column(
|
children: [
|
Image.asset(
|
travelRECIng
|
? "assets/images/map/icon_rec_location_ing.png"
|
: "assets/images/map/icon_rec_location.png",
|
width: 21,
|
),
|
Container(
|
height: 2,
|
),
|
Text(travelRECIng ? "录制中.." : "录制轨迹",
|
style: TextStyle(
|
fontSize: 7,
|
color: travelRECIng
|
? Color(0xFFFF1F35)
|
: Color(0xFF9DAAB3)))
|
],
|
),
|
),
|
Container(
|
height: 14,
|
),
|
InkWell(
|
onTap: () {
|
if (myAddressMarker != null) {
|
MapMarkerUtil.update(
|
_webViewController, myAddressMarker!.Id,
|
visibility: !_showAddress);
|
}
|
|
if (targetAddressUserMarker != null) {
|
MapMarkerUtil.update(
|
_webViewController, targetAddressUserMarker!.Id,
|
visibility: !_showAddress);
|
}
|
|
setState(() {
|
_showAddress = !_showAddress;
|
});
|
},
|
child: Column(
|
children: [
|
Image.asset(
|
_showAddress
|
? "assets/images/map/icon_map_view_location.png"
|
: "assets/images/map/icon_map_no_view_location.png",
|
width: 21,
|
),
|
Container(
|
height: 2,
|
),
|
const Text("显示位置",
|
style: TextStyle(
|
fontSize: 7, color: Color(0xFF9DAAB3)))
|
],
|
),
|
),
|
Container(
|
height: 14,
|
),
|
InkWell(
|
onTap: () {
|
setState(() {
|
//测距初始化
|
_showDistance(0);
|
_measureManager!.clear();
|
|
_mesure = !_mesure;
|
});
|
},
|
child: Column(
|
children: [
|
Image.asset(
|
_mesure
|
? "assets/images/map/icon_map_no_measure.png"
|
: "assets/images/map/icon_map_measure.png",
|
width: 21,
|
),
|
Container(
|
height: 2,
|
),
|
const Text("测量距离",
|
style: TextStyle(
|
fontSize: 7, color: Color(0xFF9DAAB3)))
|
],
|
),
|
)
|
],
|
),
|
)),
|
])
|
: Container();
|
}
|
}
|