import 'dart:async';
|
import 'dart:ui';
|
|
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/material.dart';
|
import 'package:flutter/rendering.dart';
|
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
|
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
|
import 'package:locations/api/http.dart';
|
import 'package:locations/model/map/location_model.dart';
|
import 'package:locations/ui/map/location_search.dart';
|
import 'package:locations/ui/widget/capture.dart';
|
import 'package:locations/ui/widget/map_marker.dart';
|
import 'package:locations/utils/global.dart';
|
import 'package:locations/utils/location_util.dart';
|
import 'package:locations/utils/map_util.dart';
|
import 'package:locations/utils/pageutils.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';
|
|
//控件阴影
|
List<BoxShadow> getViewShadow() {
|
return [
|
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});
|
|
BMFMapController? _mapController;
|
BMFMapOptions mapOptions = BMFMapOptions(
|
showMapScaleBar: false,
|
mapType: BMFMapType.Standard,
|
center: Global.currentPosition ?? BMFCoordinate(39.917215, 116.380341),
|
mapScaleBarPosition: BMFPoint(0, 200),
|
zoomLevel: 12,
|
mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 0));
|
|
//选择地图类型索引(0-2D 1-3D 2-卫星)
|
int selectMapTypeIndex = 0;
|
|
//是否正在录制轨迹
|
bool travelRECIng = false;
|
|
//路径录制管理器
|
TravelRECManager? _travelRECManager;
|
|
//测绘管理器
|
MeasureManager? _measureManager;
|
|
//用戶目前的位置
|
SimpleLocation? _currentMyPosition;
|
|
SimpleLocation? _currentTargetPosition;
|
|
BMFMarker? myMarker;
|
|
BMFMarker? targetUserMarker;
|
|
BMFMarker? myAddressMarker;
|
|
BMFMarker? 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;
|
|
void _init() {
|
//定位
|
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!.toBMFCoordinate(),
|
_currentMyPosition!.toBMFCoordinate()
|
]).then((value) {
|
_mapController!.updateMapOptions(BMFMapOptions(
|
zoomLevel: value.zoomLevel, center: value.center));
|
});
|
}
|
});
|
_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(BMFCoordinate(
|
value.location!.latitude!, value.location!.longitude!));
|
}
|
|
//设置现在的位置
|
_showTargetLocationMarker();
|
});
|
});
|
}
|
}
|
|
//显示位置信息的marker
|
void _showTargetLocationMarker() async {
|
if (_currentTargetPosition != null) {
|
//画头像
|
if (targetUserMarker != null) {
|
targetUserMarker!
|
.updatePosition(_currentTargetPosition!.toBMFCoordinate());
|
} else {
|
String value = await _captureTargetController.capturePng();
|
if (StringUtil.isNullOrEmpty(value)) {
|
return;
|
}
|
targetUserMarker = await MapUtil.addMarker(
|
_mapController, _currentTargetPosition!.toBMFCoordinate(), value);
|
}
|
|
//画地址
|
if (targetAddressUserMarker != null) {
|
targetAddressUserMarker!
|
.updatePosition(_currentTargetPosition!.toBMFCoordinate());
|
String value = await _captureTargetAddressController.capturePng();
|
await targetAddressUserMarker!.updateIcon(value);
|
} else {
|
String value = await _captureTargetAddressController.capturePng();
|
if (StringUtil.isNullOrEmpty(value)) {
|
return;
|
}
|
targetAddressUserMarker = await MapUtil.addMarker(
|
_mapController, _currentTargetPosition!.toBMFCoordinate(), value,
|
offset: BMFPoint(0, 2 * 64));
|
}
|
}
|
}
|
|
//显示位置信息的marker
|
void _showMyLocationMarker() async {
|
int? myUid = await UserUtil.getUid();
|
if (myUid == uid) {
|
return;
|
}
|
|
if (_currentMyPosition != null) {
|
//画头像
|
if (myMarker != null) {
|
myMarker!.updatePosition(_currentMyPosition!.toBMFCoordinate());
|
} else {
|
String value = await _captureMyController.capturePng();
|
myMarker = await MapUtil.addMarker(
|
_mapController, _currentMyPosition!.toBMFCoordinate(), value);
|
}
|
|
//画地址
|
if (myAddressMarker != null) {
|
myAddressMarker!.updatePosition(_currentMyPosition!.toBMFCoordinate());
|
String value = await _captureMyAddressController.capturePng();
|
myAddressMarker!.updateIcon(value);
|
} else {
|
String value = await _captureMyAddressController.capturePng();
|
myAddressMarker = await MapUtil.addMarker(
|
_mapController, _currentMyPosition!.toBMFCoordinate(), value,
|
offset: BMFPoint(0, 64 * 2));
|
}
|
}
|
}
|
|
@override
|
void initState() {
|
super.initState();
|
_currentTargetPosition = location;
|
}
|
|
@override
|
void dispose() {
|
super.dispose();
|
if (null != subscription) {
|
subscription!.cancel();
|
}
|
LocationUtil.stopLocation();
|
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 = "米";
|
});
|
}
|
}
|
|
//获取地图视图
|
Widget getMapView() {
|
return Container(
|
child: BMFMapWidget(
|
onBMFMapCreated: (controller) {
|
_mapController = controller;
|
_travelRECManager = TravelRECManager(_mapController);
|
_measureManager = MeasureManager(_mapController);
|
|
_init();
|
|
_mapController?.setMapOnClickedMapPoiCallback(
|
callback: (BMFMapPoi poi) {
|
//是否为测量模式
|
if (!_mesure) {
|
return;
|
}
|
_measureManager!.addPoint(poi.pt!).then((value) {
|
_showDistance(value);
|
});
|
});
|
_mapController?.setMapOnClickedMapBlankCallback(
|
callback: (BMFCoordinate coordinate) {
|
//是否为测量模式
|
if (!_mesure) {
|
return;
|
}
|
|
print("地图点击:${coordinate.toString()}");
|
|
_measureManager!.addPoint(coordinate).then((value) {
|
_showDistance(value);
|
});
|
});
|
},
|
mapOptions: mapOptions,
|
),
|
);
|
}
|
|
StreamSubscription<Map<String, Object>?>? subscription;
|
|
@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 getMapTypeView(String name, String imgPath, int index) {
|
return InkWell(
|
onTap: () {
|
setState(() {
|
selectMapTypeIndex = index;
|
});
|
if (index == 2) {
|
_mapController?.updateMapOptions(
|
BMFMapOptions(mapType: BMFMapType.Satellite));
|
} else if (index == 1) {
|
_mapController?.updateMapOptions(BMFMapOptions(
|
mapType: BMFMapType.Standard,
|
buildingsEnabled: true,
|
baseIndoorMapEnabled: true,
|
overlookEnabled: true));
|
_mapController?.showBaseIndoorMap(true);
|
} else if (index == 0) {
|
_mapController?.updateMapOptions(BMFMapOptions(
|
mapType: BMFMapType.Standard,
|
buildingsEnabled: false,
|
baseIndoorMapEnabled: false));
|
}
|
},
|
child: Column(
|
children: [
|
ClipRRect(
|
borderRadius: BorderRadius.circular(23),
|
child: Container(
|
alignment: Alignment.center,
|
color: selectMapTypeIndex == index
|
? ColorConstant.theme
|
: Color(0xFF9DAAB3),
|
width: 71,
|
height: 71,
|
child: Image.asset(
|
imgPath,
|
width: 65,
|
height: 65,
|
),
|
)),
|
Text(
|
name,
|
style: TextStyle(
|
fontSize: 14,
|
color: index == selectMapTypeIndex
|
? ColorConstant.theme
|
: Color(0xFF9DAAB3)),
|
)
|
],
|
));
|
}
|
|
Widget getContentView() {
|
//------------工具栏-----------
|
return share
|
? Stack(children: [
|
Positioned(
|
right: 10,
|
top: 10,
|
child: Container(
|
padding: 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!.toBMFCoordinate());
|
} 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) {
|
myAddressMarker!.updateVisible(!_showAddress);
|
}
|
|
if (targetAddressUserMarker != null) {
|
targetAddressUserMarker!
|
.updateVisible(!_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();
|
}
|
}
|