admin
2021-11-27 3465ac6e980f1473e4f42ba3eaafc7815423efec
功能完善
18个文件已修改
3个文件已添加
1843 ■■■■ 已修改文件
lib/api/http.dart 250 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/main.dart 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/map/location_model.dart 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/map/location_user_model.dart 74 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/sos/sos_model.dart 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/sos/sos_record_model.dart 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/user/user_info.dart 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/location.dart 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/main.dart 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/travel_main.dart 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/map/travel.dart 238 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/add_location_person.dart 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/sos/sos.dart 484 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/sos/sos_contacts.dart 147 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/base_ui.dart 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/dialog.dart 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/map_marker.dart 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/sos_ui.dart 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/event_bus_util.dart 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/map_util.dart 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/ui_utils.dart 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/api/http.dart
@@ -12,6 +12,7 @@
import 'package:locations/ui/widget/dialog.dart';
import 'package:locations/utils/encrypt_util.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
typedef OnHttpRequestFinish = void Function(HttpRequestResult result);
@@ -19,13 +20,10 @@
typedef OnHttpRequestStart = void Function();
_showLoading(BuildContext context) {
  //先丢失焦点
  FocusScope.of(context).unfocus();
  //开启加载框
  showGeneralDialog(
      context: context,
      pageBuilder: (BuildContext buildContext, Animation<double> animation,
          Animation<double> secondaryAnimation) {
        return LoadingDialog("");
      });
  DialogUtil.showDialog(context, LoadingDialog(""));
}
_dismissDialog(BuildContext context) {
@@ -91,7 +89,7 @@
    httpClient.connectionTimeout = const Duration(seconds: 10);
    var uri = Uri(
        scheme: "http",
        host: "192.168.3.122",
        host: "193.112.35.168",
        path: api,
        port: 8082,
        queryParameters: params);
@@ -195,7 +193,7 @@
          "latitude": location.latitude.toString(),
          "longitude": location.longitude.toString(),
          "address": location.address,
          "locationDetail": location.locationDetail,
          "addressDetail": location.locationDetail,
          "uid": uid.toString()
        },
        () {});
@@ -215,6 +213,8 @@
    var result = await HttpUtil.baseRequest(
        "/api/v1/location/getLocation", params, () {});
    print(result.data);
    if (result.success) {
      if (result.data!["code"] == 0) {
        return UserLocationInfo.fromJson(result.data!["data"]);
@@ -273,7 +273,9 @@
    }
    var result = await HttpUtil.baseRequest(
        "/api/v1/location/updateLocationUser", params, () {  _showLoading(context);});
        "/api/v1/location/updateLocationUser", params, () {
      _showLoading(context);
    });
    _dismissDialog(context);
    if (result.success) {
      return result.data;
@@ -335,18 +337,244 @@
  static Future<Map<String, dynamic>?> getTravel(
      int uid, int? targetUid, int startTime, int endTime, int page) async {
    var params = {
      "uid": uid,
      "uid": uid.toString(),
      "startTime": startTime.toString(),
      "endTime": endTime.toString(),
      "page": page.toString()
    };
    if (targetUid != null) {
      params["targetUid"] = targetUid;
      params["targetUid"] = targetUid.toString();
    }
    var result =
        await HttpUtil.baseRequest("/api/v1/location/getTravel", params, () {});
    print(result);
    if (result.success) {
      return result.data;
    }
    return null;
  }
}
class SOSApiUtil {
  ///添加紧急联系人
  static Future<Map<String, dynamic>?> addEmergencyContacts(
      BuildContext context,
      String? name,
      String? phone,
      String? remarks) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      ToastUtil.toast("请先登录");
      return null;
    }
    Map<String, dynamic> params = {};
    params["uid"] = uid.toString();
    params["phone"] = phone;
    params["name"] = name;
    if (!StringUtil.isNullOrEmpty(remarks)) {
      params["remarks"] = remarks;
    }
    var result = await HttpUtil.baseRequest(
        "/api/v1/sos/addEmergencyContacts", params, () {
      _showLoading(context);
    });
    _dismissDialog(context);
    if (result.success) {
      return result.data;
    }
    return null;
  }
  ///紧急联系人列表
  static Future<Map<String, dynamic>?> listEmergencyContacts(
      BuildContext context) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      ToastUtil.toast("请先登录");
      return null;
    }
    Map<String, dynamic> params = {};
    params["uid"] = uid.toString();
    var result = await HttpUtil.baseRequest(
        "/api/v1/sos/listEmergencyContacts", params, () {});
    if (result.success) {
      return result.data;
    }
    return null;
  }
  ///删除紧急联系人
  static Future<Map<String, dynamic>?> deleteEmergencyContacts(
      BuildContext context, String id) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      ToastUtil.toast("请先登录");
      return null;
    }
    Map<String, dynamic> params = {};
    params["uid"] = uid.toString();
    params["id"] = id;
    var result = await HttpUtil.baseRequest(
        "/api/v1/sos/deleteEmergencyContacts", params, () {
      _showLoading(context);
    });
    _dismissDialog(context);
    if (result.success) {
      return result.data;
    }
    return null;
  }
  ///修改紧急联系人
  static Future<Map<String, dynamic>?> updateEmergencyContacts(
      BuildContext context,
      String id,
      String name,
      String phone,
      String? remarks) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      ToastUtil.toast("请先登录");
      return null;
    }
    Map<String, dynamic> params = {"id": id};
    params["uid"] = uid.toString();
    params["phone"] = phone;
    params["name"] = name;
    if (!StringUtil.isNullOrEmpty(remarks)) {
      params["remarks"] = remarks;
    }
    var result = await HttpUtil.baseRequest(
        "/api/v1/sos/updateEmergencyContacts", params, () {
      _showLoading(context);
    });
    _dismissDialog(context);
    if (result.success) {
      return result.data;
    }
    return null;
  }
  ///获取求助信息
  static Future<Map<String, dynamic>?> addSOSRecord(
      BuildContext context, SimpleLocation location) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      ToastUtil.toast("请先登录");
      return null;
    }
    Map<String, dynamic> params = {};
    params["uid"] = uid.toString();
    params["latitude"] = location.latitude.toString();
    params["longitude"] = location.longitude.toString();
    params["address"] = location.address.toString();
    params["addressDetail"] = location.addressDetail.toString();
    var result =
    await HttpUtil.baseRequest("/api/v1/sos/addSOSRecord", params, () {
      _showLoading(context);
    });
    _dismissDialog(context);
    if (result.success) {
      return result.data;
    }
    return null;
  }
  ///获取求助信息
  static Future<Map<String, dynamic>?> listSOSRecord(
      BuildContext context, int page) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      ToastUtil.toast("请先登录");
      return null;
    }
    Map<String, dynamic> params = {"page": page.toString()};
    params["uid"] = uid.toString();
    var result =
        await HttpUtil.baseRequest("/api/v1/sos/getSOSRecordList", params, () {});
    if (result.success) {
      return result.data;
    }
    return null;
  }
  ///清除求助信息
  static Future<Map<String, dynamic>?> clearSOSRecord(
      BuildContext context) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      ToastUtil.toast("请先登录");
      return null;
    }
    Map<String, dynamic> params = {};
    params["uid"] = uid.toString();
    var result =
        await HttpUtil.baseRequest("/api/v1/sos/clearSOSRecord", params, () {
      _showLoading(context);
    });
    _dismissDialog(context);
    if (result.success) {
      return result.data;
    }
    return null;
  }
  ///获取想我求救的SOS
  static Future<Map<String, dynamic>?> getTargetSOSRecord(
      BuildContext context) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      ToastUtil.toast("请先登录");
      return null;
    }
    Map<String, dynamic> params = {};
    params["uid"] = uid.toString();
    var result = await HttpUtil.baseRequest(
        "/api/v1/sos/getTargetSOSRecord", params, () {});
    if (result.success) {
      return result.data;
    }
    return null;
  }
  ///查阅向我求救的SOS
  static Future<Map<String, dynamic>?> readTargetSOS(
      BuildContext context, String id) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      ToastUtil.toast("请先登录");
      return null;
    }
    Map<String, dynamic> params = {"id": id};
    params["uid"] = uid.toString();
    var result =
        await HttpUtil.baseRequest("/api/v1/sos/readTargetSOS", params, () {});
    if (result.success) {
      return result.data;
    }
lib/main.dart
@@ -7,6 +7,7 @@
import 'package:flutter/services.dart';
import 'package:locations/utils/app_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'package:permission_handler/permission_handler.dart';
@@ -113,9 +114,6 @@
    });
  }
  Future requestPermission() async {
    await Permission.phone.request();
    await Permission.storage.request();
@@ -130,39 +128,30 @@
        //弹出用户协议框
        String content =
            "欢迎您使用百度地图服务!<a href='${Constant.PROTOCOL_URL}'> 用户协议 </a>和<a href='${Constant.PRIVACY_URL}'>隐私政策</a>   我们非常重视您的隐私保护和个人信息保护。本隐私政策适用于您通过任何方式对百度地图各项服务的访问和使用。您可以利用百度地图搜索路况信息、商家信息、定位您所在的位置,进行路径规划、导航您想去的地址,搜索周边的服务等(以下统称“百度地图产品或服务”)。您具体获得的百度地图服务内容可能因为您使用的百度地图的版本及搭载设备不同而有所差异,如果在部分版本或搭载设备中不涵盖某些服务内容或未提供特定功能(例如:部分版本不支持登录,我们可能无法为您提供第三方服务以及其他登录后才能使用的功能;手表等穿戴设备暂时只支持步骑行的路线规划及导航,所以我们在穿戴设备上搭载的地图无法为您提供如驾车路线规划及导航等其他功能),本隐私政策中涉及到上述服务/功能及相关个人信息的内容将不适用。您可以通过多种不同的方式来使用我们的产品和服务,包括百度地图的网站、软件、供第三方网站和应用程序使用的百度地图软件开发工具包(SDK)和应用程序编程接口(API)、车载导航仪、智能后视镜等智能硬件设备。";
        showGeneralDialog(
            context: context,
            pageBuilder: (BuildContext buildContext,
                    Animation<double> animation,
                    Animation<double> secondaryAnimation) =>
                NotifyDialog(
                  "用户协议&隐私政策",
                  content,
                  () {
                    Navigator.of(context).pop();
                  },
                  () {
                    UserUtil.setAgreeProtocol().then((value) {
                      if (value) {
                        //弹出权限框
                        showGeneralDialog(
                            context: context,
                            pageBuilder: (BuildContext buildContext,
                                Animation<double> animation,
                                Animation<double> secondaryAnimation) {
                              return PermissionNotifyDialog(() {
                                Navigator.of(context).pop();
                                requestPermission().then((value) {
                                  init();
                                });
                              });
                            });
                      } else {}
                    });
                  },
                  richText: true,
                  height: 400,
                ));
        DialogUtil.showDialog(
            context,
            NotifyDialog(
              "用户协议&隐私政策",
              content,
              () {
                Navigator.of(context).pop();
              },
              () {
                UserUtil.setAgreeProtocol().then((value) {
                  if (value) {
                    //弹出权限框
                    DialogUtil.showDialog(context, PermissionNotifyDialog(() {
                      Navigator.of(context).pop();
                      requestPermission().then((value) {
                        init();
                      });
                    }));
                  } else {}
                });
              },
              richText: true,
              height: 400,
            ));
      } else {
        //已经同意了
        init();
lib/model/map/location_model.dart
@@ -55,6 +55,7 @@
    _locationDetail = json['locationDetail'];
  }
  String? _locTime;
  double? _latitude;
  double? _longitude;
@@ -115,21 +116,33 @@
    double? latitude,
    double? longitude,
    String? address,
    String? addressDetail
  }) {
    _latitude = latitude;
    _longitude = longitude;
    _address = address;
    _addressDetail=addressDetail;
  }
  SimpleLocation.fromJson(dynamic json) {
    _latitude = json['latitude'];
    _longitude = json['longitude'];
    _address = json['address'];
    _addressDetail = json['addressDetail'];
  }
  SimpleLocation.fromBaiDuLocation(BaiduLocation location) {
    _latitude = location.latitude;
    _longitude = location.longitude;
    _address = location.address;
    _addressDetail=location.locationDetail;
  }
  double? _latitude;
  double? _longitude;
  String? _address;
  String? _addressDetail;
  double? get latitude => _latitude;
@@ -137,35 +150,41 @@
  String? get address => _address;
  String? get addressDetail => _addressDetail;
  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['latitude'] = _latitude;
    map['longitude'] = _longitude;
    map['address'] = _address;
    map['addressDetail'] = _addressDetail;
    return map;
  }
}
class UserLocationInfo {
  UserLocationInfo({
    int? uid,
    SimpleLocation? location,
    String? updateTime,
  }) {
  UserLocationInfo(
      {int? uid,
      SimpleLocation? location,
      String? updateTime,
      int? locationCount}) {
    _uid = uid;
    _location = location;
    _updateTime = updateTime;
    _locationCount=locationCount;
  }
  UserLocationInfo.fromJson(dynamic json) {
    _uid = json['uid'];
    _location = SimpleLocation.fromJson(json['location']);
    _updateTime = json['updateTime'];
    _locationCount = json['locationCount'];
  }
  int? _uid;
  SimpleLocation? _location;
  String? _updateTime;
  int? _locationCount;
  int? get uid => _uid;
@@ -173,11 +192,14 @@
  String? get updateTime => _updateTime;
  int? get locationCount => _locationCount;
  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['uid'] = _uid;
    map['location'] = _location;
    map['updateTime'] = _updateTime;
    map['locationCount'] = _locationCount;
    return map;
  }
}
lib/model/map/location_user_model.dart
@@ -1,77 +1,87 @@
import 'package:locations/model/user/user_info.dart';
class LocationUserModel {
  LocationUserModel({
      String? id,
      int? targetUid,
      String? targetName,
  LocationUserModel(
      {String? id,
      int? targetUid,
      String? targetName,
      String? targetPhone,
    LocationInviteStatus? status,
    LocationUserType? userType,}){
      LocationInviteStatus? status,
      LocationUserType? userType,
      UserInfo? userInfo}) {
    _id = id;
    _targetUid = targetUid;
    _targetName = targetName;
    _targetPhone = targetPhone;
    _status = status;
    _userType = userType;
}
    _userInfo = userInfo;
  }
  LocationUserModel.fromJson(dynamic json) {
    _id = json['id'];
    _targetUid = json['targetUid'];
    _targetName = json['targetName'];
    _targetPhone = json['targetPhone'];
    switch(json['status']){
    switch (json['status']) {
      case "sentInvite":
        _status=LocationInviteStatus.sentInvite;
        _status = LocationInviteStatus.sentInvite;
        break;
      case "agree":
        _status=LocationInviteStatus.agree;
        _status = LocationInviteStatus.agree;
        break;
      case "reject":
        _status=LocationInviteStatus.reject;
        _status = LocationInviteStatus.reject;
        break;
    }
    switch(json['userType']){
    switch (json['userType']) {
      case "son":
        _userType=LocationUserType.son;
        _userType = LocationUserType.son;
        break;
      case "daughter":
        _userType=LocationUserType.daughter;
        _userType = LocationUserType.daughter;
        break;
      case "husband":
        _userType=LocationUserType.husband;
        _userType = LocationUserType.husband;
        break;
      case "wife":
        _userType=LocationUserType.wife;
        _userType = LocationUserType.wife;
        break;
      case "father":
        _userType=LocationUserType.father;
        _userType = LocationUserType.father;
        break;
      case "mother":
        _userType=LocationUserType.mother;
        _userType = LocationUserType.mother;
        break;
      case "other":
        _userType=LocationUserType.other;
        _userType = LocationUserType.other;
        break;
    }
    _userInfo = UserInfo.fromJson(json['userInfo']);
  }
  String? _id;
  int? _targetUid;
  String? _targetName;
  String? _targetPhone;
  LocationInviteStatus? _status;
  LocationUserType? _userType;
  UserInfo? _userInfo;
  String? get id => _id;
  int? get targetUid => _targetUid;
  String? get targetName => _targetName;
  String? get targetPhone => _targetPhone;
  UserInfo? get userInfo => _userInfo;
  LocationInviteStatus? get status => _status;
  LocationUserType? get userType => _userType;
  Map<String?, dynamic> toJson() {
@@ -82,25 +92,11 @@
    map['targetPhone'] = _targetPhone;
    map['status'] = _status;
    map['userType'] = _userType;
    map['userInfo'] = _userInfo;
    return map;
  }
}
enum LocationUserType { father, mother, husband, wife, daughter, son, other }
enum LocationUserType{
  father,
  mother,
  husband,
  wife,
  daughter,
  son,
  other
}
enum LocationInviteStatus{
  sentInvite,
  agree,
  reject
}
enum LocationInviteStatus { sentInvite, agree, reject }
lib/model/sos/sos_model.dart
New file
@@ -0,0 +1,39 @@
import 'package:locations/model/user/user_info.dart';
class SOSContact {
  SOSContact({String? id, String? phone, String? remarks, UserInfo? user}) {
    _id = id;
    _phone = phone;
    _remarks = remarks;
    _user = user;
  }
  SOSContact.fromJson(dynamic json) {
    _id = json['id'];
    _phone = json['phone'];
    _remarks = json['remarks'];
    _user = UserInfo.fromJson(json['user']);
  }
  String? _id;
  String? _phone;
  String? _remarks;
  UserInfo? _user;
  String? get id => _id;
  String? get phone => _phone;
  String? get remarks => _remarks;
  UserInfo? get user => _user;
  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['id'] = _id;
    map['phone'] = _phone;
    map['user'] = _user;
    map['remarks'] = _remarks;
    return map;
  }
}
lib/model/sos/sos_record_model.dart
New file
@@ -0,0 +1,67 @@
import 'package:locations/model/map/location_model.dart';
class SosRecordModel {
  SosRecordModel({
    String? portrait,
    String? from,
    String? desc,
    String? phone,
    SimpleLocation? location,
    String? createTime,
    String? targetDesc,
  }) {
    _portrait = portrait;
    _from = from;
    _desc = desc;
    _phone = phone;
    _location = location;
    _createTime = createTime;
    _targetDesc = targetDesc;
  }
  SosRecordModel.fromJson(dynamic json) {
    _portrait = json['portrait'];
    _from = json['from'];
    _desc = json['desc'];
    _phone = json['phone'];
    _location =SimpleLocation.fromJson(json['location']);
    _createTime = json['createTime'];
    _targetDesc = json['targetDesc'];
  }
  String? _portrait;
  String? _from;
  String? _desc;
  String? _phone;
  SimpleLocation? _location;
  String? _createTime;
  String? _targetDesc;
  String? get portrait => _portrait;
  String? get from => _from;
  String? get desc => _desc;
  String? get phone => _phone;
  SimpleLocation? get location => _location;
  String? get createTime => _createTime;
  String? get targetDesc => _targetDesc;
  Map<String?, dynamic> toJson() {
    final map = <String?, dynamic>{};
    map['portrait'] = _portrait;
    map['from'] = _from;
    map['desc'] = _desc;
    map['phone'] = _phone;
    if (_location != null) {
      map['location'] = _location!.toJson();
    }
    map['createTime'] = _createTime;
    map['targetDesc'] = _targetDesc;
    return map;
  }
}
lib/model/user/user_info.dart
@@ -1,6 +1,6 @@
///用户信息
class UserInfo {
  UserInfo({int? id, String? nickName, String? portrait, int? vipExpireTime}) {
  UserInfo({int? id, String? nickName, String? portrait,  int? vipExpireTime}) {
    _id = id;
    _nickName = nickName;
    _portrait = portrait;
lib/ui/main/location.dart
@@ -12,8 +12,10 @@
import 'package:locations/model/map/location_user_model.dart';
import 'package:locations/model/user/user_info.dart';
import 'package:locations/ui/map/location_search.dart';
import 'package:locations/ui/map/travel.dart';
import 'package:locations/ui/mine/add_location_person.dart';
import 'package:locations/ui/mine/login.dart';
import 'package:locations/ui/sos/sos.dart';
import 'package:locations/ui/widget/button.dart';
import 'package:locations/ui/widget/capture.dart';
import 'package:locations/ui/widget/dialog.dart';
@@ -59,14 +61,12 @@
  //用户marker
  BMFMarker? userMarker;
  String? portrait;
  BaiduLocation? location;
  SimpleLocation? location;
  BMFMapOptions mapOptions = BMFMapOptions(
      showMapScaleBar: false,
      mapType: BMFMapType.Standard,
      center: Global.currentPosition != null
          ? Global.currentPosition
          : BMFCoordinate(39.917215, 116.380341),
      center: Global.currentPosition ?? BMFCoordinate(39.917215, 116.380341),
      mapScaleBarPosition: BMFPoint(0, 200),
      zoomLevel: 12,
      mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 200));
@@ -97,36 +97,30 @@
      return;
    }
    Map<String, dynamic>? result =
    await LocationApiUtil.getInviteLocation(uid!);
        await LocationApiUtil.getInviteLocation(uid!);
    if (result!["code"] == 0) {
      LocationUserModel model = LocationUserModel.fromJson(result["data"]);
      showGeneralDialog(
          context: context,
          pageBuilder: (BuildContext buildContext, Animation<double> animation,
              Animation<double> secondaryAnimation) {
            return RequireLocationDialog(model.targetPhone!, () {
              LocationApiUtil.rejectInviteLocation(uid, model.id!)
                  .then((value) {
                if (value!["code"] == 0) {
                  Navigator.of(context).pop();
                } else {
                  ToastUtil.toast(value!["msg"]);
                }
              });
            }, () {
              LocationApiUtil.agreeInviteLocation(uid, model.id!).then((value) {
                if (value!["code"] == 0) {
                  Navigator.of(context).pop();
                } else {
                  ToastUtil.toast(value!["msg"]);
                }
              });
      DialogUtil.showDialog(
          context,
          RequireLocationDialog(model.targetPhone!, () {
            LocationApiUtil.rejectInviteLocation(uid, model.id!).then((value) {
              if (value!["code"] == 0) {
                Navigator.of(context).pop();
              } else {
                ToastUtil.toast(value!["msg"]);
              }
            });
          });
          }, () {
            LocationApiUtil.agreeInviteLocation(uid, model.id!).then((value) {
              if (value!["code"] == 0) {
                Navigator.of(context).pop();
              } else {
                ToastUtil.toast(value!["msg"]);
              }
            });
          }));
    }
  }
  @override
  void dispose() {
@@ -174,7 +168,7 @@
  //控件阴影
  List<BoxShadow> getViewShadow() {
    return [
      BoxShadow(
      const BoxShadow(
        blurRadius: 6.5,
        spreadRadius: 1,
        color: Color(0x4D0E96FF),
@@ -238,7 +232,7 @@
          padding: const EdgeInsets.fromLTRB(0, 18, 0, 0),
          decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.only(
              borderRadius: const BorderRadius.only(
                  topLeft: Radius.circular(10), topRight: Radius.circular(10)),
              boxShadow: getViewShadow()),
          child: Flex(
@@ -270,7 +264,7 @@
//控件阴影
List<BoxShadow> getViewShadow() {
  return [
    BoxShadow(
    const BoxShadow(
      blurRadius: 6.5,
      spreadRadius: 1,
      color: Color(0x4D0E96FF),
@@ -294,7 +288,7 @@
  UserInfo? user;
  BaiduLocation? _location;
  UserLocationInfo? _userLocationInfo;
  GlobalKey rootWidgetKey = GlobalKey();
@@ -314,9 +308,14 @@
        if (state == LocationState.success) {
          BaiduLocation location = BaiduLocation.fromJson(map);
          setState(() {
            _location = location;
            _userLocationInfo = UserLocationInfo(
                uid: user!.id,
                location: SimpleLocation.fromBaiDuLocation(location),
                updateTime: location.locTime,
                locationCount: 1);
          });
          eventBus.fire(UserLocationInfoEventBus(_location, user));
          eventBus.fire(
              UserLocationInfoEventBus(_userLocationInfo!.location, user));
        }
      });
    });
@@ -343,8 +342,6 @@
    eventBusLogin.cancel();
  }
  void _selectLocationUser() async {
    await _getLocationUsers();
@@ -353,21 +350,35 @@
      return;
    }
    var selectUserType = await showGeneralDialog(
        context: context,
        pageBuilder: (BuildContext buildContext, Animation<double> animation,
                Animation<double> secondaryAnimation) =>
            ListViewDialog(
              ListView.builder(
                padding: EdgeInsets.zero,
                  itemCount: userList!.length,
                  itemBuilder: (BuildContext context, int index) {
                    return _getUserItem(userList![index], index);
                  }),
              () {
                Navigator.of(context).pop();
              },
            ));
    var selectUser = await DialogUtil.showDialog(
        context,
        ListViewDialog(
          ListView.builder(
              padding: EdgeInsets.zero,
              itemCount: userList!.length,
              itemBuilder: (BuildContext context, int index) {
                return _getUserItem(userList![index], index);
              }),
          () {
            Navigator.of(context).pop();
          },
        ));
    if (selectUser != null) {
      setState(() {
        user = (selectUser as LocationUserModel).userInfo;
      });
      //开始获取定位
      UserLocationInfo? _userLocation =
          await LocationApiUtil.getLocation(user!.id!.toString());
      if (_userLocation != null) {
        print("更新时间:${_userLocation.updateTime}");
        setState(() {
          _userLocationInfo = _userLocation;
        });
        eventBus.fire(UserLocationInfoEventBus(_userLocation.location, user));
      }
    }
  }
  Future _getLocationUsers() async {
@@ -375,6 +386,8 @@
    if (uid == null) return;
    Map<String, dynamic>? result =
        await LocationApiUtil.getLocationUsers(uid, 1);
    print(result);
    if (result!["code"] == 0) {
      List<dynamic> list = result!["data"]["list"];
@@ -405,7 +418,7 @@
                      },
                      child: Container(
                        alignment: Alignment.center,
                        padding: EdgeInsets.fromLTRB(13, 0, 13, 0),
                        padding: const EdgeInsets.fromLTRB(13, 0, 13, 0),
                        height: 45,
                        decoration: BoxDecoration(
                            color: Colors.white,
@@ -431,7 +444,8 @@
                      ))),
              InkWell(
                  onTap: () {
                    print("sos");
                    NavigatorUtil.navigateToNextPage(
                        context, SOSPage(title: ""), (data) {});
                  },
                  child: Container(
                    width: 45,
@@ -455,22 +469,22 @@
              child: Stack(
                children: [
                  //位置信息
                  _location == null
                  _userLocationInfo == null
                      ? Container()
                      : getPositionInfoView(context),
                  //个人信息
                  Container(
                    height: 100,
                    margin: EdgeInsets.only(top: 10),
                    padding: EdgeInsets.only(left: 15, top: 8, right: 15),
                    margin: const EdgeInsets.only(top: 10),
                    padding: const EdgeInsets.only(left: 15, top: 8, right: 15),
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.only(
                            bottomLeft:
                                Radius.circular(locationInfoHidden ? 10 : 0),
                            bottomRight:
                                Radius.circular(locationInfoHidden ? 10 : 0),
                            topLeft: Radius.circular(10),
                            topRight: Radius.circular(10)),
                            topLeft: const Radius.circular(10),
                            topRight: const Radius.circular(10)),
                        color: Colors.white,
                        boxShadow: getViewShadow()),
                    child: Stack(
@@ -490,7 +504,7 @@
                                      "assets/images/main/icon_location_change_person.png",
                                      width: 13,
                                    ),
                                    Text(
                                    const Text(
                                      " 切换实时定位人",
                                      style: TextStyle(
                                          color: ColorConstant.theme,
@@ -504,7 +518,7 @@
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: [
                            user == null
                                ? Image(
                                ? const Image(
                                    image: AssetImage(
                                        "assets/images/mine/icon_mine_default_portrait.png"),
                                    height: 33,
@@ -549,7 +563,7 @@
                                        user!.nickName!,
                                        softWrap: false,
                                        overflow: TextOverflow.ellipsis,
                                        style: TextStyle(
                                        style: const TextStyle(
                                            color: Color(0xFFA0A0A0),
                                            fontSize: 18),
                                      ),
@@ -558,7 +572,7 @@
                                      ),
                                      Text(
                                        "ID:${user!.id!}",
                                        style: TextStyle(
                                        style: const TextStyle(
                                            color: Color(0xFFA0A0A0),
                                            fontSize: 12),
                                      )
@@ -580,7 +594,12 @@
                                          width: 82,
                                          fontSize: 12,
                                          onClick: () {
                                            print("生成轨迹");
                                            NavigatorUtil.navigateToNextPage(
                                                context,
                                                MyTravelPage(
                                                  uid: user!.id,
                                                ),
                                                (data) {});
                                          },
                                        ),
                                        Container(
@@ -604,7 +623,7 @@
                  ),
                  //更多
                  _location == null
                  _userLocationInfo == null
                      ? Container()
                      : Positioned(
                          left: MediaQuery.of(context).size.width / 2 -
@@ -617,7 +636,7 @@
                                  locationInfoHidden = !locationInfoHidden;
                                });
                              },
                              child: Container(
                              child: SizedBox(
                                  width: 70,
                                  height: 40,
                                  child: Stack(
@@ -826,7 +845,7 @@
                              color: const Color(0xFFCACDD3),
                              borderRadius: BorderRadius.circular(20)),
                          child: Text(
                            _location!.locTime!,
                            _userLocationInfo!.updateTime!,
                            style: const TextStyle(
                                color: Colors.white, fontSize: 10),
                          ),
@@ -848,10 +867,10 @@
                            ),
                            Expanded(
                                child: Text(
                              _location!.address!,
                              _userLocationInfo!.location!.address!,
                              softWrap: false,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                              style: const TextStyle(
                                  color: Color(0xFFBABABA), fontSize: 12),
                            ))
                          ],
@@ -874,7 +893,7 @@
                            ),
                            Expanded(
                                child: Text(
                              "N ${_location!.latitude!},W ${_location!.longitude!}",
                              "N ${_userLocationInfo!.location!.latitude},W ${_userLocationInfo!.location!.longitude}",
                              softWrap: false,
                              overflow: TextOverflow.ellipsis,
                              style: const TextStyle(
@@ -891,8 +910,8 @@
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: [
                            Text(
                              "查看全部共100次定位 ",
                              style: TextStyle(
                              "查看全部共${_userLocationInfo!.locationCount}次定位 ",
                              style: const TextStyle(
                                  color: Color(0xFF9DAAB3), fontSize: 10),
                            ),
                            Image.asset(
lib/ui/main/main.dart
@@ -5,12 +5,16 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/map/location_user_model.dart';
import 'package:locations/ui/main/travel_main.dart';
import 'package:locations/ui/mine/advice.dart';
import 'package:locations/ui/mine/permission.dart';
import 'package:locations/ui/widget/dialog.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'location.dart';
import 'mine.dart';
@@ -85,9 +89,6 @@
    _tabController =
        TabController(length: _pages.length, initialIndex: 1, vsync: this);
    super.initState();
    Timer(Duration(seconds: 4), () {
    });
  }
  //设置选中的导航栏
lib/ui/main/travel_main.dart
@@ -19,6 +19,7 @@
import 'package:locations/utils/map_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/user_util.dart';
import 'package:path_provider/path_provider.dart';
//控件阴影
@@ -113,13 +114,14 @@
    if (subscription != null) {
      subscription!.cancel();
    }
    subscription = await LocationUtil.startLocation( scanspan, (state, map) {
    subscription = await LocationUtil.startLocation(scanspan, (state, map) {
      if (state == LocationState.success) {
        double lat = map!["latitude"] as double;
        double lng = map!["longitude"] as double;
        currentPosition = BMFCoordinate(lat, lng);
        _mapController!.setCenterCoordinate(BMFCoordinate(lat, lng), true);
        setCurrentPosition(BMFCoordinate(lat, lng));
        _travelRECManager!.addLocation(BMFCoordinate(lat, lng));
      }
    });
  }
@@ -137,7 +139,10 @@
    if (userMarker == null) {
      _captureController.capturePng(tempPath + "/portrait.png").then((value) {
        MapUtil.addMarker(_mapController, position, value!.path,
            offset: BMFPoint(0, 40), zIndex: 10);
                offset: BMFPoint(0, 40), zIndex: 10)
            .then((value) {
          userMarker = value;
        });
      });
    } else {
      await userMarker!.updatePosition(position);
@@ -204,12 +209,17 @@
              bottom: 0,
              child: InkWell(
                  onTap: () {
                    NavigatorUtil.navigateToNextPage(
                        context,
                        MyTravelPage(
                          title: '',
                        ),
                        (data) {});
                    UserUtil.getUid().then((value) {
                      if (value == null) {
                        return;
                      }
                      NavigatorUtil.navigateToNextPage(
                          context,
                          MyTravelPage(
                            uid: value,
                          ),
                          (data) {});
                    });
                  },
                  child: Container(
                      padding: const EdgeInsets.fromLTRB(23, 15, 23, 30),
lib/ui/map/travel.dart
@@ -10,33 +10,105 @@
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/map/location_model.dart';
import 'package:locations/ui/widget/base_ui.dart';
import 'package:locations/ui/widget/button.dart';
import 'package:locations/ui/widget/map_marker.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/utils/global.dart';
import 'package:locations/utils/map_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'package:path_provider/path_provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:share_plus/share_plus.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
class MyTravelPage extends StatefulWidget {
  MyTravelPage({Key? key, required this.title}) : super(key: key);
  final String title;
  final int? uid;
  final DateTime? startTime;
  final DateTime? endTime;
  MyTravelPage({Key? key, this.uid, this.startTime, this.endTime})
      : super(key: key);
  @override
  _MyTravelPageState createState() => _MyTravelPageState();
  _MyTravelPageState createState() =>
      _MyTravelPageState(uid, startTime, endTime);
}
class _MyTravelPageState extends State<MyTravelPage>
    with SingleTickerProviderStateMixin {
  _MyTravelPageState(this.uid, this.startDate, this.endDate);
  final int? uid;
  DateTime? startDate;
  DateTime? endDate;
  int _page = 1;
  int _totalCount = 0;
  List<UserLocationInfo> _locationList = [];
  RefreshController _refreshController =
      RefreshController(initialRefresh: false);
  @override
  void initState() {
    super.initState();
    startDate = DateTime(2021, 10, 31, 13, 5, 50);
    endDate = DateTime(2021, 11, 2, 13, 5, 50);
    endDate ??= DateTime.now();
    startDate ??= DateTime(2000, 1, 1);
    _requestTravel();
  }
  //获取轨迹
  Future _requestTravel() async {
    var _uid = await UserUtil.getUid();
    if (_uid == null) {
      return;
    }
    if (_page > 1 && _totalCount <= _locationList.length) {
      return;
    }
    Map<String, dynamic>? result = await LocationApiUtil.getTravel(
        _uid!,
        uid,
        startDate!.millisecondsSinceEpoch,
        endDate!.millisecondsSinceEpoch,
        _page);
    if (result!["code"] == 0) {
      _totalCount = result["data"]["count"] as int;
      List<dynamic> list = result!["data"]["list"];
      List<UserLocationInfo> ls = [];
      list.forEach((element) {
        ls.add(UserLocationInfo.fromJson(element));
      });
      if (_page == 1) {
        _locationList.clear();
      }
      //还有更多
      if (_totalCount > _locationList.length) {
        _page += 1;
      }
      setState(() {
        _locationList.addAll(ls);
      });
    } else {
      ToastUtil.toast(result!["msg"]);
    }
  }
  void _onRefresh() async {
    _page = 1;
    await _requestTravel();
    _refreshController.refreshCompleted();
  }
  void _onLoading() async {
    await _requestTravel();
    _refreshController.loadComplete();
  }
  @override
@@ -54,7 +126,7 @@
              TopNavBar(title: "我的轨迹"),
              //日期选择
              Container(
                padding: EdgeInsets.fromLTRB(10, 8, 10, 8),
                padding: const EdgeInsets.fromLTRB(10, 8, 10, 8),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
@@ -156,16 +228,28 @@
                  child: Stack(
                    children: [
                      Container(
                        padding: EdgeInsets.only(bottom: 60),
                        decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: BorderRadius.only(
                            borderRadius: const BorderRadius.only(
                                topLeft: Radius.circular(10),
                                topRight: Radius.circular(10)),
                            boxShadow: getViewShadow()),
                        child: ListView(
                          padding: EdgeInsets.fromLTRB(0, 0, 0, 55),
                          children: [getListViewItem(0), getListViewItem(1)],
                        ),
                        child: SmartRefresher(
                            enablePullDown: true,
                            enablePullUp: true,
                            header: refreshHeader,
                            footer: loadFotter,
                            onRefresh: _onRefresh,
                            onLoading: _onLoading,
                            controller: _refreshController,
                            child: ListView.builder(
                              itemCount: _locationList.length,
                              itemBuilder: (BuildContext context, int index) {
                                return getListViewItem(index);
                              },
                              padding: const EdgeInsets.fromLTRB(0, 0, 0, 55),
                            )),
                      ),
                      Positioned(
                          bottom: 0,
@@ -173,15 +257,29 @@
                          right: 0,
                          child: InkWell(
                              onTap: () {
                                NavigatorUtil.navigateToNextPage(context,
                                   MyTravelMapPage(title: ""), (data) {});
                                if (_locationList.length == 0) {
                                  ToastUtil.toast("暂无轨迹信息");
                                  return;
                                }
                                if (_totalCount > _locationList.length) {
                                  ToastUtil.toast("请先下拉获取完整的轨迹信息");
                                  return;
                                }
                                NavigatorUtil.navigateToNextPage(
                                    context,
                                    MyTravelMapPage(
                                      locationList: _locationList,
                                    ),
                                    (data) {});
                              },
                              child: Container(
                                alignment: Alignment.center,
                                height: 55,
                                decoration: BoxDecoration(
                                    color: Colors.white,
                                    borderRadius: BorderRadius.only(
                                    borderRadius: const BorderRadius.only(
                                        topLeft: Radius.circular(10),
                                        topRight: Radius.circular(10)),
                                    boxShadow: getViewShadow()),
@@ -196,7 +294,7 @@
                                    Container(
                                      width: 8,
                                    ),
                                    Text(
                                    const Text(
                                      "生成轨迹",
                                      style: TextStyle(
                                          color: ColorConstant.theme,
@@ -215,7 +313,7 @@
  }
  TextStyle getDateStyle() {
    return TextStyle(color: Color(0xFF333333), fontSize: 17);
    return const TextStyle(color: Color(0xFF333333), fontSize: 17);
  }
  showDatePicker(currentDate, DateChangedCallback confim) {
@@ -239,6 +337,8 @@
  }
  Widget getListViewItem(int index) {
    var location = _locationList[index];
    return Container(
      padding: const EdgeInsets.fromLTRB(18, 18, 18, 9),
      child: Column(
@@ -249,9 +349,9 @@
            decoration: BoxDecoration(
                color: const Color(0xFFCACDD3),
                borderRadius: BorderRadius.circular(20)),
            child: const Text(
              "2021.08.08 12:00",
              style: TextStyle(color: Colors.white, fontSize: 10),
            child: Text(
              "${location.updateTime}",
              style: const TextStyle(color: Colors.white, fontSize: 10),
            ),
          ),
          //地址
@@ -265,16 +365,16 @@
                "assets/images/main/icon_location_position_name.png",
                height: 16,
              ),
              Text(
              const Text(
                " 当前位置:",
                style: TextStyle(color: Color(0xFF999999), fontSize: 12),
              ),
              Expanded(
                  child: Text(
                "重庆市重庆市重庆市重庆市重庆市重庆市重庆市重庆庆市重庆市重庆",
                "${location.location!.address}",
                softWrap: false,
                overflow: TextOverflow.ellipsis,
                style: TextStyle(color: Color(0xFFBABABA), fontSize: 12),
                style: const TextStyle(color: Color(0xFFBABABA), fontSize: 12),
              ))
            ],
          ),
@@ -289,16 +389,16 @@
                "assets/images/main/icon_location_position_location.png",
                height: 15,
              ),
              Text(
              const Text(
                " 经  纬  度:",
                style: TextStyle(color: Color(0xFF999999), fontSize: 12),
              ),
              Expanded(
                  child: Text(
                "N 41141.1233,W 28741.389",
                "N ${location.location!.latitude},W ${location.location!.longitude}",
                softWrap: false,
                overflow: TextOverflow.ellipsis,
                style: TextStyle(color: Color(0xFFBABABA), fontSize: 12),
                style: const TextStyle(color: Color(0xFFBABABA), fontSize: 12),
              ))
            ],
          )),
@@ -320,21 +420,23 @@
}
class MyTravelMapPage extends StatefulWidget {
  MyTravelMapPage({Key? key, required this.title}) : super(key: key);
  final String title;
  MyTravelMapPage({Key? key, required this.locationList}) : super(key: key);
  final List<UserLocationInfo> locationList;
  @override
  _MyTravelMapPageState createState() => _MyTravelMapPageState();
  _MyTravelMapPageState createState() => _MyTravelMapPageState(locationList);
}
class _MyTravelMapPageState extends State<MyTravelMapPage>
    with SingleTickerProviderStateMixin {
  _MyTravelMapPageState(this.locationList);
  final List<UserLocationInfo> locationList;
  BMFMapOptions mapOptions = BMFMapOptions(
      showMapScaleBar: false,
      mapType: BMFMapType.Standard,
      center: Global.currentPosition != null
          ? Global.currentPosition
          : BMFCoordinate(39.917215, 116.380341),
      center: Global.currentPosition ?? BMFCoordinate(39.917215, 116.380341),
      mapScaleBarPosition: BMFPoint(0, 200),
      zoomLevel: 12,
      zoomEnabled: false,
@@ -355,11 +457,12 @@
  double? mapWidth;
  double? mapHeight;
  bool cancap = false;
  @override
  void initState() {
    super.initState();
    controller = new AnimationController(
    controller = AnimationController(
        duration: const Duration(milliseconds: 300), vsync: this);
    final CurvedAnimation curve =
        CurvedAnimation(parent: controller!, curve: Curves.easeIn);
@@ -377,13 +480,60 @@
          // onBMFMapCreated(controller);
          _mapController = controller;
          //绘制路径
          List<BMFCoordinate> points = [];
          locationList.forEach((element) {
            points.add(BMFCoordinate(
                element.location!.latitude!, element.location!.longitude!));
          });
          MapUtil.drawLine(points, ColorConstant.theme, _mapController)
              .then((value) {
            //画起始点与结束点
            MapMarkerUtil.addTravelStartMarker(_mapController, points[0]!)
                .then((value) {});
            MapMarkerUtil.addTravelEndMarker(
                    _mapController, points[points.length - 1]!)
                .then((value) {})
                .then((value) {});
            //画文字
            if (points.length > 1) {
              MapMarkerUtil.addText(_mapController, points[0], "华悦中心A座");
            }
            MapMarkerUtil.addText(
                    _mapController, points[points.length - 1], "华悦中心B座",
                    bgColor: const Color(0xAAFF1F35))
                .then((value) {
              cancap = true;
            });
          });
          //设置中心点
          MapUtil.getMapShowParams(points).then((value) {
            _mapController!.updateMapOptions(BMFMapOptions(
              zoomLevel: value.zoomLevel,
              center: value.center,
              zoomEnabled: false,
              zoomEnabledWithTap: false,
              scrollEnabled: false,
            ));
          });
          _mapController?.setMapDidFinishedRenderCallback(
              callback: (bool success) {
            //地图渲染完成
            print("地图渲染完成");
            if (!cancap) {
              return;
            }
            //截图
            _mapController?.takeSnapshot().then((value) {
              Uint8List? pngBytes = value;
              getTemporaryDirectory().then((value) {
                String temMapPicturePath = value.path + "/map.png";
                String temMapPicturePath = value.path +
                    "/${DateTime.now().millisecondsSinceEpoch}_map.png";
                if (File(temMapPicturePath).existsSync()) {
                  File(temMapPicturePath).deleteSync();
                }
                File(temMapPicturePath).writeAsBytes(pngBytes!);
                setState(() {
                  mapPicturePath = temMapPicturePath;
@@ -391,6 +541,8 @@
                });
              });
            });
            //渲染完成
          });
        },
        mapOptions: mapOptions,
@@ -483,7 +635,7 @@
                                                  MainAxisAlignment.center,
                                              children: [
                                                Text.rich(TextSpan(children: [
                                                  TextSpan(
                                                  const TextSpan(
                                                      text: "共定位到",
                                                      style: TextStyle(
                                                          color: ColorConstant
@@ -492,14 +644,15 @@
                                                          fontWeight:
                                                              FontWeight.bold)),
                                                  TextSpan(
                                                      text: "8",
                                                      style: TextStyle(
                                                      text:
                                                          "${locationList.length}",
                                                      style: const TextStyle(
                                                          color:
                                                              Color(0xFFFF1F35),
                                                          fontSize: 24,
                                                          fontWeight:
                                                              FontWeight.bold)),
                                                  TextSpan(
                                                  const TextSpan(
                                                      text: "个位置",
                                                      style: TextStyle(
                                                          color: ColorConstant
@@ -523,7 +676,7 @@
                                                    Column(
                                                      children: [
                                                        Text(
                                                          "2021.09.30 00:00:00",
                                                          "${locationList[0].updateTime}",
                                                          style: TextStyle(
                                                              color: Color(
                                                                  0xFF9DAAB3),
@@ -533,7 +686,7 @@
                                                          height: 8,
                                                        ),
                                                        Text(
                                                          "2021.09.30 00:00:00",
                                                          "${locationList[locationList.length - 1].updateTime}",
                                                          style: TextStyle(
                                                              color: Color(
                                                                  0xFF9DAAB3),
@@ -708,15 +861,14 @@
      Uint8List pngBytes = byteData!.buffer.asUint8List();
      Directory tempDir = await getTemporaryDirectory();
      String tempPath = tempDir.path;
      shareImgPath="$tempPath/share.jpg";
      shareImgPath = "$tempPath/share.jpg";
      if (File(shareImgPath!).existsSync()) {
        File(shareImgPath!).deleteSync();
      }
      File(shareImgPath!).createSync();
      File(shareImgPath!).writeAsBytesSync(pngBytes);
      return File(shareImgPath!);
    } catch (e) {
    }
    } catch (e) {}
    return null;
  }
lib/ui/mine/add_location_person.dart
@@ -278,28 +278,24 @@
                          Expanded(
                              child: InkWell(
                            onTap: () {
                              showGeneralDialog(
                                  context: context,
                                  pageBuilder: (BuildContext buildContext,
                                      Animation<double> animation,
                                      Animation<double> secondaryAnimation) {
                                    return NotifyDialog(
                                        "温馨提示", "你确认要删除该定位对象吗?", () {}, () {
                                      UserUtil.getUid().then((value) {
                                        LocationApiUtil.deleteLocationUser(
                                                context,
                                                value!,
                                                userList[selectUserIndex].id)
                                            .then((value) {
                                          if (value!["code"] == 0) {
                                            ToastUtil.toast("删除成功");
                                          } else {
                                            ToastUtil.toast(value!["msg"]);
                                          }
                                        });
                              DialogUtil.showDialog(
                                  context,
                                  NotifyDialog("温馨提示", "你确认要删除该定位对象吗?", () {},
                                      () {
                                    UserUtil.getUid().then((value) {
                                      LocationApiUtil.deleteLocationUser(
                                              context,
                                              value!,
                                              userList[selectUserIndex].id)
                                          .then((value) {
                                        if (value!["code"] == 0) {
                                          ToastUtil.toast("删除成功");
                                        } else {
                                          ToastUtil.toast(value!["msg"]);
                                        }
                                      });
                                    });
                                  }).then((value) {
                                  })).then((value) {
                                _onRefresh();
                              });
                            },
@@ -767,7 +763,8 @@
                        UserUtil.getUid().then((value) {
                          if (locationUserModel != null) {
                            LocationApiUtil.updateLocationUser(context,
                            LocationApiUtil.updateLocationUser(
                                    context,
                                    locationUserModel!.id!,
                                    phoneController!.value.text,
                                    selectedUserType)
lib/ui/sos/sos.dart
@@ -1,12 +1,29 @@
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/map/location_model.dart';
import 'package:locations/model/sos/sos_model.dart';
import 'package:locations/model/sos/sos_record_model.dart';
import 'package:locations/ui/sos/sos_contacts.dart';
import 'package:locations/ui/widget/base_ui.dart';
import 'package:locations/ui/widget/button.dart';
import 'package:locations/ui/widget/dialog.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/ui/widget/sos_ui.dart';
import 'package:locations/utils/event_bus_util.dart';
import 'package:locations/utils/location_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:pull_to_refresh/pull_to_refresh.dart';
//紧急联系人输入框确定事件
typedef OnEmergencyContactSure = void Function(
    String? name, String? phone, String? remarks);
class SOSPage extends StatefulWidget {
  SOSPage({Key? key, required this.title}) : super(key: key);
@@ -31,10 +48,20 @@
  AnimationController? controller;
  Animation<double>? animation;
  final RefreshController _refreshController =
      RefreshController(initialRefresh: false);
  bool sos = false;
  int _page = 1;
  int _totalCount = 0;
  List<SosRecordModel>? _sosRecordList = null;
  @override
  void initState() {
    super.initState();
    controller = new AnimationController(
    controller = AnimationController(
        duration: const Duration(milliseconds: 300), vsync: this);
    final CurvedAnimation curve =
        CurvedAnimation(parent: controller!, curve: Curves.easeIn);
@@ -42,12 +69,50 @@
      ..addListener(() {
        setState(() {});
      });
    _onRefresh();
  }
  @override
  void dispose() {
    controller!.dispose();
    super.dispose();
  }
  Future _requestRecord() async {
    Map<String, dynamic>? result =
        await SOSApiUtil.listSOSRecord(context, _page);
    if (result!["code"] == 0) {
      Map<String, dynamic> data = result!["data"];
      List<dynamic> list = data["list"];
      List<SosRecordModel> recordList = [];
      for (var element in list) {
        print(jsonEncode(element));
        recordList.add(SosRecordModel.fromJson(element));
      }
      if (_page == 1) {
        _sosRecordList = [];
      }
      setState(() {
        _sosRecordList!.addAll(recordList);
      });
      _totalCount = data["count"];
    } else {
      ToastUtil.toast(result!["msg"]);
    }
  }
  void _onRefresh() async {
    await _requestRecord();
    _page = 1;
    _refreshController.refreshCompleted();
  }
  void _onLoading() async {
    await _requestRecord();
    _refreshController.loadComplete();
  }
  @override
@@ -78,61 +143,105 @@
                        style: TextStyle(color: Colors.white, fontSize: 12),
                      ),
                      //--------SOS动效--------
                      Container(
                        margin: EdgeInsets.only(top: 38),
                        height: 249,
                        width: 249,
                        decoration: BoxDecoration(
                            color: Color(0xFFFF2B31),
                            border:
                                Border.all(color: Color(0xFFFF4E4A), width: 2),
                            borderRadius: BorderRadius.circular(125),
                            boxShadow: [
                              BoxShadow(
                                  color: Color(0x85D3001B),
                                  blurRadius: 10,
                                  spreadRadius: 17)
                            ]),
                        child: Stack(
                          alignment: Alignment.center,
                          children: [
                            // Positioned(
                            //     top: 20,
                            //     child: Container(
                            //       height: 100,
                            //       width: 100,
                            //       decoration: BoxDecoration(
                            //           color: Colors.transparent,
                            //           borderRadius: BorderRadius.circular(50),
                            //           boxShadow: [
                            //             BoxShadow(
                            //               color: Color(0xFFF6FF00),
                            //               blurRadius: 30,
                            //               spreadRadius: 0,
                            //             )
                            //           ]),
                            //     )),
                            Center(
                                child: Image.asset(
                                    "assets/images/common/icon_sos.png")),
                            Positioned(
                                bottom: 20,
                                child: Text(
                                  "sos",
                                  style: TextStyle(
                                      fontSize: 60,
                                      color: Colors.white,
                                      fontWeight: FontWeight.bold),
                                ))
                          ],
                        ),
                      ),
                      InkWell(
                          onTap: () {
                            if (!sos) {
                              LocationUtil.startLocation(0, (state, map) {
                                if (LocationState.success == state) {
                                  SOSApiUtil.addSOSRecord(
                                          context,
                                          SimpleLocation.fromBaiDuLocation(
                                              BaiduLocation.fromJson(map)))
                                      .then((value) {
                                    if (value!["code"] == 0) {
                                      setState(() {
                                        sos = true;
                                      });
                                    } else {
                                      ToastUtil.toast(value!["msg"]);
                                    }
                                  });
                                } else if (LocationState.permission_denied ==
                                    state) {
                                  ToastUtil.toast("请开启定位权限");
                                } else {
                                  ToastUtil.toast("定位失败");
                                }
                              });
                            }else{
                              setState(() {
                                sos = false;
                              });
                            }
                          },
                          child: Container(
                            margin: const EdgeInsets.only(top: 38),
                            height: 249,
                            width: 249,
                            decoration: BoxDecoration(
                                color: const Color(0xFFFF2B31),
                                border: Border.all(
                                    color: const Color(0xFFFF4E4A), width: 2),
                                borderRadius: BorderRadius.circular(125),
                                boxShadow: const [
                                  BoxShadow(
                                      color: Color(0x85D3001B),
                                      blurRadius: 10,
                                      spreadRadius: 17)
                                ]),
                            child: Stack(
                              alignment: Alignment.topCenter,
                              children: [
                                Container(
                                    margin: const EdgeInsets.only(top: 0),
                                    child: Image.asset(
                                      "assets/images/common/icon_sos.png",
                                      width: 250,
                                    )),
                                Positioned(
                                    bottom: 10,
                                    child: Text(
                                      !sos ? "sos" : "",
                                      style: const TextStyle(
                                          fontSize: 45,
                                          color: Colors.white,
                                          fontWeight: FontWeight.bold),
                                    )),
                                sos
                                    ? SizedBox(
                                        width: 250,
                                        height: 250,
                                        child: RadarView())
                                    : Container(),
                                sos
                                    ? Positioned(
                                        bottom: 20,
                                        child: Column(children: const [
                                          Text(
                                            "正在向你的紧急联系人发送警报...",
                                            style: TextStyle(
                                              fontSize: 12,
                                              color: Colors.white,
                                            ),
                                          ),
                                          Text(
                                            "关闭求助",
                                            style: TextStyle(
                                              fontSize: 20,
                                              color: Colors.white,
                                            ),
                                          )
                                        ]))
                                    : Container(),
                              ],
                            ),
                          )),
                      //----------求助记录-------------
                      Expanded(
                          child: Container(
                        margin: EdgeInsets.only(top: 30),
                        padding: EdgeInsets.all(15),
                        decoration: BoxDecoration(
                        margin: const EdgeInsets.only(top: 30),
                        padding: const EdgeInsets.all(15),
                        decoration: const BoxDecoration(
                            color: Colors.white,
                            borderRadius: BorderRadius.only(
                                topRight: Radius.circular(10),
@@ -147,10 +256,11 @@
                                mainAxisAlignment: MainAxisAlignment.center,
                                crossAxisAlignment: CrossAxisAlignment.center,
                                children: [
                                  Text(
                                  const Text(
                                    "求助记录",
                                    style: TextStyle(
                                        color: ColorConstant.theme, fontSize: 16),
                                        color: ColorConstant.theme,
                                        fontSize: 16),
                                  ),
                                  Container(
                                    width: 5,
@@ -164,20 +274,68 @@
                              width: 12.5,
                            ),
                            InkWell(
                              child: Text(
                              onTap: () {
                                DialogUtil.showDialog(
                                    context,
                                    NotifyDialog(
                                      "温馨提示",
                                      "确定要清空记录吗?",
                                      () {
                                      },
                                      () {
                                        SOSApiUtil.clearSOSRecord(context)
                                            .then((value) {
                                          if (value!["code"] == 0) {
                                            _onRefresh();
                                          } else {
                                            ToastUtil.toast(value!["msg"]);
                                          }
                                        });
                                      },
                                      height: 200,
                                    ));
                              },
                              child: const Text(
                                "清空记录",
                                style: TextStyle(
                                    color: Color(0xFF999999), fontSize: 12),
                              ),
                            ),
                            Expanded(
                                child: ListView(
                              padding: EdgeInsets.only(top: 10),
                              children: [
                                getListViewItem(),
                                getListViewItem(),
                              ],
                            ))
                              child: SmartRefresher(
                                  enablePullDown: true,
                                  enablePullUp: true,
                                  header: refreshHeader,
                                  footer: loadFotter,
                                  onRefresh: _onRefresh,
                                  onLoading: _onLoading,
                                  controller: _refreshController,
                                  child: ListView.builder(
                                    itemCount: _sosRecordList == null
                                        ? 0
                                        : (_sosRecordList!.isEmpty
                                            ? 1
                                            : _sosRecordList!.length),
                                    itemBuilder:
                                        (BuildContext context, int index) {
                                      if (_sosRecordList!.isEmpty) {
                                        return Container(
                                            padding:
                                                const EdgeInsets.only(top: 20),
                                            alignment: Alignment.center,
                                            child: const Text(
                                              "暂无数据",
                                              style:
                                                  TextStyle(color: Colors.grey),
                                            ));
                                      }
                                      return getListViewItem(index);
                                    },
                                    padding:
                                        const EdgeInsets.fromLTRB(0, 0, 0, 55),
                                  )),
                            )
                          ],
                        ),
                      )),
@@ -189,8 +347,8 @@
              title: "紧急求助",
              rightText: "紧急联系人",
              rightClick: () {
                NavigatorUtil.navigateToNextPage(context,
                  SOSContactPage(title: ""), (data) {});
                NavigatorUtil.navigateToNextPage(
                    context, SOSContactPage(title: ""), (data) {});
              },
              backIcon: Image.asset(
                "assets/images/common/icon_sos_back.png",
@@ -217,20 +375,41 @@
                  if (controller!.status == AnimationStatus.completed) {
                    controller!.reverse();
                  }
                }, () {
                  if (controller!.status == AnimationStatus.completed) {
                    controller!.reverse();
                }, (String? name, String? phone, String? remarks) {
                  if (StringUtil.isNullOrEmpty(name)) {
                    ToastUtil.toast("请输入联系人称呼");
                    return;
                  }
                  if (StringUtil.isNullOrEmpty(phone)) {
                    ToastUtil.toast("请输入电话号码");
                    return;
                  }
                  SOSApiUtil.addEmergencyContacts(context, name, phone, remarks)
                      .then((value) {
                    if (value!["code"] == 0) {
                      ToastUtil.toast("添加成功");
                      if (controller!.status == AnimationStatus.completed) {
                        controller!.reverse();
                      }
                    } else {
                      ToastUtil.toast(value!["msg"]);
                    }
                  });
                }))
          ],
        ));
  }
  Widget getListViewItem() {
  Widget getListViewItem(int index) {
    SosRecordModel record = _sosRecordList![index];
    return Container(
      padding: EdgeInsets.fromLTRB(18, 8, 18, 8),
      padding: const EdgeInsets.fromLTRB(18, 8, 18, 8),
      margin: const EdgeInsets.only(bottom: 13),
      decoration: BoxDecoration(
          border: Border.all(color: Color(0xFFCACACA), width: 1),
          border: Border.all(color: const Color(0xFFCACACA), width: 1),
          borderRadius: BorderRadius.circular(8)),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
@@ -238,15 +417,15 @@
          Align(
              alignment: Alignment.center,
              child: Container(
                padding: EdgeInsets.fromLTRB(0, 4, 0, 4),
                padding: const EdgeInsets.fromLTRB(0, 4, 0, 4),
                width: 110,
                alignment: Alignment.center,
                decoration: BoxDecoration(
                    color: Color(0xFFC7C7C7),
                    color: const Color(0xFFC7C7C7),
                    borderRadius: BorderRadius.circular(10)),
                child: Text(
                  "2021.09.09 12:00",
                  style: TextStyle(color: Colors.white, fontSize: 10),
                  "${record.createTime}",
                  style: const TextStyle(color: Colors.white, fontSize: 10),
                ),
              )),
          Container(
@@ -254,13 +433,13 @@
          ),
          Row(
            children: [
              Image.asset(
                "assets/images/mine/icon_mine_default_portrait.png",
              Image(
                image: NetworkImage(record.portrait!),
                height: 14,
              ),
              Text(
                "由自己发出",
                style: TextStyle(fontSize: 12, color: Color(0xFF999999)),
                record.from!,
                style: const TextStyle(fontSize: 12, color: Color(0xFF999999)),
              )
            ],
          ),
@@ -268,14 +447,14 @@
            height: 6,
          ),
          Text(
            "刘洪瑞已查阅、贺小辉未查阅",
            style: TextStyle(fontSize: 12, color: Color(0xFF8CAFC8)),
            record.targetDesc!,
            style: TextStyle(fontSize: 12, color: const Color(0xFF8CAFC8)),
          ),
          Container(
            height: 6,
          ),
          Text(
            "你在位置:\"位置中文名\",经纬度:\"经纬度\"于2021.09.09 21:00发出了一条紧急救助信息,求助信息推送成功。",
            record.desc!,
            maxLines: 2,
            overflow: TextOverflow.ellipsis,
            style: TextStyle(fontSize: 12, color: Color(0xFFBABABA)),
@@ -284,7 +463,7 @@
            height: 6,
          ),
          Container(
            color: Color(0xFFCED4D9),
            color: const Color(0xFFCED4D9),
            height: 1,
          ),
          Container(
@@ -298,14 +477,15 @@
              Container(
                width: 6,
              ),
              Expanded(child:
              FittedBox(
                  fit: BoxFit.fitWidth,
                  child: Text(
                    "位置名称位置名称位置名称位置名称位置名称称位置名称位置名称位置名称位置名",
                    record.location!.address! + record.location!.addressDetail!,
                    softWrap: false,
                    overflow: TextOverflow.ellipsis,
                    style: TextStyle(fontSize: 12, color: Color(0xFF999999)),
                  )),
                  ))),
              Container(
                width: 6,
              ),
@@ -321,41 +501,68 @@
  }
}
class EmergencyContactEdit extends StatelessWidget {
class EmergencyContactEdit extends StatefulWidget {
  GestureTapCallback? titleClick;
  GestureTapCallback? cancelClick;
  GestureTapCallback? sureClick;
  OnEmergencyContactSure? sureClick;
  SOSContact? contact;
  final String title;
  EmergencyContactEdit(String this.title, GestureTapCallback? this.titleClick,
      GestureTapCallback? this.cancelClick, GestureTapCallback? this.sureClick);
  EmergencyContactEdit(
      this.title, this.titleClick, this.cancelClick, this.sureClick,
      {this.contact});
  Widget getInputView(String hintText) {
    return TextField(
      decoration: InputDecoration(
          hintText: hintText,
          hintStyle: const TextStyle(color: Color(0xFFBABABA), fontSize: 14),
          border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(10),
              borderSide: BorderSide.none),
          focusedBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(10),
              borderSide: BorderSide.none),
          fillColor: Color(0xFFF0F0F0),
          contentPadding: EdgeInsets.fromLTRB(20, 14, 20, 14),
          filled: true),
    );
  @override
  _EmergencyContactEditState createState() => _EmergencyContactEditState(
      title, this.titleClick, this.cancelClick, this.sureClick,
      contact: contact);
}
class _EmergencyContactEditState extends State<EmergencyContactEdit> {
  GestureTapCallback? titleClick;
  GestureTapCallback? cancelClick;
  OnEmergencyContactSure? sureClick;
  SOSContact? contact;
  final String title;
  var eventBusContact;
  TextEditingController? _nameController = TextEditingController();
  TextEditingController? _phoneController = TextEditingController();
  TextEditingController? _remarksController = TextEditingController();
  _EmergencyContactEditState(
      this.title,
      GestureTapCallback? this.titleClick,
      GestureTapCallback? this.cancelClick,
      OnEmergencyContactSure? this.sureClick,
      {this.contact});
  @override
  void initState() {
    super.initState();
    eventBusContact = eventBus.on<SOSContact>().listen((event) {
      if (!title.contains("编辑")) {
        return;
      }
      //设置用户头像
      setState(() {
        contact = event;
      });
      if (contact != null) {
        _phoneController!.text = contact!.phone!;
        _nameController!.text = contact!.user!.nickName!;
        if (!StringUtil.isNullOrEmpty(contact!.remarks)) {
          _remarksController!.text = contact!.remarks!;
        }
      }
    });
  }
  //控件阴影
  List<BoxShadow> getViewShadow() {
    return [
      BoxShadow(
        blurRadius: 6.5,
        spreadRadius: 1,
        color: Color(0x4D0E96FF),
      )
    ];
  @override
  void dispose() {
    super.dispose();
    eventBusContact.cancel();
  }
  @override
@@ -373,7 +580,12 @@
        children: [
          InkWell(
              onTap: () {
                titleClick!();
                if (titleClick != null && contact == null) {
                  _phoneController!.text = "";
                  _nameController!.text = "";
                  _remarksController!.text = "";
                  titleClick!();
                }
              },
              child: Container(
                  padding: EdgeInsets.fromLTRB(0, 15, 0, 15),
@@ -388,19 +600,19 @@
                        Container(width: 12),
                        Text(
                          title,
                          style:
                              TextStyle(color: ColorConstant.theme, fontSize: 15),
                          style: TextStyle(
                              color: ColorConstant.theme, fontSize: 15),
                        )
                      ]))),
          getInputView("联系人称呼"),
          getInputView("联系人称呼", _nameController),
          Container(
            height: 14,
          ),
          getInputView("联系人手机号码"),
          getInputView("联系人手机号码", _phoneController),
          Container(
            height: 14,
          ),
          getInputView("备注(选填)"),
          getInputView("备注(选填)", _remarksController),
          Container(
            height: 16,
          ),
@@ -427,7 +639,10 @@
                    fontSize: 18,
                    height: 40,
                    onClick: () {
                      sureClick!();
                      sureClick!(
                          _nameController!.value.text,
                          _phoneController!.value.text,
                          _remarksController!.value.text);
                    },
                  )),
            ],
@@ -436,4 +651,33 @@
      ),
    );
  }
  Widget getInputView(String hintText, TextEditingController? controller) {
    return TextField(
      controller: controller,
      decoration: InputDecoration(
          hintText: hintText,
          hintStyle: const TextStyle(color: Color(0xFFBABABA), fontSize: 14),
          border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(10),
              borderSide: BorderSide.none),
          focusedBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(10),
              borderSide: BorderSide.none),
          fillColor: Color(0xFFF0F0F0),
          contentPadding: EdgeInsets.fromLTRB(20, 14, 20, 14),
          filled: true),
    );
  }
  //控件阴影
  List<BoxShadow> getViewShadow() {
    return [
      BoxShadow(
        blurRadius: 6.5,
        spreadRadius: 1,
        color: Color(0x4D0E96FF),
      )
    ];
  }
}
lib/ui/sos/sos_contacts.dart
@@ -5,12 +5,19 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/sos/sos_model.dart';
import 'package:locations/ui/sos/sos.dart';
import 'package:locations/ui/widget/base_ui.dart';
import 'package:locations/ui/widget/button.dart';
import 'package:locations/ui/widget/dialog.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/utils/event_bus_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:pull_to_refresh/pull_to_refresh.dart';
class SOSContactPage extends StatefulWidget {
  SOSContactPage({Key? key, required this.title}) : super(key: key);
@@ -37,10 +44,20 @@
  AnimationController? controller;
  Animation<double>? animation;
  List<SOSContact>? contactList;
  RefreshController _refreshController =
      RefreshController(initialRefresh: false);
  void _onRefresh() async {
    await requestContacts();
    _refreshController.refreshCompleted();
  }
  @override
  void initState() {
    super.initState();
    controller = new AnimationController(
    controller = AnimationController(
        duration: const Duration(milliseconds: 300), vsync: this);
    final CurvedAnimation curve =
        CurvedAnimation(parent: controller!, curve: Curves.easeIn);
@@ -48,6 +65,30 @@
      ..addListener(() {
        setState(() {});
      });
    setState(() {
      selectedIndex = 0;
    });
    requestContacts();
  }
  Future requestContacts() async {
    Map<String, dynamic>? result =
        await SOSApiUtil.listEmergencyContacts(context);
    if (result!["code"] == 0) {
      List<dynamic> list = result["data"]["list"];
      List<SOSContact> clist = [];
      list.forEach((element) {
        clist.add(SOSContact.fromJson(element));
      });
      if (clist.isNotEmpty) {
        eventBus.fire(clist[selectedIndex]);
      }
      setState(() {
        contactList = clist;
      });
    } else {
      ToastUtil.toast(result!["msg"]);
    }
  }
  @override
@@ -72,10 +113,30 @@
                  decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(10),
                      color: Colors.white),
                  child: ListView(
                    padding: EdgeInsets.all(0),
                    children: [getListViewItem(0), getListViewItem(1)],
                  ),
                  child: SmartRefresher(
                      enablePullDown: true,
                      header: refreshHeader,
                      onRefresh: _onRefresh,
                      controller: _refreshController,
                      child: ListView.builder(
                        padding: const EdgeInsets.all(0),
                        itemCount: contactList == null
                            ? 0
                            : (contactList!.isEmpty ? 1 : contactList!.length),
                        itemBuilder: (BuildContext context, int index) {
                          if (contactList!.isEmpty) {
                            return Container(
                                padding: EdgeInsets.only(top: 20),
                                alignment: Alignment.center,
                                child: Text(
                                  "暂无数据",
                                  style: TextStyle(color: Colors.grey),
                                ));
                          } else {
                            return getListViewItem(index);
                          }
                        },
                      )),
                )),
                //---------底部菜单----------
                Container(
@@ -83,7 +144,7 @@
                  margin: EdgeInsets.only(top: 3, left: 10, right: 10),
                  decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.only(
                      borderRadius: const BorderRadius.only(
                          topLeft: Radius.circular(10),
                          topRight: Radius.circular(10)),
                      boxShadow: getViewShadow()),
@@ -92,6 +153,9 @@
                      Expanded(
                          child: InkWell(
                        onTap: () {
                          if (contactList == null || contactList!.isEmpty) {
                            return;
                          }
                          //TODO 赋值弹出框
                          if (controller!.status == AnimationStatus.dismissed) {
                            controller!.forward();
@@ -127,16 +191,26 @@
                      Expanded(
                          child: InkWell(
                        onTap: () {
                          if (contactList == null || contactList!.isEmpty) {
                            return;
                          }
                          showGeneralDialog(
                              context: context,
                              pageBuilder: (BuildContext buildContext,
                                  Animation<double> animation,
                                  Animation<double> secondaryAnimation) {
                                return NotifyDialog("温馨提示", "你确认要删除这位紧急联系人吗?",
                                    () {
                                  // Navigator.of(context).pop();
                                }, () {
                                  // Navigator.of(context).pop();
                                return NotifyDialog(
                                    "温馨提示", "你确认要删除这位紧急联系人吗?", () {}, () {
                                  SOSApiUtil.deleteEmergencyContacts(context,
                                          contactList![selectedIndex].id!)
                                      .then((value) {
                                    if (value!["code"] == 0) {
                                      ToastUtil.toast("删除成功");
                                      _onRefresh();
                                    } else {
                                      ToastUtil.toast(value!["msg"]);
                                    }
                                  });
                                });
                              });
                        },
@@ -156,7 +230,7 @@
                              const Text(
                                "删除",
                                style: TextStyle(
                                    color:ColorConstant.theme, fontSize: 15),
                                    color: ColorConstant.theme, fontSize: 15),
                              )
                            ],
                          ),
@@ -177,19 +251,45 @@
                if (controller!.status == AnimationStatus.completed) {
                  controller!.reverse();
                }
              }, () {
                if (controller!.status == AnimationStatus.completed) {
                  controller!.reverse();
              }, (String? name, String? phone, String? remarks) {
                if (StringUtil.isNullOrEmpty(name)) {
                  ToastUtil.toast("请输入联系人称呼");
                  return;
                }
              }))
                if (StringUtil.isNullOrEmpty(phone)) {
                  ToastUtil.toast("请输入电话号码");
                  return;
                }
                SOSContact contact = contactList![selectedIndex];
                SOSApiUtil.updateEmergencyContacts(
                        context, contact.id!, name!, phone!, remarks)
                    .then((value) {
                  if (value!["code"] == 0) {
                    if (controller!.status == AnimationStatus.completed) {
                      controller!.reverse();
                    }
                    ToastUtil.toast("修改成功");
                    _onRefresh();
                  } else {
                    ToastUtil.toast(value!["msg"]);
                  }
                });
              },
                  contact: contactList != null && contactList!.isNotEmpty
                      ? contactList![selectedIndex]
                      : null))
        ]));
  }
  Widget getListViewItem(int index) {
    SOSContact contact = contactList![index];
    return InkWell(
        onTap: () {
          setState(() {
            selectedIndex = index;
            eventBus.fire(contactList![index]);
          });
        },
        child: Container(
@@ -205,10 +305,7 @@
          ),
          child: Row(
            children: [
              Image.asset(
                "assets/images/mine/icon_mine_default_portrait.png",
                height: 50,
              ),
              Image(image: NetworkImage(contact.user!.portrait!), height: 50),
              Container(
                width: 10,
              ),
@@ -217,14 +314,16 @@
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    "刘洪瑞",
                    style: TextStyle(color: ColorConstant.theme, fontSize: 18),
                    contact.user!.nickName!,
                    style: const TextStyle(
                        color: ColorConstant.theme, fontSize: 18),
                  ),
                  Container(
                    height: 5,
                  ),
                  Text("18888888888",
                      style: TextStyle(color: Color(0xFF9DAAB3), fontSize: 15))
                  Text(contact!.phone!,
                      style: const TextStyle(
                          color: Color(0xFF9DAAB3), fontSize: 15))
                ],
              )
            ],
lib/ui/widget/base_ui.dart
@@ -1,6 +1,25 @@
import 'package:flutter/cupertino.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
WaterDropHeader refreshHeader = const WaterDropHeader(complete: Text("刷新成功"));
WaterDropHeader refreshHeader=const  WaterDropHeader(complete:Text("刷新成功"));
CustomFooter loadFotter = CustomFooter(
  builder: (BuildContext context, LoadStatus? mode) {
    Widget body;
    if (mode == LoadStatus.idle) {
      body = Text("上拉加载");
    } else if (mode == LoadStatus.loading) {
      body = CupertinoActivityIndicator();
    } else if (mode == LoadStatus.failed) {
      body = Text("加载失败!点击重试!");
    } else if (mode == LoadStatus.canLoading) {
      body = Text("松手,加载更多!");
    } else {
      body = Text("没有更多数据了!");
    }
    return Container(
      height: 55.0,
      child: Center(child: body),
    );
  },
);
lib/ui/widget/dialog.dart
@@ -18,12 +18,14 @@
  final double height;
  final Color contentColor;
  bool touchOutCancel = false;
  final String cancelName;
  final String sureName;
  NotifyDialog(this.title, this.content, this.onCancel, this.onSure,
      {this.fontSize = 16.0,
      this.richText = false,
      this.height = 240,
      this.contentColor = const Color(0xFF7E7E7E)});
      this.contentColor = const Color(0xFF7E7E7E),this.cancelName="取消",this.sureName="确定"});
  Widget getContent() {
    if (richText) {
@@ -65,7 +67,7 @@
            child: Align(
                alignment: Alignment.center,
                child: Container(
                  decoration: BoxDecoration(
                  decoration: const BoxDecoration(
                      borderRadius: BorderRadius.all(Radius.circular(15)),
                      color: Colors.white),
                  alignment: Alignment.topCenter,
@@ -83,7 +85,7 @@
                            title,
                            style: TextStyle(fontSize: 18, color: Colors.white),
                          ),
                          decoration: BoxDecoration(
                          decoration: const BoxDecoration(
                            color: Color(0xFF1CC7FF),
                            borderRadius: BorderRadius.only(
                                topLeft: Radius.circular(15),
@@ -115,8 +117,8 @@
                                  border: Border.all(color: Color(0xFF0E96FF)),
                                  borderRadius: BorderRadius.circular(10)),
                              child: Text(
                                "取消",
                                style: TextStyle(
                                cancelName,
                                style: const TextStyle(
                                    color: Color(0xFF0E96FF), fontSize: 18),
                              ),
                            ),
@@ -128,15 +130,15 @@
                              onSure();
                            },
                            child: Container(
                              margin: EdgeInsets.fromLTRB(6, 0, 15, 15),
                              margin: const EdgeInsets.fromLTRB(6, 0, 15, 15),
                              alignment: Alignment.center,
                              height: 44,
                              decoration: BoxDecoration(
                                  color: Color(0xFF0E96FF),
                                  borderRadius: BorderRadius.circular(10)),
                              child: Text(
                                "同意",
                                style: TextStyle(
                                sureName,
                                style: const TextStyle(
                                    color: Colors.white, fontSize: 18),
                              ),
                            ),
lib/ui/widget/map_marker.dart
@@ -146,4 +146,25 @@
        offset: BMFPoint(0, 38));
    return [marker, marker1];
  }
  ///添加文字图层
  static Future addText(
      BMFMapController? _mapController, BMFCoordinate position,String text,
      {Color bgColor = const Color(0xAA0E95FE),
      Color fontColor = Colors.white}) async {
    BMFText bmfText = BMFText(
        text: "     "+text+"  ",
        position: position,
        bgColor: bgColor,
        fontColor: fontColor,
        fontSize: 30,
        typeFace: BMFTypeFace(
            familyName: BMFFamilyName.sMonospace,
            textStype: BMFTextStyle.BOLD_ITALIC),
        alignY: BMFVerticalAlign.ALIGN_CENTER_VERTICAL,
        alignX: BMFHorizontalAlign.ALIGN_LEFT);
    /// 添加text
    await _mapController!.addText(bmfText);
  }
}
lib/ui/widget/sos_ui.dart
New file
@@ -0,0 +1,105 @@
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
///SOS雷达扫描View
class RadarView extends StatefulWidget {
  @override
  _RadarViewState createState() => _RadarViewState();
}
class _RadarViewState extends State<RadarView>
    with SingleTickerProviderStateMixin {
  AnimationController? _controller;
  Animation<double>? _animation;
  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 2));
    _animation = Tween(begin: .0, end: pi * 2).animate(_controller!);
    _controller!.repeat();
    super.initState();
  }
  @override
  void dispose() {
    _controller!.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation!,
      builder: (context, child) {
        return CustomPaint(
          painter: RadarPainter(_animation!.value),
        );
      },
    );
  }
}
///SOS雷达扫描动画
class RadarPainter extends CustomPainter {
  final double angle;
  final Paint _bgPaint = Paint()
    ..color = Colors.white
    ..strokeWidth = 1
    ..style = PaintingStyle.stroke;
  final Paint _paint = Paint()..style = PaintingStyle.fill;
  int circleCount = 0;
  RadarPainter(this.angle);
  @override
  void paint(Canvas canvas, Size size) {
    var radius = min(size.width / 2, size.height / 2);
    // canvas.drawLine(Offset(size.width / 2, size.height / 2 - radius),
    //     Offset(size.width / 2, size.height / 2 + radius), _bgPaint);
    // canvas.drawLine(Offset(size.width / 2 - radius, size.height / 2),
    //     Offset(size.width / 2 + radius, size.height / 2), _bgPaint);
    for (var i = 1; i <= circleCount; ++i) {
      canvas.drawCircle(Offset(size.width / 2, size.height / 2),
          radius * i / circleCount, _bgPaint);
    }
    _paint.shader = ui.Gradient.sweep(
        Offset(size.width / 2, size.height / 2),
        [Colors.white.withOpacity(.01), Colors.yellow.withOpacity(.6)],
        [.0, 1.0],
        TileMode.clamp,
        .0,
        pi / 4);
    canvas.save();
    double r = sqrt(pow(size.width, 2) + pow(size.height, 2));
    double startAngle = atan(size.height / size.width);
    Point p0 = Point(r * cos(startAngle), r * sin(startAngle));
    Point px = Point(r * cos(angle + startAngle), r * sin(angle + startAngle));
    canvas.translate((p0.x - px.x) / 2, (p0.y - px.y) / 2);
    canvas.rotate(angle);
    canvas.drawArc(
        Rect.fromCircle(
            center: Offset(size.width / 2, size.height / 2), radius: radius),
        0,
        pi / 4,
        true,
        _paint);
    canvas.restore();
  }
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}
lib/utils/event_bus_util.dart
@@ -5,7 +5,7 @@
EventBus eventBus = EventBus();
class UserLocationInfoEventBus {
  BaiduLocation? location;
  SimpleLocation? location;
  UserInfo? user;
  UserLocationInfoEventBus(this.location, this.user);
}
lib/utils/map_util.dart
@@ -12,9 +12,9 @@
  ///画线
  ///https://lbs.baidu.com/index.php?title=flutter/loc/render-map/ployline
  ///https://mapopen-pub-androidsdk.bj.bcebos.com/map/flutter/docs/utils_api_1.2/flutter_baidu_mapapi_utils/flutter_baidu_mapapi_utils-library.html
  static drawLine(List<BMFCoordinate> points, Color lineColor,
  static Future drawLine(List<BMFCoordinate> points, Color lineColor,
      BMFMapController? _mapController,
      {int width = 16}) {
      {int width = 16}) async {
    List<Color> colors = [lineColor];
    List<int> indexs = List.filled(points.length - 1, 0);
@@ -28,19 +28,23 @@
        lineDashType: BMFLineDashType.LineDashTypeNone,
        lineCapType: BMFLineCapType.LineCapRound,
        lineJoinType: BMFLineJoinType.LineJoinRound);
    _mapController?.addPolyline(colorsPolyline);
    await _mapController?.addPolyline(colorsPolyline);
  }
  //获取地图的显示参数
  static Future<MapShowInfo> getMapShowParams(
      List<BMFCoordinate> points) async {
    List<double?> outSides = _getOutSidePoint(points);
    BMFCoordinate center = BMFCoordinate(
        (outSides[0]! + outSides[1]!) / 2, (outSides[2]! + outSides[3]!) / 2);
    //留1/20的边界
    int zoom =
        await _getZoom(outSides[0], outSides[1], outSides[2], outSides[3]);
    return MapShowInfo(center, zoom);
    if (points.length == 1) {
      return MapShowInfo(points[0], 18);
    } else {
      List<double?> outSides = _getOutSidePoint(points);
      BMFCoordinate center = BMFCoordinate(
          (outSides[0]! + outSides[1]!) / 2, (outSides[2]! + outSides[3]!) / 2);
      //留1/20的边界
      int zoom =
          await _getZoom(outSides[0], outSides[1], outSides[2], outSides[3]);
      return MapShowInfo(center, zoom);
    }
  }
  //
@@ -136,6 +140,11 @@
  static cleanAllMarkers(BMFMapController? _mapController) {
    _mapController!.cleanAllMarkers();
  }
  static Future<double?> getDistance(
      BMFCoordinate pointA, BMFCoordinate pointB) async {
    return await BMFCalculateUtils.getLocationDistance(pointA, pointB);
  }
}
///路径录制管理
@@ -161,8 +170,7 @@
    positionList.add(currentPosition);
    //画起始点
    MapMarkerUtil.addTravelStartMarker(
        mapController, currentPosition!)
    MapMarkerUtil.addTravelStartMarker(mapController, currentPosition!)
        .then((value) {
      startMarkers = value;
    });
lib/utils/ui_utils.dart
@@ -1,7 +1,20 @@
import 'package:flutter/cupertino.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:flutter/material.dart';
class ToastUtil {
  static toast(String text) {
    Fluttertoast.showToast(msg: text);
  }
}
class DialogUtil {
  static Future<dynamic> showDialog(BuildContext context, Dialog dialog) async {
    return await showGeneralDialog(
        context: context,
        pageBuilder: (BuildContext buildContext, Animation<double> animation,
            Animation<double> secondaryAnimation) {
          return dialog;
        });
  }
}