admin
2021-12-03 f092e392700f68cdbfc545c9801f530d19fd39fa
功能完善
1 文件已重命名
40个文件已修改
19个文件已添加
3762 ■■■■ 已修改文件
android/app/build.gradle 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/MainActivity.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/MyApplication.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/plugins/DeviceUtilPlugins.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/plugins/ShareFlutterPlugins.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/build.gradle 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/common/icon_back.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/common/icon_location_marker.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/common/icon_location_permission_input.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/common/icon_phone.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/map/icon_map_measure.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/map/icon_map_measure_back.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/map/icon_map_measure_delete.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/map/icon_map_meaure_node_1.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/map/icon_map_meaure_node_2.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/map/icon_map_meaure_node_3.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/map/icon_map_no_measure.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/map/icon_map_no_view_location.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/map/icon_map_view_location.png 补丁 | 查看 | 原始文档 | blame | 历史
lib/api/http.dart 105 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/main.dart 104 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/common/adinfo_model.dart 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/map/location_model.dart 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/sos/sos_record_model.dart 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/common/browser.dart 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/location.dart 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/main.dart 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/mine.dart 195 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/travel_main.dart 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/map/location_search.dart 254 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/map/map.dart 740 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/map/travel.dart 74 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/add_location_person.dart 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/advice.dart 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/advice_submit.dart 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/login.dart 342 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/permission.dart 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/settings.dart 103 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/sos/sos.dart 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/sos/sos_contacts.dart 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/capture.dart 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/map_marker.dart 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/nav.dart 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/ad_util.dart 388 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/app_util.dart 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/cache_util.dart 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/config_util.dart 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/event_bus_util.dart 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/global.dart 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/jsinterface.dart 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/location_util.dart 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/map_util.dart 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/pageutils.dart 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/permission_util.dart 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/push_util.dart 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/setting_util.dart 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/ui_constant.dart 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/ui_utils.dart 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pubspec.lock 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pubspec.yaml 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/build.gradle
@@ -99,4 +99,6 @@
    //QQ互联
    implementation files('lib/open_sdk_3.5.7.4_r1bc9afe_lite.jar')
    implementation 'com.taobao.android:utdid4all:1.5.2'
}
android/app/src/main/java/com/yeshi/location/MainActivity.java
@@ -2,6 +2,7 @@
import android.os.Bundle;
import com.yeshi.location.plugins.DeviceUtilPlugins;
import com.yeshi.location.plugins.FlutterAliyunPhoneNumberAuthPlugins;
import com.yeshi.location.plugins.InitAppFlutterPlugins;
import com.yeshi.location.plugins.LoginFlutterPlugins;
@@ -25,16 +26,18 @@
    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        Log.i(TAG,"configureFlutterEngine");
        Log.i(TAG, "configureFlutterEngine");
        //原生分享
        ShareFlutterPlugins.registerWith(MainActivity.this, flutterEngine.getDartExecutor().getBinaryMessenger());
        //阿里云一键登录
        FlutterAliyunPhoneNumberAuthPlugins.registerWith(MainActivity.this,flutterEngine.getDartExecutor().getBinaryMessenger());
        FlutterAliyunPhoneNumberAuthPlugins.registerWith(MainActivity.this, flutterEngine.getDartExecutor().getBinaryMessenger());
        //系统初始化
        InitAppFlutterPlugins.registerWith(flutterEngine.getDartExecutor().getBinaryMessenger());
        //登录插件
        LoginFlutterPlugins.registerWith(MainActivity.this,flutterEngine.getDartExecutor().getBinaryMessenger());
        //POI搜索
        POISearchFlutterPlugins.registerWith(MainActivity.this,flutterEngine.getDartExecutor().getBinaryMessenger());
        LoginFlutterPlugins.registerWith(MainActivity.this, flutterEngine.getDartExecutor().getBinaryMessenger());
//        //POI搜索
//        POISearchFlutterPlugins.registerWith(MainActivity.this,flutterEngine.getDartExecutor().getBinaryMessenger());
        //设备帮助插件
        DeviceUtilPlugins.registerWith(getApplicationContext(), flutterEngine.getDartExecutor().getBinaryMessenger());
    }
}
android/app/src/main/java/com/yeshi/location/MyApplication.java
@@ -11,6 +11,7 @@
import com.tencent.tauth.Tencent;
import com.umeng.analytics.MobclickAgent;
import com.umeng.commonsdk.UMConfigure;
import com.ut.device.UTDevice;
import com.yeshi.location.utils.ManifestDataUtil;
import androidx.multidex.MultiDex;
@@ -24,6 +25,7 @@
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        MultiDex.install(this);
        UTDevice.getUtdid(application);
    }
@@ -32,7 +34,6 @@
        super.onCreate();
        application = this;
        MultiDex.install(this);
    }
    public static void init(final Application application) {
android/app/src/main/java/com/yeshi/location/plugins/DeviceUtilPlugins.java
New file
@@ -0,0 +1,60 @@
package com.yeshi.location.plugins;
import android.app.Activity;
import android.content.Context;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.search.poi.OnGetPoiSearchResultListener;
import com.baidu.mapapi.search.poi.PoiDetailResult;
import com.baidu.mapapi.search.poi.PoiDetailSearchResult;
import com.baidu.mapapi.search.poi.PoiIndoorResult;
import com.baidu.mapapi.search.poi.PoiNearbySearchOption;
import com.baidu.mapapi.search.poi.PoiResult;
import com.baidu.mapapi.search.poi.PoiSearch;
import com.ut.device.UTDevice;
import java.util.HashMap;
import java.util.Map;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
public class DeviceUtilPlugins implements BasicMessageChannel.MessageHandler<Object> {
    private static final String TAG = "DeviceUtil";
    private final Context activity;
    private final BasicMessageChannel<Object> messageChannel;
    private BasicMessageChannel.Reply<Object> reply;
    private PoiSearch mPoiSearch = PoiSearch.newInstance();
    private DeviceUtilPlugins(Context activity, BinaryMessenger messager) {
        this.activity = activity;
        this.messageChannel = new BasicMessageChannel<Object>(messager, "DeviceUtil", StandardMessageCodec.INSTANCE);
        messageChannel.setMessageHandler(this);
    }
    public static DeviceUtilPlugins registerWith(Context activity, BinaryMessenger messager) {
        return new DeviceUtilPlugins(activity, messager);
    }
    @Override
    public void onMessage(@Nullable Object message, @NonNull BasicMessageChannel.Reply<Object> reply) {
        this.reply = reply;
        Map<String, String> arguments = (Map<String, String>) message;
        String method = arguments.get("method");
        switch (method) {
            //初始化
            case "getUtdid":
                reply.reply(UTDevice.getUtdid(activity));
                break;
        }
    }
}
android/app/src/main/java/com/yeshi/location/plugins/ShareFlutterPlugins.java
@@ -31,6 +31,8 @@
        channel = new MethodChannel(messager, CHANNEL);
        ShareFlutterPlugins instance = new ShareFlutterPlugins(activity);
        channel.setMethodCallHandler(instance);
    }
    @Override
android/build.gradle
@@ -2,6 +2,9 @@
    repositories {
        google()
        mavenCentral()
        maven {
            url "http://repo.baichuan-android.taobao.com/content/groups/BaichuanRepositories/"
        }
    }
    dependencies {
@@ -14,6 +17,9 @@
    repositories {
        google()
        mavenCentral()
        maven {
            url "http://repo.baichuan-android.taobao.com/content/groups/BaichuanRepositories/"
        }
    }
}
assets/images/common/icon_back.png

assets/images/common/icon_location_marker.png
assets/images/common/icon_location_permission_input.png
assets/images/common/icon_phone.png
assets/images/map/icon_map_measure.png
assets/images/map/icon_map_measure_back.png
assets/images/map/icon_map_measure_delete.png

assets/images/map/icon_map_meaure_node_1.png
assets/images/map/icon_map_meaure_node_2.png
assets/images/map/icon_map_meaure_node_3.png
assets/images/map/icon_map_no_measure.png
assets/images/map/icon_map_no_view_location.png
assets/images/map/icon_map_view_location.png
lib/api/http.dart
@@ -10,7 +10,9 @@
import 'package:locations/model/map/location_model.dart';
import 'package:locations/model/map/location_user_model.dart';
import 'package:locations/ui/widget/dialog.dart';
import 'package:locations/utils/app_util.dart';
import 'package:locations/utils/encrypt_util.dart';
import 'package:locations/utils/global.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
@@ -52,8 +54,9 @@
    return EncryptUtil.MD5(signStr);
  }
  static Future<HttpRequestResult> baseRequest(String api,
      Map<String, dynamic> params, OnHttpRequestStart? onStart) async {
  static Future<HttpRequestResult> baseRequest(
      String api, Map<String, dynamic> params, OnHttpRequestStart? onStart,
      {bool notifyError = false}) async {
    // params ??= {};
    if (Platform.isAndroid) {
@@ -68,16 +71,18 @@
      }
    }
    print("androidId:${_androidInfo!.androidId}");
    print("device:${_androidInfo!.device}");
    print("type:${_androidInfo!.type}");
    params["version"] = (await AppUtil.getVersionCode()).toString();
    print("androidInfo:${_androidInfo!.version}");
    //添加附加参数
    params["timestamp"] = DateTime.now().millisecondsSinceEpoch.toString();
    params["platform"] = Platform.isAndroid ? "android" : "ios";
    params["packages"] = "com.dw.zzql";
    if (Platform.isAndroid) {
      params["device"] = _androidInfo!.device;
      if (Global.utdId != null) {
        params["utdId"] = Global.utdId;
      }
      params["osVersion"] = _androidInfo!.version.release;
    } else if (Platform.isIOS) {
      params["device"] = _iosInfo!.identifierForVendor;
@@ -86,10 +91,10 @@
    params["sign"] = _getSign(params);
    var httpClient = HttpClient();
    httpClient.connectionTimeout = const Duration(seconds: 10);
    httpClient.connectionTimeout = const Duration(seconds: 20);
    var uri = Uri(
        scheme: "http",
        host: "193.112.35.168",
        host: "192.168.3.122",
        path: api,
        port: 8082,
        queryParameters: params);
@@ -98,21 +103,27 @@
    if (onStart != null) {
      onStart();
    }
    HttpRequestResult requestResult;
    try {
      HttpClientRequest request = await httpClient.postUrl(uri);
      var response = await request.close();
      if (response.statusCode == HttpStatus.ok) {
        String result = await response.transform(const Utf8Decoder()).join();
        return HttpRequestResult(true, jsonDecode(result));
        requestResult = HttpRequestResult(true, jsonDecode(result));
      } else {
        return HttpRequestResult(true, null, msg: "网络请求失败");
        requestResult = HttpRequestResult(false, null, msg: "网络请求失败");
      }
    } on TimeoutException catch (_) {
      return HttpRequestResult(true, null, msg: "网络请求超时");
      requestResult = HttpRequestResult(false, null, msg: "网络请求超时");
    } on SocketException catch (_) {
      Fluttertoast.showToast(msg: "网络请求出错");
      return HttpRequestResult(true, null, msg: "网络请求出错");
      requestResult = HttpRequestResult(false, null, msg: "网络请求出错");
    }
    if (notifyError && !requestResult.success) {
      ToastUtil.toast(requestResult.msg!);
    }
    return requestResult;
  }
}
@@ -125,6 +136,23 @@
      _showLoading(context);
    });
    _dismissDialog(context);
    if (result.success) {
      return result.data;
    }
    return null;
  }
  static Future<Map<String, dynamic>?> uploadPushRegId(
      BuildContext context, String regId) async {
    var uid = await UserUtil.getUid();
    var params = {"regId": regId};
    if (uid != null) {
      params["uid"] = uid.toString();
    }
    var result =
        await HttpUtil.baseRequest("/api/v1/user/uploadPushRegId", params, () {
    });
    if (result.success) {
      return result.data;
    }
@@ -147,7 +175,7 @@
    var result =
        await HttpUtil.baseRequest("/api/v1/user/loginPhone", params, () {
      _showLoading(context);
    });
    }, notifyError: true);
    _dismissDialog(context);
    if (result.success) {
      return result.data;
@@ -467,7 +495,6 @@
    return null;
  }
  ///获取求助信息
  static Future<Map<String, dynamic>?> addSOSRecord(
      BuildContext context, SimpleLocation location) async {
@@ -484,11 +511,9 @@
    params["address"] = location.address.toString();
    params["addressDetail"] = location.addressDetail.toString();
    var result =
    await HttpUtil.baseRequest("/api/v1/sos/addSOSRecord", params, () {
        await HttpUtil.baseRequest("/api/v1/sos/addSOSRecord", params, () {
      _showLoading(context);
    });
    _dismissDialog(context);
    if (result.success) {
@@ -496,7 +521,6 @@
    }
    return null;
  }
  ///获取求助信息
  static Future<Map<String, dynamic>?> listSOSRecord(
@@ -510,8 +534,8 @@
    Map<String, dynamic> params = {"page": page.toString()};
    params["uid"] = uid.toString();
    var result =
        await HttpUtil.baseRequest("/api/v1/sos/getSOSRecordList", params, () {});
    var result = await HttpUtil.baseRequest(
        "/api/v1/sos/getSOSRecordList", params, () {});
    if (result.success) {
      return result.data;
    }
@@ -581,3 +605,44 @@
    return null;
  }
}
class FeedBackApiUtil {
  ///查阅向我求救的SOS
  static Future<Map<String, dynamic>?> advice(
      BuildContext context, String? type, String content) async {
    var uid = await UserUtil.getUid();
    Map<String, dynamic> params = {};
    if (uid != null) {
      params["uid"] = uid.toString();
    }
    if (type != null) {
      params["type"] = type;
    }
    params["content"] = content;
    var result =
        await HttpUtil.baseRequest("/api/v1/feedback/advice", params, () {
      _showLoading(context);
    }, notifyError: true);
    _dismissDialog(context);
    if (result.success) {
      return result.data;
    }
    return null;
  }
}
class ConfigApiUtil {
  ///查阅向我求救的SOS
  static Future<Map<String, dynamic>?> getConfig() async {
    var result = await HttpUtil.baseRequest(
        "/api/v1/config/getConfig", {}, () {},
        notifyError: true);
    if (result.success) {
      return result.data;
    }
    return null;
  }
}
lib/main.dart
@@ -5,8 +5,12 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:locations/api/http.dart';
import 'package:locations/utils/ad_util.dart';
import 'package:locations/utils/app_util.dart';
import 'package:locations/utils/config_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/permission_util.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'package:permission_handler/permission_handler.dart';
@@ -14,6 +18,7 @@
import 'ui/main/main.dart';
import 'ui/widget/dialog.dart';
import 'utils/ui_constant.dart';
import 'utils/global.dart';
void main() {
  if (Platform.isAndroid) {
@@ -34,21 +39,21 @@
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
          // This is the theme of your application.
          //
          // Try running your application with "flutter run". You'll see the
          // application has a blue toolbar. Then, without quitting the app, try
          // changing the primarySwatch below to Colors.green and then invoke
          // "hot reload" (press "r" in the console where you ran "flutter run",
          // or simply save your changes to "hot reload" in a Flutter IDE).
          // Notice that the counter didn't reset back to zero; the application
          // is not restarted.
        title: 'Flutter Demo',
        theme: ThemeData(
            // This is the theme of your application.
            //
            // Try running your application with "flutter run". You'll see the
            // application has a blue toolbar. Then, without quitting the app, try
            // changing the primarySwatch below to Colors.green and then invoke
            // "hot reload" (press "r" in the console where you ran "flutter run",
            // or simply save your changes to "hot reload" in a Flutter IDE).
            // Notice that the counter didn't reset back to zero; the application
            // is not restarted.
          primaryColor: Color(0xFFFFFFFF)),
      home: SplashPage(title: ''),
    );
            primaryColor: Color(0xFFFFFFFF)),
        home: SplashPage(title: ''),
        navigatorKey: navigatorKey);
  }
}
@@ -73,51 +78,41 @@
class _SplashPageState extends State<SplashPage>
    with SingleTickerProviderStateMixin {
  int selectIndex = 1;
  Widget? _splash;
  @override
  void initState() {
    super.initState();
    // Timer(const Duration(seconds: 5), () {
    //   Navigator.of(context).push(CustomRouteSlide(MainPage(title: "")));
    // });
    Timer(const Duration(seconds: 2), () {
      // Navigator.of(context).push(CustomRouteSlide(MainPage(title: "")));
      // showGeneralDialog(context: context,
      //     pageBuilder: (BuildContext buildContext, Animation<double> animation,
      //         Animation<double> secondaryAnimation) {
      //       return PermissionNotifyDialog(() {
      //         Navigator.of(context).pop();
      //         Navigator.of(context).push(CustomRouteSlide(MainPage(title: "")));
      //       });
      //     });
    });
    // showDialog(
    //     context: context,
    //     builder: (context) => PermissionNotifyDialog((){
    //       Navigator.of(context).push(CustomRouteSlide(MainPage(title: "")));
    //     }));
// String content="欢迎您使用百度地图服务!<a href='http://www.baidu.com'> http://www.baidu.com </a> 我们非常重视您的隐私保护和个人信息保护。本隐私政策适用于您通过任何方式对百度地图各项服务的访问和使用。您可以利用百度地图搜索路况信息、商家信息、定位您所在的位置,进行路径规划、导航您想去的地址,搜索周边的服务等(以下统称“百度地图产品或服务”)。您具体获得的百度地图服务内容可能因为您使用的百度地图的版本及搭载设备不同而有所差异,如果在部分版本或搭载设备中不涵盖某些服务内容或未提供特定功能(例如:部分版本不支持登录,我们可能无法为您提供第三方服务以及其他登录后才能使用的功能;手表等穿戴设备暂时只支持步骑行的路线规划及导航,所以我们在穿戴设备上搭载的地图无法为您提供如驾车路线规划及导航等其他功能),本隐私政策中涉及到上述服务/功能及相关个人信息的内容将不适用。您可以通过多种不同的方式来使用我们的产品和服务,包括百度地图的网站、软件、供第三方网站和应用程序使用的百度地图软件开发工具包(SDK)和应用程序编程接口(API)、车载导航仪、智能后视镜等智能硬件设备。";
//       showDialog(
//           context: context,
//           builder: (context) => NotifyDialog("用户协议&隐私政策", content, () {}, () {},richText: true,height: 400,));
  }
  void init() {
    AppUtil.initApp(context).then((value) {
      //TODO 显示开屏广告
  void init() async {
    //请求配置信息
    var value = await ConfigApiUtil.getConfig();
    if (value != null) {
      ConfigUtil.saveConfig(value["data"]);
    }
    bool result = await AppUtil.initApp(context);
    double width = MediaQuery.of(context).size.width;
    double height = MediaQuery.of(context).size.height;
    Widget? splash = CSJAdUtil.loadSplash("887634894",width,height-94, (success, msg) {
      NavigatorUtil.navigateToNextPageWithFinish(context,
          MaterialPageRoute(builder: (context) {
        return MainPage(title: "");
      }));
    });
    setState(() {
      _splash = splash;
    });
  }
  Future requestPermission() async {
    await Permission.phone.request();
    await Permission.storage.request();
    await Permission.location.request();
    await PermissionUtil.openPermission(Permission.phone);
    await PermissionUtil.openPermission(Permission.locationAlways);
    return;
  }
@@ -168,20 +163,23 @@
              direction: Axis.vertical,
              children: [
                Expanded(
                    child: Container(
                  alignment: Alignment.center,
                  padding: EdgeInsets.all(72),
                  color: Colors.white,
                  child: Image.asset("assets/images/ic_splash.png"),
                )),
                    child: Stack(children: [
                  Container(
                    alignment: Alignment.center,
                    padding: const EdgeInsets.all(72),
                    color: Colors.white,
                    child: Image.asset("assets/images/ic_splash.png"),
                  ),
                  _splash != null ? _splash! : Container()
                ])),
                Container(
                  alignment: Alignment.center,
                  color: Color(0xFFF8F8F8),
                  color: const Color(0xFFF8F8F8),
                  height: 94,
                  child: Flex(
                    direction: Axis.vertical,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                    children: const [
                      Text(
                        "定位追踪轨迹",
                        style:
lib/model/common/adinfo_model.dart
New file
@@ -0,0 +1,29 @@
/// type : "csj"
/// pid : "123456"
class AdinfoModel {
  AdinfoModel({
      String? type,
      String? pid,}){
    _type = type;
    _pid = pid;
}
  AdinfoModel.fromJson(dynamic json) {
    _type = json['type'];
    _pid = json['pid'];
  }
  String? _type;
  String? _pid;
  String? get type => _type;
  String? get pid => _pid;
  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['type'] = _type;
    map['pid'] = _pid;
    return map;
  }
}
lib/model/map/location_model.dart
@@ -10,7 +10,8 @@
/// street : "嘉康路"
/// address : "中国重庆市渝北区礼嘉街道嘉康路"
/// locationDetail : "在天际湾附近"
///
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
class BaiduLocation {
  BaiduLocation({
    String? locTime,
@@ -160,6 +161,11 @@
    map['addressDetail'] = _addressDetail;
    return map;
  }
  BMFCoordinate toBMFCoordinate(){
    return BMFCoordinate(_latitude!,_longitude!);
  }
}
class UserLocationInfo {
lib/model/sos/sos_record_model.dart
@@ -9,6 +9,7 @@
    SimpleLocation? location,
    String? createTime,
    String? targetDesc,
    int? targetUid
  }) {
    _portrait = portrait;
    _from = from;
@@ -17,6 +18,7 @@
    _location = location;
    _createTime = createTime;
    _targetDesc = targetDesc;
    _targetUid = targetUid;
  }
  SosRecordModel.fromJson(dynamic json) {
@@ -24,9 +26,10 @@
    _from = json['from'];
    _desc = json['desc'];
    _phone = json['phone'];
    _location =SimpleLocation.fromJson(json['location']);
    _location = SimpleLocation.fromJson(json['location']);
    _createTime = json['createTime'];
    _targetDesc = json['targetDesc'];
    _targetUid =int.parse( json['targetUid']);
  }
  String? _portrait;
@@ -36,6 +39,7 @@
  SimpleLocation? _location;
  String? _createTime;
  String? _targetDesc;
  int? _targetUid;
  String? get portrait => _portrait;
@@ -51,6 +55,8 @@
  String? get targetDesc => _targetDesc;
  int? get targetUid => _targetUid;
  Map<String?, dynamic> toJson() {
    final map = <String?, dynamic>{};
    map['portrait'] = _portrait;
@@ -62,6 +68,7 @@
    }
    map['createTime'] = _createTime;
    map['targetDesc'] = _targetDesc;
    map['targetUid'] = _targetUid;
    return map;
  }
}
lib/ui/common/browser.dart
@@ -4,20 +4,26 @@
import 'package:flutter/material.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/utils/jsinterface.dart';
import 'package:locations/utils/string_util.dart';
import 'package:webview_flutter/webview_flutter.dart';
class BrowserPage extends StatefulWidget {
  BrowserPage({Key? key, required this.title}) : super(key: key);
  BrowserPage({Key? key, required this.title, required this.url})
      : super(key: key);
  final String title;
  final String url;
  @override
  _BrowserPageState createState() => _BrowserPageState();
  _BrowserPageState createState() => _BrowserPageState(title, url);
}
class _BrowserPageState extends State<BrowserPage>
    with SingleTickerProviderStateMixin {
  String title = "";
  String? url;
  double progress = 0;
  _BrowserPageState(this.title, this.url);
  @override
  void initState() {
@@ -41,19 +47,22 @@
              height: 1,
              child: LinearProgressIndicator(
                backgroundColor: Colors.white,
                valueColor: AlwaysStoppedAnimation(Color(0xFF0E96FF)),
                valueColor: const AlwaysStoppedAnimation(Color(0xFF0E96FF)),
                value: progress,
              ),
            ),
            Expanded(
                child: WebView(
                  //http://192.168.3.122:8848/test/JsTest.html
              initialUrl: 'http://192.168.3.122:8848/test/JsTest.html',
              onWebViewCreated: (WebViewController webViewController){
                _webViewController=webViewController;
              //http://192.168.3.122:8848/test/JsTest.html
              initialUrl: "http://192.168.3.122:8848/base/JsTest.html",
              onWebViewCreated: (WebViewController webViewController) {
                _webViewController = webViewController;
              },
              javascriptMode: JavascriptMode.unrestricted,
              javascriptChannels: JavascriptInterface(context,_webViewController).getInterfaces(),
              javascriptChannels:
                  JavascriptInterface(context, _webViewController)
                      .getInterfaces(),
              navigationDelegate: (NavigationRequest request) {
                print("链接:${request.url}");
                if (!request.url.startsWith("http")) {
@@ -66,6 +75,13 @@
              },
              onPageFinished: (url) {
                print("process:onPageFinished-$url");
                _webViewController!.getTitle().then((value) {
                  if (value != null) {
                    setState(() {
                      title = value;
                    });
                  }
                });
              },
              onProgress: (int process) {
                print("process:$process");
lib/ui/main/location.dart
@@ -12,6 +12,7 @@
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/map.dart';
import 'package:locations/ui/map/travel.dart';
import 'package:locations/ui/mine/add_location_person.dart';
import 'package:locations/ui/mine/login.dart';
@@ -25,11 +26,13 @@
import 'package:locations/utils/location_util.dart';
import 'package:locations/utils/map_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/permission_util.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
typedef OnPositionHiidenChange = void Function(bool hidden);
@@ -51,6 +54,38 @@
  @override
  _LocationPageState createState() => _LocationPageState();
  //获取定位邀请
  static void getLocationInvite(BuildContext context) async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      return;
    }
    Map<String, dynamic>? result =
    await LocationApiUtil.getInviteLocation(uid);
    if (result!["code"] == 0) {
      LocationUserModel model = LocationUserModel.fromJson(result["data"]);
      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"]);
              }
            });
          }));
    }
  }
}
class _LocationPageState extends State<LocationPage>
@@ -71,7 +106,13 @@
      zoomLevel: 12,
      mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 200));
  //定位
  var eventBusLocation;
  //定位权限
  var eventBusLocationPermission;
  PermissionStatus? locationPermissionStatus;
  @override
  void initState() {
@@ -88,44 +129,30 @@
      drawMarker(
          BMFCoordinate(event.location!.latitude!, event.location!.longitude!));
    });
    _getLocationInvite();
    eventBusLocationPermission =
        eventBus.on<UserLocationPermissionEventBus>().listen((event) {
      setState(() {
        locationPermissionStatus = event.status;
      });
    });
    Permission.locationAlways.status.then((value) {
      setState(() {
        locationPermissionStatus = value;
      });
    });
    LocationPage.getLocationInvite(context);
  }
  void _getLocationInvite() async {
    var uid = await UserUtil.getUid();
    if (uid == null) {
      return;
    }
    Map<String, dynamic>? result =
        await LocationApiUtil.getInviteLocation(uid!);
    if (result!["code"] == 0) {
      LocationUserModel model = LocationUserModel.fromJson(result["data"]);
      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() {
    super.dispose();
    (eventBusLocation as StreamSubscription).cancel();
    (eventBusLocationPermission as StreamSubscription).cancel();
  }
  @override
@@ -146,9 +173,55 @@
                      height: 33,
                      width: 33,
                    ))),
            Align(
                alignment: Alignment.bottomCenter,
                child: getAddLocationObjectView()),
            locationPermissionStatus != null &&
                    locationPermissionStatus != PermissionStatus.granted
                ? Align(
                    alignment: Alignment.bottomCenter,
                    child: InkWell(
                        onTap: () {
                          PermissionUtil.openPermission(
                                  Permission.locationAlways,
                                  force: true)
                              .then((value) {
                            setState(() {
                              locationPermissionStatus = value;
                            });
                          });
                        },
                        child: Container(
                          height: 38,
                          margin: const EdgeInsets.fromLTRB(10, 0, 10, 80),
                          padding: const EdgeInsets.fromLTRB(18, 0, 10, 0),
                          decoration: BoxDecoration(
                            color: const Color(0xFFFF5163),
                            borderRadius: BorderRadius.circular(10),
                          ),
                          child: Row(
                            children: [
                              const Text(
                                "开启位置权限后软件才能正常运行",
                                style: TextStyle(
                                    color: Colors.white, fontSize: 15),
                              ),
                              Expanded(child: Container()),
                              const Text(
                                "去开启",
                                style: TextStyle(
                                    color: Colors.white, fontSize: 15),
                              ),
                              const SizedBox(
                                width: 7,
                              ),
                              Image.asset(
                                "assets/images/common/icon_location_permission_input.png",
                                width: 10,
                              )
                            ],
                          ),
                        )))
                : Container(),
            //地图marker截图组件
            Positioned(
@@ -197,65 +270,17 @@
    if (_mapController == null) {
      return;
    }
    Directory? tempDir = await getExternalStorageDirectory();
    String tempPath = tempDir!.path;
    File? file =
        await _captureController.capturePng("$tempPath/location_person.png");
    String base64Img = await _captureController.capturePng();
    if (userMarker == null) {
      userMarker =
          await MapUtil.addMarker(_mapController, location, file!.path);
      userMarker = await MapUtil.addMarker(_mapController, location, base64Img);
    } else {
      userMarker!.updateIcon(file!.path);
      userMarker!.updateIcon(base64Img);
      userMarker!.updatePosition(location);
    }
    _mapController!.setCenterCoordinate(location, true);
  }
  //添加想定位的人
  Widget getAddLocationObjectView() {
    return InkWell(
        onTap: () {
          UserUtil.isLogin().then((value) {
            if (!value) {
              NavigatorUtil.navigateToNextPage(
                  context, LoginPage(title: ""), (data) {});
              return;
            }
            NavigatorUtil.navigateToNextPage(
                context, AddLocationPersonPage(title: ""), (data) {});
          });
        },
        child: Container(
          alignment: Alignment.topCenter,
          height: 72,
          margin: const EdgeInsets.fromLTRB(10, 0, 10, 0),
          padding: const EdgeInsets.fromLTRB(0, 18, 0, 0),
          decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: const BorderRadius.only(
                  topLeft: Radius.circular(10), topRight: Radius.circular(10)),
              boxShadow: getViewShadow()),
          child: Flex(
            direction: Axis.horizontal,
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Image.asset(
                "assets/images/common/icon_person.png",
                height: 17,
              ),
              Container(
                width: 11,
                height: 1,
              ),
              const Text(
                "添加想定位的对象",
                style: TextStyle(color: ColorConstant.theme, fontSize: 15),
              )
            ],
          ),
        ));
  }
  @override
  bool get wantKeepAlive => true;
@@ -309,7 +334,7 @@
          BaiduLocation location = BaiduLocation.fromJson(map);
          setState(() {
            _userLocationInfo = UserLocationInfo(
                uid: user!.id,
                uid: user != null ? user!.id : null,
                location: SimpleLocation.fromBaiDuLocation(location),
                updateTime: location.locTime,
                locationCount: 1);
@@ -364,20 +389,24 @@
          },
        ));
    if (selectUser != null) {
      setState(() {
        user = (selectUser as LocationUserModel).userInfo;
      });
      _setSelectUserData(selectUser);
    }
  }
      //开始获取定位
      UserLocationInfo? _userLocation =
          await LocationApiUtil.getLocation(user!.id!.toString());
      if (_userLocation != null) {
        print("更新时间:${_userLocation.updateTime}");
        setState(() {
          _userLocationInfo = _userLocation;
        });
        eventBus.fire(UserLocationInfoEventBus(_userLocation.location, user));
      }
  _setSelectUserData(LocationUserModel selectUser) async {
    setState(() {
      user = selectUser.userInfo;
    });
    //开始获取定位
    UserLocationInfo? _userLocation =
        await LocationApiUtil.getLocation(user!.id!.toString());
    if (_userLocation != null) {
      print("更新时间:${_userLocation.updateTime}");
      setState(() {
        _userLocationInfo = _userLocation;
      });
      eventBus.fire(UserLocationInfoEventBus(_userLocation.location, user));
    }
  }
@@ -390,14 +419,14 @@
    print(result);
    if (result!["code"] == 0) {
      List<dynamic> list = result!["data"]["list"];
      List<dynamic> list = result["data"]["list"];
      List<LocationUserModel> users = [];
      list.forEach((element) {
        users.add(LocationUserModel.fromJson(element));
      });
      userList = users;
    } else {
      ToastUtil.toast(result!["msg"]);
      ToastUtil.toast(result["msg"]);
    }
  }
@@ -444,8 +473,15 @@
                      ))),
              InkWell(
                  onTap: () {
                    NavigatorUtil.navigateToNextPage(
                        context, SOSPage(title: ""), (data) {});
                    UserUtil.isLogin().then((value) {
                      if (value) {
                        NavigatorUtil.navigateToNextPage(
                            context, SOSPage(title: ""), (data) {});
                      } else {
                        NavigatorUtil.navigateToNextPage(
                            context, LoginPage(title: ""), (data) {});
                      }
                    });
                  },
                  child: Container(
                    width: 45,
@@ -613,6 +649,19 @@
                                          fontSize: 12,
                                          onClick: () {
                                            print("实时共享");
                                            NavigatorUtil.navigateToNextPage(
                                                context,
                                                MapPage(
                                                  "位置共享",
                                                  share: true,
                                                  uid: user!.id,
                                                  location:
                                                      _userLocationInfo != null
                                                          ? _userLocationInfo!
                                                              .location
                                                          : null,
                                                ),
                                                (data) {});
                                          },
                                        ),
                                      ])
@@ -681,7 +730,64 @@
                                      ]))))
                ],
              )),
          Expanded(child:
          Align(
              alignment: Alignment.bottomCenter,
              child: getAddLocationObjectView())),
        ]));
  }
  //添加想定位的人
  Widget getAddLocationObjectView() {
    return InkWell(
        onTap: () {
          UserUtil.isLogin().then((value) {
            if (!value) {
              NavigatorUtil.navigateToNextPage(
                  context, LoginPage(title: ""), (data) {});
              return;
            }
            NavigatorUtil.navigateToNextPage(
                context, AddLocationPersonPage(title: ""), (data) {
                  if(data!=null) {
                    _setSelectUserData(data);
                  }
            });
          });
        },
        child: Container(
          alignment: Alignment.topCenter,
          height: 72,
          // margin: const EdgeInsets.fromLTRB(10, 0, 10, 0),
          padding: const EdgeInsets.fromLTRB(0, 18, 0, 0),
          decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: const BorderRadius.only(
                  topLeft: Radius.circular(10), topRight: Radius.circular(10)),
              boxShadow: getViewShadow()),
          child: Flex(
            direction: Axis.horizontal,
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Image.asset(
                "assets/images/common/icon_person.png",
                height: 17,
              ),
              Container(
                width: 11,
                height: 1,
              ),
              const Text(
                "添加想定位的对象",
                style: TextStyle(color: ColorConstant.theme, fontSize: 15),
              )
            ],
          ),
        ));
  }
  Widget _getStatus(LocationInviteStatus? status, LocationUserModel user) {
@@ -904,22 +1010,29 @@
                        Container(
                          height: 5,
                        ),
                        Flex(
                          direction: Axis.horizontal,
                          mainAxisAlignment: MainAxisAlignment.center,
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: [
                            Text(
                              "查看全部共${_userLocationInfo!.locationCount}次定位 ",
                              style: const TextStyle(
                                  color: Color(0xFF9DAAB3), fontSize: 10),
                            ),
                            Image.asset(
                              "assets/images/main/icon_location_position_more.png",
                              height: 10,
                            ),
                          ],
                        ),
                        InkWell(
                            onTap: () {
                              NavigatorUtil.navigateToNextPage(
                                  context,
                                  MyTravelPage(uid: _userLocationInfo!.uid),
                                  (data) {});
                            },
                            child: Flex(
                              direction: Axis.horizontal,
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: [
                                Text(
                                  "查看全部共${_userLocationInfo!.locationCount}次定位 ",
                                  style: const TextStyle(
                                      color: Color(0xFF9DAAB3), fontSize: 10),
                                ),
                                Image.asset(
                                  "assets/images/main/icon_location_position_more.png",
                                  height: 10,
                                ),
                              ],
                            )),
                      ],
                    ),
                  ))));
lib/ui/main/main.dart
@@ -11,6 +11,7 @@
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/ad_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
@@ -89,6 +90,14 @@
    _tabController =
        TabController(length: _pages.length, initialIndex: 1, vsync: this);
    super.initState();
    CSJAdUtil.loadInterstitial("947240112");
    // CSJAdUtil.loadReward("947239184", (success, msg) {
    //
    //   print("success:$success  msg:$msg");
    //
    // });
  }
  //设置选中的导航栏
lib/ui/main/mine.dart
@@ -5,13 +5,17 @@
import 'package:locations/api/http.dart';
import 'package:locations/model/user/user_info.dart';
import 'package:locations/ui/common/browser.dart';
import 'package:locations/ui/map/travel.dart';
import 'package:locations/ui/mine/advice.dart';
import 'package:locations/ui/mine/login.dart';
import 'package:locations/ui/mine/permission.dart';
import 'package:locations/ui/mine/settings.dart';
import 'package:locations/ui/mine/share_to_friends.dart';
import 'package:locations/ui/mine/try_functions.dart';
import 'package:locations/utils/ad_util.dart';
import 'package:locations/utils/config_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
@@ -66,47 +70,95 @@
  bool isLogin = false;
  bool isVIP = false;
  List<Widget> list = [];
  Widget? adView;
  bool adDeleted = false;
  final List<Functions> data = [
    Functions("assets/images/mine/icon_mine_permission.png", "权限设置",
        "permission", false),
    Functions("assets/images/mine/icon_mine_kefu.png", "在线客服", "kefu", false),
    Functions(
        "assets/images/mine/icon_mine_permission.png", "权限设置", "permission"),
    Functions("assets/images/mine/icon_mine_kefu.png", "在线客服", "kefu"),
    Functions("assets/images/mine/icon_mine_footmark.png", "轨迹足迹", "footmark"),
        "assets/images/mine/icon_mine_footmark.png", "轨迹足迹", "footmark", true),
    Functions("assets/images/mine/icon_mine_functions.png", "功能使用", "functions",
        true),
    Functions(
        "assets/images/mine/icon_mine_functions.png", "功能使用", "functions"),
    Functions("assets/images/mine/icon_mine_course.png", "使用教程", "course"),
    Functions("assets/images/mine/icon_mine_share.png", "分享好友", "share"),
    Functions("assets/images/mine/icon_mine_advice.png", "意见反馈", "advice"),
    Functions("assets/images/mine/icon_mine_protocol.png", "用户协议", "protocol"),
    Functions("assets/images/mine/icon_mine_privacy.png", "隐私政策", "privacy"),
    Functions("assets/images/mine/icon_mine_setting.png", "更多设置", "setting"),
        "assets/images/mine/icon_mine_course.png", "使用教程", "course", false),
    Functions("assets/images/mine/icon_mine_share.png", "分享好友", "share", false),
    Functions(
        "assets/images/mine/icon_mine_advice.png", "意见反馈", "advice", false),
    Functions(
        "assets/images/mine/icon_mine_protocol.png", "用户协议", "protocol", false),
    Functions(
        "assets/images/mine/icon_mine_privacy.png", "隐私政策", "privacy", false),
    Functions(
        "assets/images/mine/icon_mine_setting.png", "更多设置", "setting", false),
  ];
  void _onClick(Functions function) {
  void _onClick(Functions function) async {
    if (function.needLogin) {
      bool login = await UserUtil.isLogin();
      if (!login) {
        NavigatorUtil.navigateToNextPage(
            context, LoginPage(title: ""), (data) {});
        return;
      }
    }
    int? uid = await UserUtil.getUid();
    switch (function.key) {
      case "permission":
        Navigator.of(context).push(CustomRouteSlide(PermissionPage(title: "")));
        NavigatorUtil.navigateToNextPage(
            context, PermissionPage(title: ""), (data) {});
        break;
      case "kefu":
        ConfigUtil.getConfig(ConfigKey.kefu).then((value) {
          if (!StringUtil.isNullOrEmpty(value)) {
            NavigatorUtil.navigateToNextPage(
                context, BrowserPage(title: "在线客服", url: value!), (data) {});
          }
        });
        break;
      case "footmark":
        NavigatorUtil.navigateToNextPage(
            context, MyTravelPage(uid: uid!), (data) {});
        break;
      case "functions":
        Navigator.of(context)
            .push(CustomRouteSlide(TryFunctionsPage(title: "")));
        NavigatorUtil.navigateToNextPage(
            context, TryFunctionsPage(title: ""), (data) {});
        break;
      case "course":
        ConfigUtil.getConfig(ConfigKey.course).then((value) {
          if (!StringUtil.isNullOrEmpty(value)) {
            NavigatorUtil.navigateToNextPage(
                context, BrowserPage(title: "使用教程", url: value!), (data) {});
          }
        });
        break;
      case "share":
        Navigator.of(context)
            .push(CustomRouteSlide(ShareToFriendsPage(title: "")));
        NavigatorUtil.navigateToNextPage(
            context, ShareToFriendsPage(title: ""), (data) {});
        break;
      case "advice":
        Navigator.of(context).push(CustomRouteSlide(AdvicePage(title: "")));
        NavigatorUtil.navigateToNextPage(
            context, AdvicePage(title: ""), (data) {});
        break;
      case "protocol":
        Navigator.of(context).push(CustomRouteSlide(BrowserPage(title: "")));
        NavigatorUtil.navigateToNextPage(
            context,
            BrowserPage(
              title: "用户协议",
              url: Constant.PROTOCOL_URL,
            ),
            (data) {});
        break;
      case "privacy":
        NavigatorUtil.navigateToNextPage(
            context,
            BrowserPage(
              title: "隐私政策",
              url: Constant.PRIVACY_URL,
            ),
            (data) {});
        break;
      case "setting":
        NavigatorUtil.navigateToNextPage(context, SettingPage(title: ""),
@@ -148,6 +200,28 @@
  void _login() {
    NavigatorUtil.navigateToNextPage(context, LoginPage(title: ""), (data) {
      _getUserInfo();
    });
  }
  void _loadAd() async {
    if (adView != null) {
      return;
    }
    if (adDeleted) {
      return;
    }
    Widget ad = CSJAdUtil.loadExpress(
        "947239180", MediaQuery.of(context).size.width - 20, 200,
        (success, msg) {
      adDeleted = true;
      setState(() {
        adView = null;
      });
    });
    setState(() {
      adView = ad;
    });
  }
@@ -280,6 +354,7 @@
  @override
  Widget build(BuildContext context) {
    _loadAd();
    return Scaffold(
        backgroundColor: Colors.white,
        body: SingleChildScrollView(
@@ -367,30 +442,56 @@
                                      }
                                    });
                                  },
                                  child: Container(
                                      width: 99,
                                      height: 26,
                                      alignment: Alignment.center,
                                      child: Text(
                                        isVIP ? "查看详情" : "立即开通",
                                        style: const TextStyle(
                                            color: Color(0xFFD4A880)),
                                      ),
                                      decoration: const BoxDecoration(
                                        color: Color(0xFFFAEAB9),
                                        borderRadius: BorderRadius.only(
                                            topLeft: Radius.circular(13),
                                            bottomLeft: Radius.circular(13)),
                                        boxShadow: [
                                          BoxShadow(
                                              color: Color(0x4D0E96FF),
                                              blurRadius: 2.0,
                                              offset:
                                                  Offset(0.0, 3.0), //阴影y轴偏移量
                                              spreadRadius: 0 //阴影扩散程度
                                              )
                                        ],
                                      ))))
                                  child: InkWell(
                                      onTap: () {
                                        UserUtil.isLogin().then((value) {
                                          if (!value) {
                                            NavigatorUtil.navigateToNextPage(
                                                context,
                                                LoginPage(title: ""),
                                                (data) {});
                                            return;
                                          }
                                          ConfigUtil.getConfig(
                                                  ConfigKey.vipLink)
                                              .then((value) {
                                            if (!StringUtil.isNullOrEmpty(
                                                value)) {
                                              NavigatorUtil.navigateToNextPage(
                                                  context,
                                                  BrowserPage(
                                                      title: "会员", url: value!),
                                                  (data) {});
                                            }
                                          });
                                        });
                                      },
                                      child: Container(
                                          width: 99,
                                          height: 26,
                                          alignment: Alignment.center,
                                          child: Text(
                                            isVIP ? "查看详情" : "立即开通",
                                            style: const TextStyle(
                                                color: Color(0xFFD4A880)),
                                          ),
                                          decoration: const BoxDecoration(
                                            color: Color(0xFFFAEAB9),
                                            borderRadius: BorderRadius.only(
                                                topLeft: Radius.circular(13),
                                                bottomLeft:
                                                    Radius.circular(13)),
                                            boxShadow: [
                                              BoxShadow(
                                                  color: Color(0x4D0E96FF),
                                                  blurRadius: 2.0,
                                                  offset: Offset(0.0, 3.0),
                                                  //阴影y轴偏移量
                                                  spreadRadius: 0 //阴影扩散程度
                                                  )
                                            ],
                                          )))))
                        ],
                      ),
                      decoration: const BoxDecoration(
@@ -411,6 +512,13 @@
                ],
              ),
            ),
            Container(
              child: adView ?? Container(),
              margin: EdgeInsets.only(left: 10, right: 10),
            ),
            //广告
            //功能区域
            Container(
              height: 340,
@@ -434,9 +542,10 @@
}
class Functions {
  Functions(this.icon, this.name, this.key);
  Functions(this.icon, this.name, this.key, this.needLogin);
  String icon;
  String name;
  String key;
  bool needLogin;
}
lib/ui/main/travel_main.dart
@@ -74,7 +74,7 @@
  @override
  void initState() {
    super.initState();
    _animationController = new AnimationController(
    _animationController = AnimationController(
        duration: const Duration(milliseconds: 300), vsync: this);
    final CurvedAnimation curve =
        CurvedAnimation(parent: _animationController!, curve: Curves.easeIn);
@@ -117,7 +117,7 @@
    subscription = await LocationUtil.startLocation(scanspan, (state, map) {
      if (state == LocationState.success) {
        double lat = map!["latitude"] as double;
        double lng = map!["longitude"] as double;
        double lng = map["longitude"] as double;
        currentPosition = BMFCoordinate(lat, lng);
        _mapController!.setCenterCoordinate(BMFCoordinate(lat, lng), true);
        setCurrentPosition(BMFCoordinate(lat, lng));
@@ -134,11 +134,9 @@
  }
  setCurrentPosition(BMFCoordinate position) async {
    Directory? tempDir = await getExternalStorageDirectory();
    String tempPath = tempDir!.path;
    if (userMarker == null) {
      _captureController.capturePng(tempPath + "/portrait.png").then((value) {
        MapUtil.addMarker(_mapController, position, value!.path,
      _captureController.capturePng().then((value) {
        MapUtil.addMarker(_mapController, position, value,
                offset: BMFPoint(0, 40), zIndex: 10)
            .then((value) {
          userMarker = value;
@@ -163,6 +161,7 @@
              child: //----------搜索框--------
                  Flex(
                direction: Axis.horizontal,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Expanded(
                      child: InkWell(
@@ -396,7 +395,7 @@
                      LocationUtil.startLocation(0, (state, map) {
                        if (state == LocationState.success) {
                          double lat = map!["latitude"] as double;
                          double lng = map!["longitude"] as double;
                          double lng = map["longitude"] as double;
                          currentPosition = BMFCoordinate(lat, lng);
                          _mapController!.setCenterCoordinate(
                              BMFCoordinate(lat, lng), true);
@@ -475,7 +474,16 @@
                ),
                InkWell(
                  onTap: () {
                    _startLocation(0);
                    if(travelRECIng){
                      //正在录制轨迹
                      if( Global.currentPosition!=null) {
                        _mapController!.updateMapOptions(BMFMapOptions(center:
                        Global.currentPosition
                        ));
                      }
                    }else {
                      _startLocation(0);
                    }
                  },
                  child: Column(
                    children: [
lib/ui/map/location_search.dart
@@ -7,10 +7,14 @@
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_baidu_mapapi_search/flutter_baidu_mapapi_search.dart';
import 'package:locations/utils/ad_util.dart';
import 'package:locations/utils/global.dart';
import 'package:locations/utils/location_util.dart';
import 'package:locations/utils/map_util.dart';
import 'package:locations/utils/search_util.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:url_launcher/url_launcher.dart';
//控件阴影
List<BoxShadow> getViewShadow() {
@@ -45,10 +49,19 @@
  //1-搜索页面  2-建议搜索页面 3-结果展示页面
  int state = 1;
  Widget? expressAd;
  TextEditingController? editingController;
  List<BMFPoiInfo>? suggestList;
  List<BMFPoiInfo>? recordList;
  BMFCoordinate? currentPosition;
  //选中位置信息
  BMFPoiInfo? selectedPoiInfo;
  BMFMarker? selectedPoiInfoMarker;
  bool deleteAd = false;
  @override
  void initState() {
@@ -57,8 +70,25 @@
    loadRecord();
  }
  void loadAd() async {
    if (deleteAd && expressAd == null) {
      return;
    }
    Widget ad = CSJAdUtil.loadExpress(
        "947239180", MediaQuery.of(context).size.width - 20, 190,
        (success, msg) {
      setState(() {
        expressAd = null;
        deleteAd = true;
      });
    });
    setState(() {
      expressAd = ad;
    });
  }
  //加载记录页面
  void loadRecord(){
  void loadRecord() {
    SearchUtil.getRecordList().then((value) {
      setState(() {
        recordList = value;
@@ -86,6 +116,7 @@
  @override
  Widget build(BuildContext context) {
    loadAd();
    return Scaffold(
        resizeToAvoidBottomInset: false,
        backgroundColor: Colors.white,
@@ -102,8 +133,8 @@
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(10),
                  boxShadow: getViewShadow()),
              child: Flex(
                direction: Axis.horizontal,
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  InkWell(
                      onTap: () {
@@ -117,31 +148,51 @@
                    width: 10,
                  ),
                  Expanded(
                      child: TextField(
                    autofocus: false,
                    controller: editingController,
                    onChanged: (text) {
                      print(text);
                      // setState(() {
                      //   if (text.isNotEmpty)
                      //     state = 2;
                      //   else
                      //     state = 1;
                      // });
                      startPOISearch(text);
                    },
                    decoration: const InputDecoration(
                        focusedBorder: InputBorder.none,
                        border: InputBorder.none,
                        hintText: "地图上找位置",
                        hintStyle:
                            TextStyle(fontSize: 14, color: Color(0xFFC4CDD1))),
                  )),
                      child: state == 3
                          ? Text(
                              selectedPoiInfo!.name!,
                              overflow: TextOverflow.ellipsis,
                              style: const TextStyle(
                                  fontSize: 14, color: ColorConstant.title),
                            )
                          : TextField(
                              autofocus: false,
                              controller: editingController,
                              onChanged: (text) {
                                print(text);
                                // setState(() {
                                //   if (text.isNotEmpty)
                                //     state = 2;
                                //   else
                                //     state = 1;
                                // });
                                startPOISearch(text);
                              },
                              decoration: const InputDecoration(
                                  focusedBorder: InputBorder.none,
                                  border: InputBorder.none,
                                  hintText: "地图上找位置",
                                  hintStyle: TextStyle(
                                      fontSize: 14, color: Color(0xFFC4CDD1))),
                            )),
                  InkWell(
                    onTap: () {
                      // selectedPoiInfo.detailInfo.
                      if (state == 3) {
                        if (selectedPoiInfoMarker != null) {
                          MapUtil.removeMarker(
                              _mapController, selectedPoiInfoMarker!);
                          selectedPoiInfoMarker = null;
                        }
                        setState(() {
                          state = 2;
                        });
                      }
                    },
                    child: Container(
                        padding: const EdgeInsets.only(right: 10),
                        child: const Text(
                          "搜索",
                        child: Text(
                          state == 3 ? "取消" : "搜索",
                          style: TextStyle(
                              color: ColorConstant.theme, fontSize: 15),
                        )),
@@ -155,13 +206,104 @@
        ]));
  }
  //banner广告
  Widget? _banner;
  loadBanner() {
    if (_banner != null) {
      return;
    }
    Widget ad = CSJAdUtil.loadBanner(
        "947242084",
        MediaQuery.of(context).size.width,
        MediaQuery.of(context).size.width / 4, (success, msg) {
      setState(() {
        _banner = null;
      });
    });
    setState(() {
      _banner = ad;
    });
  }
  Widget getContentView() {
    loadBanner();
    if (state == 1)
      return getNotSearchView();
    else if (state == 2) {
      return getSearchSuggestView();
    } else
      return Container();
    } else {
      String? phone = selectedPoiInfo!.phone;
      return Stack(
        alignment: Alignment.bottomCenter,
        children: [
          Container(
            padding: EdgeInsets.fromLTRB(20, 15, 20, 15),
            height: 97,
            decoration: BoxDecoration(
                boxShadow: getViewShadow(),
                color: Colors.white,
                borderRadius: const BorderRadius.only(
                    topLeft: Radius.circular(15),
                    topRight: Radius.circular(15))),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(children: [
                  Expanded(
                      child: Text(
                    "${selectedPoiInfo!.name!}",
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    style: const TextStyle(
                        color: ColorConstant.title, fontSize: 20),
                  )),
                  const SizedBox(
                    width: 4,
                  ),
                  StringUtil.isNullOrEmpty(phone)
                      ? Container()
                      : InkWell(
                          onTap: () {
                            launch("tel:" + phone!);
                          },
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.center,
                            children: [
                              Image.asset(
                                "assets/images/common/icon_phone.png",
                                width: 14,
                              ),
                              const SizedBox(
                                height: 4,
                              ),
                              const Text(
                                "电话",
                                style: TextStyle(
                                    color: Color(0xFF393939), fontSize: 9),
                              )
                            ],
                          ))
                ]),
                Expanded(child: Container()),
                Text(
                  "${MapUtil.getDistanceDesc(selectedPoiInfo!.distance!)} |${selectedPoiInfo!.address!}",
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style:
                      const TextStyle(color: Color(0xFF666666), fontSize: 12),
                )
              ],
            ),
          ),
          Container(
            margin: const EdgeInsets.only(bottom: 100),
            height: MediaQuery.of(context).size.width / 4,
            child: _banner ?? Container(),
          )
        ],
      );
    }
  }
  Widget getNotSearchView() {
@@ -227,7 +369,7 @@
                )),
            //广告
            Container(
              height: 210,
              child: expressAd ?? Container(),
            ),
          ]),
        ),
@@ -240,14 +382,17 @@
            child: Container(
          color: Colors.white,
          padding: const EdgeInsets.only(left: 20, right: 20),
          child:  ListView.builder(itemBuilder: (BuildContext context, int index){
              return getHistoryItem(recordList![index].name!, recordList![index].address!, recordList![index]);
            },itemCount: recordList==null?0:recordList!.length,) ,
          child: ListView.builder(
            itemBuilder: (BuildContext context, int index) {
              return getHistoryItem(recordList![index].name!,
                  recordList![index].address!, recordList![index]);
            },
            itemCount: recordList == null ? 0 : recordList!.length,
          ),
        ))
      ],
    );
  }
  setSearchKey(String key) {
    editingController!.text = key;
@@ -325,10 +470,10 @@
                  alignment: Alignment.center,
                  height: 50,
                  width: 50,
                  child: CircularProgressIndicator(strokeWidth: 3.0));
            } else if (suggestList!.length == 0) {
                  child: const CircularProgressIndicator(strokeWidth: 3.0));
            } else if (suggestList!.isEmpty) {
              return Container(
                  alignment: Alignment.center, child: Text("暂无数据"));
                  alignment: Alignment.center, child: const Text("暂无数据"));
            } else {
              return getHistoryItem(suggestList![index].name!,
                  suggestList![index].address!, suggestList![index]);
@@ -339,17 +484,48 @@
        ));
  }
  void _showPOIDetail(BMFPoiInfo info) async {
    double? distance;
    if (Global.currentPosition != null) {
      distance = await MapUtil.getDistance(
          Global.currentPosition!, BMFCoordinate.fromMap(info!.pt!.toMap()));
    }
    distance ??= 0;
    info.distance = distance.toInt();
    //显示地址详情
    SearchUtil.addSearchRecord(info);
    setState(() {
      state = 3;
      selectedPoiInfo = info;
    });
    //添加marker
    if (selectedPoiInfoMarker == null) {
      MapUtil.addMarker(_mapController, BMFCoordinate.fromMap(info.pt!.toMap()),
              "assets/images/common/icon_location_marker.png")
          .then((value) {
        selectedPoiInfoMarker = value;
      });
    } else {
      selectedPoiInfoMarker!
          .updatePosition(BMFCoordinate.fromMap(info.pt!.toMap()));
    }
    _mapController!
        .setCenterCoordinate(BMFCoordinate.fromMap(info.pt!.toMap()), true);
  }
  //历史记录项目
  Widget getHistoryItem(String title, String content, BMFPoiInfo info,{bool showDistance=false}) {
  Widget getHistoryItem(String title, String content, BMFPoiInfo info,
      {bool showDistance = false}) {
    return InkWell(
        onTap: () {
          SearchUtil.addSearchRecord(info);
          _showPOIDetail(info);
        },
        child: Container(
          height: 67,
          decoration: BoxDecoration(
          decoration: const BoxDecoration(
              color: Colors.white,
              border: const Border(
              border: Border(
                  bottom: BorderSide(
                      // 设置单侧边框的样式
                      color: Color(0xFFF1F2F3),
lib/ui/map/map.dart
New file
@@ -0,0 +1,740 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/map/location_model.dart';
import 'package:locations/ui/map/location_search.dart';
import 'package:locations/ui/widget/capture.dart';
import 'package:locations/ui/widget/map_marker.dart';
import 'package:locations/utils/global.dart';
import 'package:locations/utils/location_util.dart';
import 'package:locations/utils/map_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
//控件阴影
List<BoxShadow> getViewShadow() {
  return [
    BoxShadow(
      blurRadius: 6.5,
      spreadRadius: 1,
      color: Color(0x4D0E96FF),
    )
  ];
}
class MapPage extends StatefulWidget {
  final String title;
  //共享实时位置
  final bool share;
  final int? uid;
  final SimpleLocation? location;
  MapPage(this.title, {this.share = false, this.uid, this.location});
  @override
  _MapPageState createState() =>
      _MapPageState(title, share: share, uid: uid, location: location);
}
class _MapPageState extends State<MapPage> with SingleTickerProviderStateMixin {
  final String title;
  //共享实时位置
  final bool share;
  final int? uid;
  final SimpleLocation? location;
  _MapPageState(this.title, {this.share = false, this.uid, this.location});
  BMFMapController? _mapController;
  BMFMapOptions mapOptions = BMFMapOptions(
      showMapScaleBar: false,
      mapType: BMFMapType.Standard,
      center: Global.currentPosition ?? BMFCoordinate(39.917215, 116.380341),
      mapScaleBarPosition: BMFPoint(0, 200),
      zoomLevel: 12,
      mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 0));
  //选择地图类型索引(0-2D 1-3D 2-卫星)
  int selectMapTypeIndex = 0;
  //是否正在录制轨迹
  bool travelRECIng = false;
  //路径录制管理器
  TravelRECManager? _travelRECManager;
  //测绘管理器
  MeasureManager? _measureManager;
  //用戶目前的位置
  SimpleLocation? _currentMyPosition;
  SimpleLocation? _currentTargetPosition;
  BMFMarker? myMarker;
  BMFMarker? targetUserMarker;
  BMFMarker? myAddressMarker;
  BMFMarker? targetAddressUserMarker;
  //是否显示地址信息
  bool _showAddress = true;
  //是否需要测距
  bool _mesure = false;
  String _distance = "";
  String _distanceUnit = "米";
  Timer? _timer;
  final CaptureController _captureMyController = CaptureController();
  final CaptureController _captureTargetController = CaptureController();
  final CaptureController _captureMyAddressController = CaptureController();
  final CaptureController _captureTargetAddressController = CaptureController();
  double? pixelRatio;
  void _init() {
    //定位
    LocationUtil.startLocation(0, (state, map) {
      if (state == LocationState.success) {
        setState(() {
          _currentMyPosition =
              SimpleLocation.fromBaiDuLocation(BaiduLocation.fromJson(map!));
          _showMyLocationMarker();
        });
      }
    });
    if (share && uid != null) {
      Timer(const Duration(seconds: 1), () {
        _showTargetLocationMarker();
        //设置地图中心点与缩放等级
        if (_currentTargetPosition != null && _currentMyPosition != null) {
          MapUtil.getMapShowParams([
            _currentTargetPosition!.toBMFCoordinate(),
            _currentMyPosition!.toBMFCoordinate()
          ]).then((value) {
            _mapController!.updateMapOptions(BMFMapOptions(
                zoomLevel: value.zoomLevel, center: value.center));
          });
        }
      });
      _showTargetLocationMarker();
      //获取自己现在的位置
      Timer.periodic(const Duration(seconds: 10), (timer) {
        _timer = timer;
        LocationApiUtil.getLocation(uid!.toString()).then((value) {
          if (value == null) {
            return;
          }
          setState(() {
            _currentTargetPosition = value.location;
          });
          if (travelRECIng) {
            _travelRECManager!.addLocation(BMFCoordinate(
                value.location!.latitude!, value.location!.longitude!));
          }
          //设置现在的位置
          _showTargetLocationMarker();
        });
      });
    }
  }
  //显示位置信息的marker
  void _showTargetLocationMarker() async {
    if (_currentTargetPosition != null) {
      //画头像
      if (targetUserMarker != null) {
        targetUserMarker!
            .updatePosition(_currentTargetPosition!.toBMFCoordinate());
      } else {
        String value = await _captureTargetController.capturePng();
        if (StringUtil.isNullOrEmpty(value)) {
          return;
        }
        targetUserMarker = await MapUtil.addMarker(
            _mapController, _currentTargetPosition!.toBMFCoordinate(), value);
      }
      //画地址
      if (targetAddressUserMarker != null) {
        targetAddressUserMarker!
            .updatePosition(_currentTargetPosition!.toBMFCoordinate());
        String value = await _captureTargetAddressController.capturePng();
        await targetAddressUserMarker!.updateIcon(value);
      } else {
        String value = await _captureTargetAddressController.capturePng();
        if (StringUtil.isNullOrEmpty(value)) {
          return;
        }
        targetAddressUserMarker = await MapUtil.addMarker(
            _mapController, _currentTargetPosition!.toBMFCoordinate(), value,
            offset: BMFPoint(0, 2 * 64));
      }
    }
  }
  //显示位置信息的marker
  void _showMyLocationMarker() async {
    int? myUid = await UserUtil.getUid();
    if (myUid == uid) {
      return;
    }
    if (_currentMyPosition != null) {
      //画头像
      if (myMarker != null) {
        myMarker!.updatePosition(_currentMyPosition!.toBMFCoordinate());
      } else {
        String value = await _captureMyController.capturePng();
        myMarker = await MapUtil.addMarker(
            _mapController, _currentMyPosition!.toBMFCoordinate(), value);
      }
      //画地址
      if (myAddressMarker != null) {
        myAddressMarker!.updatePosition(_currentMyPosition!.toBMFCoordinate());
        String value = await _captureMyAddressController.capturePng();
        myAddressMarker!.updateIcon(value);
      } else {
        String value = await _captureMyAddressController.capturePng();
        myAddressMarker = await MapUtil.addMarker(
            _mapController, _currentMyPosition!.toBMFCoordinate(), value,
            offset: BMFPoint(0, 64 * 2));
      }
    }
  }
  @override
  void initState() {
    super.initState();
    _currentTargetPosition = location;
  }
  @override
  void dispose() {
    super.dispose();
    if (null != subscription) {
      subscription!.cancel();
    }
    LocationUtil.stopLocation();
    if (_timer != null) {
      _timer!.cancel();
    }
  }
  _showDistance(double distance) {
    print("距离:$distance");
    if (distance > 1000) {
      setState(() {
        _distance = (distance / 1000).toStringAsFixed(2);
        _distanceUnit = "千米";
      });
    } else {
      setState(() {
        _distance = distance.toStringAsFixed(0);
        _distanceUnit = "米";
      });
    }
  }
  //获取地图视图
  Widget getMapView() {
    return Container(
      child: BMFMapWidget(
        onBMFMapCreated: (controller) {
          _mapController = controller;
          _travelRECManager = TravelRECManager(_mapController);
          _measureManager = MeasureManager(_mapController);
          _init();
          _mapController?.setMapOnClickedMapPoiCallback(
              callback: (BMFMapPoi poi) {
            //是否为测量模式
            if (!_mesure) {
              return;
            }
            _measureManager!.addPoint(poi.pt!).then((value) {
              _showDistance(value);
            });
          });
          _mapController?.setMapOnClickedMapBlankCallback(
              callback: (BMFCoordinate coordinate) {
            //是否为测量模式
            if (!_mesure) {
              return;
            }
            print("地图点击:${coordinate.toString()}");
            _measureManager!.addPoint(coordinate).then((value) {
              _showDistance(value);
            });
          });
        },
        mapOptions: mapOptions,
      ),
    );
  }
  StreamSubscription<Map<String, Object>?>? subscription;
  @override
  Widget build(BuildContext context) {
    pixelRatio ??= DimenUtil.getPixelRatio(context);
    print("pixelRatio:$pixelRatio");
    return Scaffold(
        resizeToAvoidBottomInset: false,
        backgroundColor: Colors.white,
        body: Stack(children: [
          getMapView(),
          Flex(direction: Axis.vertical, children: [
            //搜索框
            Container(
              margin: const EdgeInsets.fromLTRB(10, 35, 10, 11),
              child: //----------搜索框--------
                  Flex(
                direction: Axis.horizontal,
                children: [
                  Expanded(
                      child: Container(
                          alignment: Alignment.center,
                          padding: EdgeInsets.fromLTRB(13, 0, 13, 0),
                          height: 45,
                          decoration: BoxDecoration(
                              color: Colors.white,
                              borderRadius: BorderRadius.circular(10),
                              boxShadow: getViewShadow()),
                          child: Stack(alignment: Alignment.center, children: [
                            Row(
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: [
                                InkWell(
                                    onTap: () {
                                      print("退出");
                                      Navigator.of(context).pop();
                                    },
                                    child: Row(children: [
                                      const Image(
                                          image: AssetImage(
                                            "assets/images/common/icon_back.png",
                                          ),
                                          height: 17),
                                      Container(
                                        width: 8,
                                      ),
                                      const Text(
                                        "退出",
                                        style: TextStyle(
                                            fontSize: 16,
                                            color: ColorConstant.title),
                                      )
                                    ])),
                                Container(
                                  width: 10,
                                ),
                                const Expanded(child: Text("")),
                              ],
                            ),
                            Text(
                              title,
                              style: const TextStyle(
                                  fontSize: 16, color: ColorConstant.title),
                            ),
                          ]))),
                ],
              ),
            ),
            Expanded(child: getContentView())
          ]),
          _mesure
              ? Align(
                  alignment: Alignment.bottomCenter,
                  child: Container(
                    height: 92,
                    margin: const EdgeInsets.only(left: 10, right: 10),
                    decoration: BoxDecoration(
                        color: Colors.white,
                        boxShadow: getViewShadow(),
                        borderRadius: const BorderRadius.only(
                            topLeft: Radius.circular(10),
                            topRight: Radius.circular(10))),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.center,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text.rich(TextSpan(
                          children: <TextSpan>[
                            const TextSpan(text: '距离:'),
                            TextSpan(
                                text: "$_distance",
                                style: const TextStyle(
                                    fontWeight: FontWeight.bold)),
                            TextSpan(text: _distanceUnit),
                          ],
                          style: const TextStyle(
                              color: ColorConstant.theme, fontSize: 15),
                        )),
                        const SizedBox(
                          height: 20,
                        ),
                        SizedBox(
                          height: 28,
                          child: Row(
                            children: [
                              Expanded(
                                  child: InkWell(
                                      onTap: () {
                                        _measureManager!
                                            .backPoint()
                                            .then((value) {
                                          _showDistance(value);
                                        });
                                      },
                                      child: Row(
                                        mainAxisAlignment:
                                            MainAxisAlignment.center,
                                        children: [
                                          Image.asset(
                                            "assets/images/map/icon_map_measure_back.png",
                                            height: 15.5,
                                          ),
                                          const SizedBox(
                                            width: 9.5,
                                          ),
                                          const Text(
                                            "回退",
                                            style: TextStyle(
                                                fontSize: 15,
                                                color: ColorConstant.theme),
                                          )
                                        ],
                                      ))),
                              Container(
                                height: 28,
                                width: 1,
                                color: ColorConstant.theme,
                              ),
                              Expanded(
                                  child: InkWell(
                                      onTap: () {
                                        _measureManager!.clear();
                                        _showDistance(0);
                                      },
                                      child: Row(
                                        mainAxisAlignment:
                                            MainAxisAlignment.center,
                                        children: [
                                          Image.asset(
                                            "assets/images/map/icon_map_measure_delete.png",
                                            height: 15.5,
                                          ),
                                          const SizedBox(
                                            width: 9.5,
                                          ),
                                          const Text(
                                            "删除",
                                            style: TextStyle(
                                                fontSize: 15,
                                                color: ColorConstant.theme),
                                          )
                                        ],
                                      ))),
                            ],
                          ),
                        )
                      ],
                    ),
                  ))
              : Container(),
          Positioned(
              top: -100,
              child: Row(children: [
                CaptureWidget(
                  widget: PersonLocationMarker(Image.asset(
                    "assets/images/mine/icon_mine_default_portrait.png",
                  )),
                  captureController: _captureMyController,
                ),
                CaptureWidget(
                  widget: PersonLocationMarker(
                    Image.asset(
                      "assets/images/mine/icon_mine_default_portrait.png",
                    ),
                    borderColor: const Color(0xFFFF1F35),
                  ),
                  captureController: _captureTargetController,
                ),
              ])),
          Positioned(
              top: -100,
              child: Column(children: [
                CaptureWidget(
                  widget: (_currentMyPosition != null &&
                          _currentMyPosition!.addressDetail != null)
                      ? Container(
                          width: 122,
                          height: 37,
                          decoration: BoxDecoration(
                              color: ColorConstant.theme.withAlpha(200),
                              borderRadius: BorderRadius.circular(2.5)),
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            crossAxisAlignment: CrossAxisAlignment.center,
                            children: [
                              Text(
                                "${_currentMyPosition!.addressDetail!}",
                                overflow: TextOverflow.ellipsis,
                                style: const TextStyle(
                                    fontSize: 12, color: Colors.white),
                              ),
                              Container(
                                height: 2,
                              ),
                              Text(
                                  "N ${_currentMyPosition!.latitude}, E ${_currentMyPosition!.longitude}",
                                  style: const TextStyle(
                                      fontSize: 7, color: Colors.white)),
                            ],
                          ))
                      : Container(),
                  captureController: _captureMyAddressController,
                ),
                CaptureWidget(
                  widget: (_currentTargetPosition != null &&
                          _currentTargetPosition!.addressDetail != null)
                      ? Container(
                          width: 122,
                          height: 37,
                          decoration: BoxDecoration(
                              color: const Color(0xFFFF1F35).withAlpha(200),
                              borderRadius: BorderRadius.circular(2.5)),
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            crossAxisAlignment: CrossAxisAlignment.center,
                            children: [
                              Text(
                                "${_currentTargetPosition!.addressDetail!}",
                                overflow: TextOverflow.ellipsis,
                                style: const TextStyle(
                                    fontSize: 12, color: Colors.white),
                              ),
                              Container(
                                height: 2,
                              ),
                              Text(
                                  "N ${_currentTargetPosition!.latitude}, E ${_currentTargetPosition!.longitude}",
                                  style: const TextStyle(
                                      fontSize: 7, color: Colors.white)),
                            ],
                          ))
                      : Container(),
                  captureController: _captureTargetAddressController,
                ),
              ]))
        ]));
  }
  Widget getMapTypeView(String name, String imgPath, int index) {
    return InkWell(
        onTap: () {
          setState(() {
            selectMapTypeIndex = index;
          });
          if (index == 2) {
            _mapController?.updateMapOptions(
                BMFMapOptions(mapType: BMFMapType.Satellite));
          } else if (index == 1) {
            _mapController?.updateMapOptions(BMFMapOptions(
                mapType: BMFMapType.Standard,
                buildingsEnabled: true,
                baseIndoorMapEnabled: true,
                overlookEnabled: true));
            _mapController?.showBaseIndoorMap(true);
          } else if (index == 0) {
            _mapController?.updateMapOptions(BMFMapOptions(
                mapType: BMFMapType.Standard,
                buildingsEnabled: false,
                baseIndoorMapEnabled: false));
          }
        },
        child: Column(
          children: [
            ClipRRect(
                borderRadius: BorderRadius.circular(23),
                child: Container(
                  alignment: Alignment.center,
                  color: selectMapTypeIndex == index
                      ? ColorConstant.theme
                      : Color(0xFF9DAAB3),
                  width: 71,
                  height: 71,
                  child: Image.asset(
                    imgPath,
                    width: 65,
                    height: 65,
                  ),
                )),
            Text(
              name,
              style: TextStyle(
                  fontSize: 14,
                  color: index == selectMapTypeIndex
                      ? ColorConstant.theme
                      : Color(0xFF9DAAB3)),
            )
          ],
        ));
  }
  Widget getContentView() {
    //------------工具栏-----------
    return share
        ? Stack(children: [
            Positioned(
                right: 10,
                top: 10,
                child: Container(
                  padding: EdgeInsets.only(top: 13, bottom: 13),
                  width: 43,
                  decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.circular(7.5)),
                  child: Column(
                    children: [
                      InkWell(
                        onTap: () {
                          if (!travelRECIng) {
                            if (_currentTargetPosition == null) {
                              ToastUtil.toast("尚未获取到对方位置");
                              return;
                            }
                            _travelRECManager!.startREC(
                                _currentTargetPosition!.toBMFCoordinate());
                          } else {
                            //结束录制
                            _travelRECManager!.stopREC();
                          }
                          setState(() {
                            travelRECIng = !travelRECIng;
                          });
                        },
                        child: Column(
                          children: [
                            Image.asset(
                              travelRECIng
                                  ? "assets/images/map/icon_rec_location_ing.png"
                                  : "assets/images/map/icon_rec_location.png",
                              width: 21,
                            ),
                            Container(
                              height: 2,
                            ),
                            Text(travelRECIng ? "录制中.." : "录制轨迹",
                                style: TextStyle(
                                    fontSize: 7,
                                    color: travelRECIng
                                        ? Color(0xFFFF1F35)
                                        : Color(0xFF9DAAB3)))
                          ],
                        ),
                      ),
                      Container(
                        height: 14,
                      ),
                      InkWell(
                        onTap: () {
                          if (myAddressMarker != null) {
                            myAddressMarker!.updateVisible(!_showAddress);
                          }
                          if (targetAddressUserMarker != null) {
                            targetAddressUserMarker!
                                .updateVisible(!_showAddress);
                          }
                          setState(() {
                            _showAddress = !_showAddress;
                          });
                        },
                        child: Column(
                          children: [
                            Image.asset(
                              _showAddress
                                  ? "assets/images/map/icon_map_view_location.png"
                                  : "assets/images/map/icon_map_no_view_location.png",
                              width: 21,
                            ),
                            Container(
                              height: 2,
                            ),
                            const Text("显示位置",
                                style: TextStyle(
                                    fontSize: 7, color: Color(0xFF9DAAB3)))
                          ],
                        ),
                      ),
                      Container(
                        height: 14,
                      ),
                      InkWell(
                        onTap: () {
                          setState(() {
                            //测距初始化
                            _showDistance(0);
                            _measureManager!.clear();
                            _mesure = !_mesure;
                          });
                        },
                        child: Column(
                          children: [
                            Image.asset(
                              _mesure
                                  ? "assets/images/map/icon_map_no_measure.png"
                                  : "assets/images/map/icon_map_measure.png",
                              width: 21,
                            ),
                            Container(
                              height: 2,
                            ),
                            const Text("测量距离",
                                style: TextStyle(
                                    fontSize: 7, color: Color(0xFF9DAAB3)))
                          ],
                        ),
                      )
                    ],
                  ),
                )),
          ])
        : Container();
  }
}
lib/ui/map/travel.dart
@@ -16,6 +16,7 @@
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/ad_util.dart';
import 'package:locations/utils/global.dart';
import 'package:locations/utils/map_util.dart';
import 'package:locations/utils/pageutils.dart';
@@ -49,7 +50,7 @@
  DateTime? endDate;
  int _page = 1;
  int _totalCount = 0;
  List<UserLocationInfo> _locationList = [];
  List<UserLocationInfo>? _locationList = null;
  RefreshController _refreshController =
      RefreshController(initialRefresh: false);
@@ -60,6 +61,11 @@
    endDate ??= DateTime.now();
    startDate ??= DateTime(2000, 1, 1);
    _requestTravel();
  }
  //获取轨迹
@@ -68,36 +74,45 @@
    if (_uid == null) {
      return;
    }
    if (_page > 1 && _totalCount <= _locationList.length) {
    if (_page > 1 &&
        _locationList != null &&
        _totalCount <= _locationList!.length) {
      return;
    }
    Map<String, dynamic>? result = await LocationApiUtil.getTravel(
        _uid!,
        _uid,
        uid,
        startDate!.millisecondsSinceEpoch,
        endDate!.millisecondsSinceEpoch,
        _page);
    if (result!["code"] == 0) {
      _locationList ??= [];
      _totalCount = result["data"]["count"] as int;
      List<dynamic> list = result!["data"]["list"];
      List<dynamic> list = result["data"]["list"];
      List<UserLocationInfo> ls = [];
      list.forEach((element) {
        ls.add(UserLocationInfo.fromJson(element));
      });
      if (_page == 1) {
        _locationList.clear();
        if (_locationList != null) {
          _locationList!.clear();
        }
      }
      //还有更多
      if (_totalCount > _locationList.length) {
      if (_totalCount > _locationList!.length) {
        _page += 1;
      }
      setState(() {
        _locationList.addAll(ls);
        _locationList!.addAll(ls);
      });
    } else {
      ToastUtil.toast(result!["msg"]);
      ToastUtil.toast(result["msg"]);
    }
  }
  void _onRefresh() async {
@@ -162,6 +177,7 @@
                                    showDatePicker(startDate, (date) {
                                      setState(() {
                                        startDate = date;
                                        _onRefresh();
                                      });
                                    });
                                  },
@@ -192,6 +208,7 @@
                                    showDatePicker(endDate, (date) {
                                      setState(() {
                                        endDate = date;
                                        _onRefresh();
                                      });
                                    });
                                  },
@@ -244,9 +261,21 @@
                            onLoading: _onLoading,
                            controller: _refreshController,
                            child: ListView.builder(
                              itemCount: _locationList.length,
                              itemCount: _locationList == null
                                  ? 0
                                  : (_locationList!.isEmpty
                                      ? 1
                                      : _locationList!.length),
                              itemBuilder: (BuildContext context, int index) {
                                return getListViewItem(index);
                                if (_locationList!.isEmpty) {
                                  return
                                       Container(
                                         padding: EdgeInsets.only(top: 50),
                                          alignment: Alignment.center,
                                          child: const Text("暂无数据"));
                                } else {
                                  return getListViewItem(index);
                                }
                              },
                              padding: const EdgeInsets.fromLTRB(0, 0, 0, 55),
                            )),
@@ -257,12 +286,13 @@
                          right: 0,
                          child: InkWell(
                              onTap: () {
                                if (_locationList.length == 0) {
                                if (_locationList == null ||
                                    _locationList!.isEmpty) {
                                  ToastUtil.toast("暂无轨迹信息");
                                  return;
                                }
                                if (_totalCount > _locationList.length) {
                                if (_totalCount > _locationList!.length) {
                                  ToastUtil.toast("请先下拉获取完整的轨迹信息");
                                  return;
                                }
@@ -270,7 +300,7 @@
                                NavigatorUtil.navigateToNextPage(
                                    context,
                                    MyTravelMapPage(
                                      locationList: _locationList,
                                      locationList: _locationList!,
                                    ),
                                    (data) {});
                              },
@@ -337,7 +367,7 @@
  }
  Widget getListViewItem(int index) {
    var location = _locationList[index];
    var location = _locationList![index];
    return Container(
      padding: const EdgeInsets.fromLTRB(18, 18, 18, 9),
@@ -470,6 +500,11 @@
      ..addListener(() {
        setState(() {});
      });
    Timer(Duration(seconds: 2), (){
      //插屏广告
      CSJAdUtil.loadInterstitial("947240112");
    });
  }
  //获取地图视图
@@ -489,20 +524,21 @@
          MapUtil.drawLine(points, ColorConstant.theme, _mapController)
              .then((value) {
            //画起始点与结束点
            MapMarkerUtil.addTravelStartMarker(_mapController, points[0]!)
            MapMarkerUtil.addTravelStartMarker(_mapController, points[0])
                .then((value) {});
            MapMarkerUtil.addTravelEndMarker(
                    _mapController, points[points.length - 1]!)
                    _mapController, points[points.length - 1])
                .then((value) {})
                .then((value) {});
            //画文字
            if (points.length > 1) {
              MapMarkerUtil.addText(_mapController, points[0], "华悦中心A座");
              MapMarkerUtil.addText(_mapController, points[0],
                  locationList[0].location!.addressDetail!);
            }
            MapMarkerUtil.addText(
                    _mapController, points[points.length - 1], "华悦中心B座",
            MapMarkerUtil.addText(_mapController, points[points.length - 1],
                    locationList[points.length - 1].location!.addressDetail!,
                    bgColor: const Color(0xAAFF1F35))
                .then((value) {
              cancap = true;
lib/ui/mine/add_location_person.dart
@@ -136,7 +136,7 @@
    print("result:$result");
    _refreshController.refreshCompleted();
    if (result!["code"] == 0) {
      List<dynamic> list = result!["data"]["list"];
      List<dynamic> list = result["data"]["list"];
      List<LocationUserModel> users = [];
      list.forEach((element) {
        users.add(LocationUserModel.fromJson(element));
@@ -145,7 +145,7 @@
        userList = users;
      });
    } else {
      ToastUtil.toast(result!["msg"]);
      ToastUtil.toast(result["msg"]);
    }
  }
@@ -291,7 +291,7 @@
                                        if (value!["code"] == 0) {
                                          ToastUtil.toast("删除成功");
                                        } else {
                                          ToastUtil.toast(value!["msg"]);
                                          ToastUtil.toast(value["msg"]);
                                        }
                                      });
                                    });
@@ -361,8 +361,8 @@
    _onRefresh();
  }
  Widget _getStatus(LocationInviteStatus? status) {
    switch (status) {
  Widget _getStatus(LocationUserModel user) {
    switch (user.status) {
      case LocationInviteStatus.sentInvite:
        return const Text(
          "等待同意",
@@ -371,7 +371,9 @@
      case LocationInviteStatus.agree:
        return InkWell(
            onTap: () {},
            onTap: () {
              Navigator.of(context).pop(user);
            },
            child: Container(
              padding: const EdgeInsets.only(left: 10, right: 10),
              height: 25,
@@ -463,7 +465,7 @@
                      ),
                    ],
                  )),
                  _getStatus(user.status)
                  _getStatus(user)
                ],
              )),
          Container(
@@ -639,7 +641,7 @@
    print("返回结果:$selectUserType");
    if (selectUserType != null) {
      setState(() {
        selectedUserType = selectUserType! as LocationUserType?;
        selectedUserType = selectUserType as LocationUserType?;
        print("赋值结果:$selectedUserType");
      });
    }
@@ -773,7 +775,7 @@
                                ToastUtil.toast("修改成功");
                                Navigator.of(context).pop();
                              } else {
                                ToastUtil.toast(value!["msg"]);
                                ToastUtil.toast(value["msg"]);
                              }
                            });
                          } else {
@@ -788,7 +790,7 @@
                                ToastUtil.toast("添加成功");
                                Navigator.of(context).pop();
                              } else {
                                ToastUtil.toast(value!["msg"]);
                                ToastUtil.toast(value["msg"]);
                              }
                            });
                          }
lib/ui/mine/advice.dart
@@ -6,7 +6,7 @@
import 'package:locations/utils/pageutils.dart';
import 'advice_submit.dart';
import 'package:launch_review/launch_review.dart';
void main() {
  runApp(MyApp());
}
@@ -84,12 +84,15 @@
                },
              ),
              getItemView(
                bgColor: Color(0xFF29D5FF),
                shadowColor: Color(0x4D0E96FF),
                bgColor: const Color(0xFF29D5FF),
                shadowColor: const Color(0x4D0E96FF),
                title: "给鼓励",
                subTitle: "您的鼓励是我们前进的动力",
                icon: "assets/images/advice/icon_advice_like.png",
                onTap: () {},
                onTap: () {
                  LaunchReview.launch();
                },
              ),
            ],
          ),
lib/ui/mine/advice_submit.dart
@@ -2,8 +2,10 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:locations/api/http.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_utils.dart';
void main() {
  runApp(MyApp());
@@ -43,6 +45,8 @@
    with SingleTickerProviderStateMixin {
  List<String> questions = ["账号问题", "定位问题", "其他问题"];
  final TextEditingController _contentController = TextEditingController();
  @override
  void initState() {
    super.initState();
@@ -65,6 +69,7 @@
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        resizeToAvoidBottomInset: false,
        backgroundColor: Color(0xFFFFFFFF),
        body: Container(
          child: Flex(
@@ -72,9 +77,9 @@
            children: [
              TopNavBar(title: "提意见"),
              Container(
                padding: EdgeInsets.fromLTRB(17.5, 15, 17.5, 15),
                padding: const EdgeInsets.fromLTRB(17.5, 15, 17.5, 15),
                decoration: BoxDecoration(
                    color: Color(0xFFFAFAFA),
                    color: const Color(0xFFFAFAFA),
                    border: Border.all(color: Color(0xFFDBDBDB))),
                child: Flex(
                  direction: Axis.vertical,
@@ -86,9 +91,10 @@
                    ),
                    Container(
                      child: TextField(
                        controller: _contentController,
                        maxLines: 9,
                        maxLength: 200,
                        decoration: InputDecoration(
                        decoration: const InputDecoration(
                            focusedBorder: InputBorder.none,
                            border: InputBorder.none,
                            counterStyle: TextStyle(color: Color(0xFF666666)),
@@ -108,16 +114,34 @@
                margin: EdgeInsets.fromLTRB(18, 50, 18, 0),
                child: InkWell(
                  onTap: () {
                    print("提交反馈");
                    var content = _contentController.text;
                    if (StringUtil.isNullOrEmpty(content)) {
                      ToastUtil.toast("请填写问题");
                      return;
                    }
                    var type;
                    if (checkIndex > -1) {
                      type = questions[checkIndex];
                    }
                    FeedBackApiUtil.advice(context, type, content).then((value) {
                      if (value == null) {
                        return;
                      }
                      if (value["code"] == 0) {
                        Navigator.of(context).pop();
                      } else {
                        ToastUtil.toast(value["msg"]);
                      }
                    });
                  },
                  child: Container(
                    height: 42.5,
                    alignment: Alignment.center,
                    decoration: BoxDecoration(
                    decoration: const BoxDecoration(
                        color: Color(0xFF0E96FF),
                        borderRadius: BorderRadius.all(Radius.circular(10))),
                    child: Text(
                    child: const Text(
                      "提交反馈",
                      style: TextStyle(color: Colors.white, fontSize: 18),
                    ),
lib/ui/mine/login.dart
@@ -10,8 +10,10 @@
import 'package:html/dom.dart' as dom;
import 'package:locations/api/http.dart';
import 'package:locations/model/user/user_info.dart';
import 'package:locations/ui/common/browser.dart';
import 'package:locations/ui/widget/button.dart';
import 'package:locations/utils/event_bus_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/push_util.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_constant.dart';
@@ -104,14 +106,14 @@
          await messageChannel.send({"method": "closeLogin"});
          // Fluttertoast.showToast(msg: "token:" + value["token"]);
          Map<String, dynamic>? resultValue =
              await UserApiUtil.login(context, "", "", value["token"]);
          await UserApiUtil.login(context, "", "", value["token"]);
          if (resultValue == null) return;
          if (resultValue!["code"] == 0) {
          if (resultValue["code"] == 0) {
            UserInfo user = UserInfo.fromJson(resultValue["data"]);
            _loginSuccess(user);
          } else {
            Fluttertoast.showToast(msg: value!["msg"]);
            Fluttertoast.showToast(msg: value["msg"]);
          }
        } else if (value["code"] == 700000) {
          //Fluttertoast.showToast(msg: "取消登录了");
@@ -131,7 +133,7 @@
    Map value = await UserUtil.loginQQ();
  }
  void _loginSuccess(UserInfo user){
  void _loginSuccess(UserInfo user) {
    UserUtil.setUserInfo(user).then((value) {
      print("登录成功");
      eventBus.fire(LoginEventBus(true));
@@ -159,7 +161,7 @@
              blurRadius: 2.0,
              offset: Offset(0.0, 5.0), //阴影y轴偏移量
              spreadRadius: 1 //阴影扩散程度
              )
          )
        ]);
  }
@@ -202,7 +204,7 @@
                                ),
                                Row(
                                  mainAxisAlignment:
                                      MainAxisAlignment.spaceAround,
                                  MainAxisAlignment.spaceAround,
                                  children: [
                                    // getThirdLoginItem("微信登录",
                                    //     "assets/images/login/ic_login_wx.png"),
@@ -210,9 +212,9 @@
                                    //     "assets/images/login/ic_login_qq.png"),
                                    oneKeyLogin
                                        ? getThirdLoginItem("手机号登录",
                                            "assets/images/login/ic_login_phone.png")
                                        "assets/images/login/ic_login_phone.png")
                                        : getThirdLoginItem("一键登录",
                                            "assets/images/login/ic_login_onekey.png"),
                                        "assets/images/login/ic_login_onekey.png"),
                                  ],
                                )
                              ])))),
@@ -233,21 +235,25 @@
                    child: Container(
                        child: Html(
                            data:
                                "<p>登录即表明同意<a href='${Constant.PROTOCOL_URL}'>&nbsp;用户协议&nbsp;</a>和<a href='${Constant.PRIVACY_URL}'>&nbsp;隐私政策&nbsp;</a></p>",
                            "<p>登录即表明同意&nbsp;<a href='${Constant
                                .PROTOCOL_URL}'>用户协议</a>&nbsp;和&nbsp;<a href='${Constant
                                .PRIVACY_URL}'>隐私政策</a>&nbsp;</p>",
                            style: {
                              "a": Style(
                                  textDecoration: TextDecoration.none,
                                  color: ColorConstant.theme),
                              "p": Style(
                                  textDecoration: TextDecoration.none,
                                  color: Color(0xFF999999),
                                  color: const Color(0xFF999999),
                                  fontSize: FontSize.medium)
                            },
                            onLinkTap: (String? url,
                                RenderContext context,
                                RenderContext context1,
                                Map<String, String> attributes,
                                dom.Element? element) {
                              print(url);
                              NavigatorUtil.navigateToNextPage(
                                  context, BrowserPage(title: element!.innerHtml, url: url!,), (
                                  data) {});
                            }))),
              ])
            ]),
@@ -270,178 +276,178 @@
  Widget getLoginContent() {
    return oneKeyLogin
        ? Container(
            child: Column(children: [
            MyFillButton(
              "本机号码一键登录",
              10,
              height: 45,
              fontSize: 17,
              onClick: () {
                aliyunOneKeyLogin();
              },
            )
          ]))
        child: Column(children: [
          MyFillButton(
            "本机号码一键登录",
            10,
            height: 45,
            fontSize: 17,
            onClick: () {
              aliyunOneKeyLogin();
            },
          )
        ]))
        : Column(
            crossAxisAlignment: CrossAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.fromLTRB(20, 0, 5, 0),
          decoration: BoxDecoration(
              color: const Color(0xFFF5F5F5),
              borderRadius: BorderRadius.circular(10)),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Container(
                alignment: Alignment.centerLeft,
                padding: EdgeInsets.fromLTRB(20, 0, 5, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/images/login/icon_phone.png",
                      width: 14,
                      height: 20,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                      style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                      onChanged: (value) {
                        setState(() {
                          phone = value;
                        });
                      },
                      textAlign: TextAlign.start,
                      keyboardType: TextInputType.phone,
                      controller: phoneController,
                      maxLength: 11,
                      decoration: InputDecoration(
                        counterText: "",
                        hintText: "请输入手机号",
                        hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                        contentPadding: EdgeInsets.only(bottom: 3),
                        border: InputBorder.none,
                        focusedBorder: InputBorder.none,
                      ),
                    )),
                  ],
                ),
              Image.asset(
                "assets/images/login/icon_phone.png",
                width: 14,
                height: 20,
              ),
              Container(height: 10),
              Container(
                alignment: Alignment.centerLeft,
                padding: EdgeInsets.fromLTRB(20, 0, 7, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/images/login/icon_code.png",
                      width: 16,
              Container(width: 14),
              Expanded(
                  child: TextField(
                    style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                    onChanged: (value) {
                      setState(() {
                        phone = value;
                      });
                    },
                    textAlign: TextAlign.start,
                    keyboardType: TextInputType.phone,
                    controller: phoneController,
                    maxLength: 11,
                    decoration: InputDecoration(
                      counterText: "",
                      hintText: "请输入手机号",
                      hintStyle:
                      TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                      contentPadding: EdgeInsets.only(bottom: 3),
                      border: InputBorder.none,
                      focusedBorder: InputBorder.none,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                      style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                      onChanged: (value) {
                        setState(() {
                          phone = value;
                        });
                      },
                      textAlign: TextAlign.start,
                      keyboardType: TextInputType.phone,
                      controller: codeController,
                      maxLength: 8,
                      decoration: const InputDecoration(
                        counterText: "",
                        hintText: "请输入验证码",
                        hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                        contentPadding: EdgeInsets.only(bottom: 3),
                        border: InputBorder.none,
                        focusedBorder: InputBorder.none,
                      ),
                    )),
                    MyFillButton(
                      (reSendSMSTimeLeft! > 0
                              ? (reSendSMSTimeLeft.toString() + "S重新获取")
                              : reSendSMSTimeLeft == -1
                                  ? "获取验证码"
                                  : "重新获取")
                          .toString(),
                      10,
                      height: 34,
                      padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
                      enable: (reSendSMSTimeLeft! < 1 &&
                          StringUtil.isMobile(phoneController!.value.text)),
                      onClick: () {
                        if (!(reSendSMSTimeLeft! < 1 &&
                            StringUtil.isMobile(phoneController!.value.text))) {
                          return;
                        }
                        //发送验证码
                        UserApiUtil.sendSMS(
                                context, phoneController!.value.text)
                            .then((value) {
                          if (value != null && value["code"] == 0) {
                            setState(() {
                              reSendSMSTimeLeft = 60;
                              //倒计时
                              timer = Timer.periodic(const Duration(seconds: 1),
                                  (timer) {
                                if (reSendSMSTimeLeft! > 0) {
                                  setState(() {
                                    reSendSMSTimeLeft = reSendSMSTimeLeft! - 1;
                                  });
                                } else {
                                  timer.cancel();
                                }
                              });
                            });
                          }
                        });
                      },
                    ),
                  ],
                ),
                  )),
            ],
          ),
        ),
        Container(height: 10),
        Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.fromLTRB(20, 0, 7, 0),
          decoration: BoxDecoration(
              color: const Color(0xFFF5F5F5),
              borderRadius: BorderRadius.circular(10)),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Image.asset(
                "assets/images/login/icon_code.png",
                width: 16,
              ),
              Container(height: 10),
              const Text("未注册的手机号注册后系统会自动创建账户",
                  style: TextStyle(color: Color(0xFFA0A0A0), fontSize: 12)),
              Container(height: 20),
              Container(width: 14),
              Expanded(
                  child: TextField(
                    style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                    onChanged: (value) {
                      setState(() {
                        phone = value;
                      });
                    },
                    textAlign: TextAlign.start,
                    keyboardType: TextInputType.phone,
                    controller: codeController,
                    maxLength: 8,
                    decoration: const InputDecoration(
                      counterText: "",
                      hintText: "请输入验证码",
                      hintStyle:
                      TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                      contentPadding: EdgeInsets.only(bottom: 3),
                      border: InputBorder.none,
                      focusedBorder: InputBorder.none,
                    ),
                  )),
              MyFillButton(
                "登录",
                (reSendSMSTimeLeft! > 0
                    ? (reSendSMSTimeLeft.toString() + "S重新获取")
                    : reSendSMSTimeLeft == -1
                    ? "获取验证码"
                    : "重新获取")
                    .toString(),
                10,
                height: 45,
                color: const Color(0xFFFF2B4B),
                fontSize: 17,
                enable: StringUtil.isMobile(phoneController!.value.text) &&
                    codeController!.value.text.length >= 4,
                height: 34,
                padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
                enable: (reSendSMSTimeLeft! < 1 &&
                    StringUtil.isMobile(phoneController!.value.text)),
                onClick: () {
                  if (!(StringUtil.isMobile(phoneController!.value.text) &&
                      codeController!.value.text.length >= 4)) {
                  if (!(reSendSMSTimeLeft! < 1 &&
                      StringUtil.isMobile(phoneController!.value.text))) {
                    return;
                  }
                  if (!checked) {
                    Fluttertoast.showToast(msg: "请同意用户协议与隐私政策");
                    return;
                  }
                  UserApiUtil.login(context, phoneController!.value.text,
                          codeController!.value.text, "")
                  //发送验证码
                  UserApiUtil.sendSMS(
                      context, phoneController!.value.text)
                      .then((value) {
                        print("结果: $value");
                    if (value!["code"] == 0) {
                      UserInfo user = UserInfo.fromJson(value["data"]);
                      _loginSuccess(user);
                    } else {
                      Fluttertoast.showToast(msg: value!["msg"]);
                    if (value != null && value["code"] == 0) {
                      setState(() {
                        reSendSMSTimeLeft = 60;
                        //倒计时
                        timer = Timer.periodic(const Duration(seconds: 1),
                                (timer) {
                              if (reSendSMSTimeLeft! > 0) {
                                setState(() {
                                  reSendSMSTimeLeft = reSendSMSTimeLeft! - 1;
                                });
                              } else {
                                timer.cancel();
                              }
                            });
                      });
                    }
                  });
                },
              ),
            ],
          );
          ),
        ),
        Container(height: 10),
        const Text("未注册的手机号注册后系统会自动创建账户",
            style: TextStyle(color: Color(0xFFA0A0A0), fontSize: 12)),
        Container(height: 20),
        MyFillButton(
          "登录",
          10,
          height: 45,
          color: const Color(0xFFFF2B4B),
          fontSize: 17,
          enable: StringUtil.isMobile(phoneController!.value.text) &&
              codeController!.value.text.length >= 4,
          onClick: () {
            if (!(StringUtil.isMobile(phoneController!.value.text) &&
                codeController!.value.text.length >= 4)) {
              return;
            }
            if (!checked) {
              Fluttertoast.showToast(msg: "请同意用户协议与隐私政策");
              return;
            }
            UserApiUtil.login(context, phoneController!.value.text,
                codeController!.value.text, "")
                .then((value) {
              print("结果: $value");
              if (value!["code"] == 0) {
                UserInfo user = UserInfo.fromJson(value["data"]);
                _loginSuccess(user);
              } else {
                Fluttertoast.showToast(msg: value["msg"]);
              }
            });
          },
        ),
      ],
    );
  }
  Widget getThirdLoginItem(String name, String iconAsset) {
lib/ui/mine/permission.dart
@@ -3,7 +3,8 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/utils/permission_util.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
  runApp(MyApp());
@@ -40,39 +41,78 @@
class _PermissionPageState extends State<PermissionPage>
    with SingleTickerProviderStateMixin {
  bool location = false;
  bool _location = false;
  bool _phone=false;
  @override
  void initState() {
    super.initState();
    _getPermissionState();
  }
  _getPermissionState() async {
    PermissionStatus status = await Permission.locationAlways.status;
    setState(() {
      if (status == PermissionStatus.granted) {
        _location = true;
      } else {
        _location = false;
      }
    });
    status = await Permission.phone.status;
    setState(() {
      if (status == PermissionStatus.granted) {
        _phone = true;
      } else {
        _phone = false;
      }
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Color(0xFFF5F5F5),
        body: Container(
          child: Flex(
            direction: Axis.vertical,
            children: [
              TopNavBar(title: "权限设置"),
              Container(
                padding: EdgeInsets.fromLTRB(20, 22, 20, 24),
                child: Text(
                padding: const EdgeInsets.fromLTRB(20, 22, 20, 24),
                child: const Text(
                  "受手机运行机制以及手机系统的影响,后台定位功能可能会出现不稳定,需要按照下方进行设置,以确保定位信息实时稳定。",
                  style: TextStyle(color: Color(0xFF666666), fontSize: 12),
                ),
              ),
              getItemView(
                title: '位置权限',
                subTitle: '关闭后,21:00-09:00不接受任何推送\n获取轨迹以及实时位置信息',
                subTitle: '定位轨迹必备权限\n获取轨迹以及实时位置信息',
                notify: '建议开启',
                checked: location,
                checked: _location,
                changed: (bool value) {
                  setState(() {
                    location = value;
                  });
                  if (value) {
                    PermissionUtil.openPermission(Permission.locationAlways,
                            force: true)
                        .then((value) {
                      if (value == PermissionStatus.granted) {
                        setState(() {
                          _location = true;
                        });
                      }
                    });
                  } else {
                    PermissionUtil.closePermission(Permission.locationAlways).then((value) {
                      if (value == PermissionStatus.granted) {
                        setState(() {
                          _location = true;
                        });
                      }
                    });
                  }
                },
              ),
              getItemView(
@@ -80,20 +120,42 @@
                subTitle: '判断设备唯一性,确保账号安全必备权限\n获取设备MAC信息',
                notify: '建议开启',
                checked: true,
                changed: (bool value) {},
                changed: (bool value) {
                  if (value) {
                    PermissionUtil.openPermission(Permission.phone,
                        force: true)
                        .then((value) {
                      if (value == PermissionStatus.granted) {
                        setState(() {
                          _phone = true;
                        });
                      }
                    });
                  } else {
                    PermissionUtil.closePermission(Permission.phone).then((value) {
                      if (value == PermissionStatus.granted) {
                        setState(() {
                          _phone = true;
                        });
                      }
                    });
                  }
                },
              ),
              getItemView(
                title: '省电模式',
                subTitle: '此模式下本软件运行可能会收到限制\n定位期间尽量确保电量充足',
                notify: '建议勿开启',
                checked: true,
                checked: false,
                changed: (bool value) {},
              ),
              getItemView(
                title: '电池优化白名单',
                subTitle: '此模式下本软件运行可能会收到限制\n定位期间尽量确保电量充足',
                notify: '快速设置',
                checked: true,
                checked: false,
                changed: (bool value) {},
              ),
              getItemView(
lib/ui/mine/settings.dart
@@ -3,17 +3,24 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:locations/api/http.dart';
import 'package:locations/ui/common/browser.dart';
import 'package:locations/ui/widget/dialog.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/utils/cache_util.dart';
import 'package:locations/utils/config_util.dart';
import 'package:locations/utils/event_bus_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/push_util.dart';
import 'package:locations/utils/setting_util.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'package:package_info/package_info.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
@@ -49,8 +56,10 @@
    with SingleTickerProviderStateMixin {
  bool msg = false;
  bool ad = false;
  bool login = false;
  String cacheSize = "0B";
  String version = "";
  @override
  void initState() {
@@ -59,6 +68,45 @@
      setState(() {
        login = value;
      });
    });
    _getCacheSize();
    _getVersion();
    SettingUtil.isEnablePush().then((value) {
      setState(() {
        msg = value;
      });
    });
    SettingUtil.isEnableRecommendAd().then((value) {
      ad = value;
    });
  }
  ///获取缓存大小
  void _getCacheSize() async {
    int byteSize = await CacheUtil.total();
    String? desc;
    if (byteSize < 1000) {
      desc = byteSize.toString() + "KB";
    } else if (byteSize < 1000 * 1000) {
      desc = (byteSize / 1000).toStringAsFixed(0) + "KB";
    } else if (byteSize < 1000 * 1000 * 1000) {
      desc = (byteSize / (1000 * 1000)).toStringAsFixed(1) + "MB";
    } else {
      desc = (byteSize / (1000 * 1000 * 1000)).toStringAsFixed(1) + "GB";
    }
    setState(() {
      cacheSize = desc!;
    });
  }
  void _getVersion() async {
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    setState(() {
      version = packageInfo.version;
    });
  }
@@ -92,9 +140,10 @@
                marginBottom: 1,
                checked: msg,
                changed: (bool value) {
                  print(value);
                  setState(() {
                    msg = value;
                  SettingUtil.setPush(value).then((value1) {
                    setState(() {
                      msg = value;
                    });
                  });
                }),
            getBigItemView(
@@ -104,40 +153,64 @@
                marginBottom: 16,
                checked: ad,
                changed: (bool value) {
                  print(value);
                  setState(() {
                    ad = value;
                  SettingUtil.setRecommendAd(value).then((value1) {
                    setState(() {
                      ad = value;
                    });
                  });
                }),
            getCommonItemView(
                title: "清理缓存",
                content: "551.4MB",
                content: cacheSize,
                onClick: () {
                  print("清理缓存");
                  DialogUtil.showDialog(
                      context,
                      NotifyDialog("温馨提示", "是否清楚缓存?", () {}, () {
                        CacheUtil.clear().then((value) {
                          ToastUtil.toast("缓存清除成功");
                          _getCacheSize();
                        });
                      }));
                }),
            getCommonItemView(
                title: "检查更新",
                content: "版本号2.0.0",
                content: "版本号:$version",
                onClick: () {
                  print("检查更新");
                  ToastUtil.toast("已经是最新版本");
                }),
            getCommonItemView(
                title: "账户注销",
                content: "",
                onClick: () {
                  print("账户注销");
                  ConfigUtil.getConfig(ConfigKey.unRegister).then((value) {
                    if (!StringUtil.isNullOrEmpty(value)) {
                      NavigatorUtil.navigateToNextPage(context,
                          BrowserPage(title: "账户注销", url: value!), (data) {});
                    }
                  });
                }),
            getCommonItemView(
                title: "隐私投诉",
                content: "",
                onClick: () {
                  print("隐私投诉");
                  ConfigUtil.getConfig(ConfigKey.privacyComplain).then((value) {
                    if (!StringUtil.isNullOrEmpty(value)) {
                      NavigatorUtil.navigateToNextPage(context,
                          BrowserPage(title: "隐私投诉", url: value!), (data) {});
                    }
                  });
                }),
            getCommonItemView(
                title: "第三方SDK列表",
                content: "",
                onClick: () {
                  print("第三方SDK列表");
                  ConfigUtil.getConfig(ConfigKey.sdkList).then((value) {
                    if (!StringUtil.isNullOrEmpty(value)) {
                      NavigatorUtil.navigateToNextPage(
                          context, BrowserPage(title: "第三方SDK列表", url: value!), (data) {});
                    }
                  });
                }),
            Expanded(
              child: Container(),
lib/ui/sos/sos.dart
@@ -7,6 +7,7 @@
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/map/map.dart';
import 'package:locations/ui/sos/sos_contacts.dart';
import 'package:locations/ui/widget/base_ui.dart';
import 'package:locations/ui/widget/button.dart';
@@ -83,7 +84,7 @@
    Map<String, dynamic>? result =
        await SOSApiUtil.listSOSRecord(context, _page);
    if (result!["code"] == 0) {
      Map<String, dynamic> data = result!["data"];
      Map<String, dynamic> data = result["data"];
      List<dynamic> list = data["list"];
      List<SosRecordModel> recordList = [];
      for (var element in list) {
@@ -100,7 +101,7 @@
      _totalCount = data["count"];
    } else {
      ToastUtil.toast(result!["msg"]);
      ToastUtil.toast(result["msg"]);
    }
  }
@@ -158,7 +159,7 @@
                                        sos = true;
                                      });
                                    } else {
                                      ToastUtil.toast(value!["msg"]);
                                      ToastUtil.toast(value["msg"]);
                                    }
                                  });
                                } else if (LocationState.permission_denied ==
@@ -168,7 +169,7 @@
                                  ToastUtil.toast("定位失败");
                                }
                              });
                            }else{
                            } else {
                              setState(() {
                                sos = false;
                              });
@@ -273,6 +274,7 @@
                            Container(
                              width: 12.5,
                            ),
                            _totalCount>0?
                            InkWell(
                              onTap: () {
                                DialogUtil.showDialog(
@@ -280,15 +282,14 @@
                                    NotifyDialog(
                                      "温馨提示",
                                      "确定要清空记录吗?",
                                      () {
                                      },
                                      () {},
                                      () {
                                        SOSApiUtil.clearSOSRecord(context)
                                            .then((value) {
                                          if (value!["code"] == 0) {
                                            _onRefresh();
                                          } else {
                                            ToastUtil.toast(value!["msg"]);
                                            ToastUtil.toast(value["msg"]);
                                          }
                                        });
                                      },
@@ -300,7 +301,7 @@
                                style: TextStyle(
                                    color: Color(0xFF999999), fontSize: 12),
                              ),
                            ),
                            ):Container(),
                            Expanded(
                              child: SmartRefresher(
                                  enablePullDown: true,
@@ -394,7 +395,7 @@
                        controller!.reverse();
                      }
                    } else {
                      ToastUtil.toast(value!["msg"]);
                      ToastUtil.toast(value["msg"]);
                    }
                  });
                }))
@@ -469,32 +470,40 @@
          Container(
            height: 6,
          ),
          Row(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Image.asset("assets/images/main/icon_location_position_name.png",
                  height: 16),
              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,
              ),
              Image.asset(
                "assets/images/main/icon_location_position_more.png",
                height: 10,
              ),
            ],
          )
          InkWell(
              onTap: () {
                NavigatorUtil.navigateToNextPage(context, MapPage("位置",share: false,location:record.location,uid: record.targetUid,), (data) { });
              },
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Image.asset(
                      "assets/images/main/icon_location_position_name.png",
                      height: 16),
                  Container(
                    width: 6,
                  ),
                  Expanded(
                      child: FittedBox(
                          fit: BoxFit.fitWidth,
                          child: Text(
                            record.location!.address! +
                                record.location!.addressDetail!,
                            softWrap: false,
                            overflow: TextOverflow.ellipsis,
                            style: const TextStyle(
                                fontSize: 12, color: Color(0xFF999999)),
                          ))),
                  Container(
                    width: 6,
                  ),
                  Image.asset(
                    "assets/images/main/icon_location_position_more.png",
                    height: 10,
                  ),
                ],
              ))
        ],
      ),
    );
lib/ui/sos/sos_contacts.dart
@@ -87,7 +87,7 @@
        contactList = clist;
      });
    } else {
      ToastUtil.toast(result!["msg"]);
      ToastUtil.toast(result["msg"]);
    }
  }
@@ -208,7 +208,7 @@
                                      ToastUtil.toast("删除成功");
                                      _onRefresh();
                                    } else {
                                      ToastUtil.toast(value!["msg"]);
                                      ToastUtil.toast(value["msg"]);
                                    }
                                  });
                                });
@@ -272,7 +272,7 @@
                    ToastUtil.toast("修改成功");
                    _onRefresh();
                  } else {
                    ToastUtil.toast(value!["msg"]);
                    ToastUtil.toast(value["msg"]);
                  }
                });
              },
@@ -321,7 +321,7 @@
                  Container(
                    height: 5,
                  ),
                  Text(contact!.phone!,
                  Text(contact.phone!,
                      style: const TextStyle(
                          color: Color(0xFF9DAAB3), fontSize: 15))
                ],
lib/ui/widget/capture.dart
@@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui';
@@ -5,7 +6,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
///对widget截图
@@ -23,30 +23,25 @@
  Widget build(BuildContext context) {
    return RepaintBoundary(key: rootWidgetKey, child: widget);
  }
}
class CaptureController {
  GlobalKey? _globalKey;
  setGlobalKey(GlobalKey globalKey) {
    _globalKey = globalKey;
  }
  Future<File?> capturePng(String path) async {
  Future<String> capturePng() async {
    try {
      RenderRepaintBoundary? boundary = _globalKey!.currentContext!
          .findRenderObject() as RenderRepaintBoundary;
      var image = await boundary.toImage(pixelRatio: 3.0);
      ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
      Uint8List pngBytes = byteData!.buffer.asUint8List();
      File(path).writeAsBytesSync(pngBytes);
      return File(path);
      String str = base64.encode(pngBytes);
      return str;
    } catch (e) {}
    return null;
    return "";
  }
}
lib/ui/widget/map_marker.dart
@@ -96,8 +96,9 @@
//用户(带头像)位置marker
class PersonLocationMarker extends StatelessWidget {
  final Image portrait;
  final Color borderColor;
  PersonLocationMarker(this.portrait);
  PersonLocationMarker(this.portrait,{this.borderColor=ColorConstant.theme});
  @override
  Widget build(BuildContext context) {
@@ -112,7 +113,7 @@
        height: 42 - 3 * 2,
        padding: EdgeInsets.all(2),
        decoration: BoxDecoration(
            color: ColorConstant.theme,
            color: borderColor,
            borderRadius: BorderRadius.circular(40)),
        child: Image.asset(
          "assets/images/mine/icon_mine_default_portrait.png",
lib/ui/widget/nav.dart
@@ -51,7 +51,10 @@
                        Expanded(
                            child: Center(
                                child: Text(
                          title,
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                          style:
                              TextStyle(fontSize: 18, color: textColor),
                        ))),
lib/utils/ad_util.dart
New file
@@ -0,0 +1,388 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_unionad/flutter_unionad.dart';
import 'package:locations/model/common/adinfo_model.dart';
import 'package:locations/utils/config_util.dart';
import 'package:locations/utils/string_util.dart';
import 'package:flutter_tencentad/flutter_tencentad.dart';
//紧急联系人输入框确定事件
typedef OnAdCallback = void Function(bool success, String msg);
class AdUtil {
  static Future init() async {
    await CSJAdUtil.init();
    await GDTAdUtil.init();
  }
  static void showSplashAd(
      double width, double height, OnAdCallback adCallback) async {
    var csjAppId = await ConfigUtil.getConfig(ConfigKey.csjAppId);
    var gdtAppId = await ConfigUtil.getConfig(ConfigKey.gdtAppId);
    if (StringUtil.isNullOrEmpty(csjAppId) &&
        StringUtil.isNullOrEmpty(gdtAppId)) {
      adCallback(false, "appId为空");
      return;
    }
    var adInfo = await getAdInfo(AdPosition.splash);
    if (adInfo == null) {
      adCallback(false, "appId为空");
      return;
    }
    if (adInfo.type == "csj") {
      CSJAdUtil.loadSplash(adInfo.pid!, width, height, (success, msg) {});
    } else {
      GDTAdUtil.loadSplash(adInfo.pid!, (success, msg) {});
    }
    //加载广告
  }
  static Future<AdinfoModel?> getAdInfo(String position) async {
    var result = await ConfigUtil.getConfig(position);
    if (result == null) {
      return null;
    }
    AdinfoModel model = AdinfoModel.fromJson(jsonDecode(result));
    if (StringUtil.isNullOrEmpty(model.type)) {
      return null;
    }
  }
}
class CSJAdUtil {
  static Future init() async {
    var csjAppId = await ConfigUtil.getConfig(ConfigKey.csjAppId);
    //初始化穿山甲
    if (!StringUtil.isNullOrEmpty(csjAppId)) {
      // if (Platform.isAndroid) {
      //   await FlutterUnionad.andridPrivacy(
      //     isCanUseLocation: false,
      //     //是否允许SDK主动使用地理位置信息 true可以获取,false禁止获取。默认为true
      //     lat: 1.0,
      //     //当isCanUseLocation=false时,可传入地理位置信息,穿山甲sdk使用您传入的地理位置信息lat
      //     lon: 1.0,
      //     //当isCanUseLocation=false时,可传入地理位置信息,穿山甲sdk使用您传入的地理位置信息lon
      //     isCanUsePhoneState: false,
      //     //是否允许SDK主动使用手机硬件参数,如:imei
      //     imei: "123",
      //     //当isCanUsePhoneState=false时,可传入imei信息,穿山甲sdk使用您传入的imei信息
      //     isCanUseWifiState: false,
      //     //是否允许SDK主动使用ACCESS_WIFI_STATE权限
      //     isCanUseWriteExternal: false,
      //     //是否允许SDK主动使用WRITE_EXTERNAL_STORAGE权限
      //     oaid: "111", //开发者可以传入oaid
      //   );
      // }
      await FlutterUnionad.register(
          androidAppId: csjAppId!,
          //穿山甲广告 Android appid 必填
          iosAppId: "",
          //穿山甲广告 ios appid 必填
          useTextureView: false,
          //使用TextureView控件播放视频,默认为SurfaceView,当有SurfaceView冲突的场景,可以使用TextureView 选填
          appName: "定位追踪轨迹",
          //appname 必填
          allowShowNotify: true,
          //是否允许sdk展示通知栏提示 选填
          allowShowPageWhenScreenLock: true,
          //是否在锁屏场景支持展示广告落地页 选填
          debug: true,
          //是否显示debug日志
          supportMultiProcess: true,
          //是否支持多进程,true支持 选填
          directDownloadNetworkType: [
            FlutterUnionadNetCode.NETWORK_STATE_4G,
            FlutterUnionadNetCode.NETWORK_STATE_WIFI
          ]); //允许直接下载的网络状态集合 选填
    }
  }
  static Widget? loadSplash(
      String? pid, double width, double height, OnAdCallback adCallback) {
    if (pid == null) {
      adCallback(false, "pid为空");
      return null;
    }
    return FlutterUnionad.splashAdView(
      //是否使用个性化模版  设定widget宽高
      mIsExpress: true,
      //android 开屏广告广告id 必填
      androidCodeId: pid,
      //ios 开屏广告广告id 必填
      iosCodeId: "",
      //是否支持 DeepLink 选填
      supportDeepLink: true,
      // 期望view 宽度 dp 选填 mIsExpress=true必填
      expressViewWidth: width,
      //期望view高度 dp 选填 mIsExpress=true必填
      expressViewHeight: height,
      callBack: FlutterUnionadSplashCallBack(
        onShow: () {
          print("开屏广告显示");
        },
        onClick: () {
          print("开屏广告点击");
        },
        onFail: (error) {
          adCallback(false, error);
        },
        onFinish: () {
          print("开屏广告倒计时结束");
          adCallback(true, "");
        },
        onSkip: () {
          print("开屏广告跳过");
          adCallback(true, "");
        },
        onTimeOut: () {
          print("开屏广告超时");
          adCallback(false, "开屏广告超时");
        },
      ),
    );
  }
  static Widget loadBanner(
      String pid, double width, double height, OnAdCallback callback) {
    return FlutterUnionad.bannerAdView(
      //andrrid banner广告id 必填
      androidCodeId: pid,
      //ios banner广告id 必填
      iosCodeId: "",
      //是否使用个性化模版
      mIsExpress: true,
      //是否支持 DeepLink 选填
      supportDeepLink: true,
      //一次请求广告数量 大于1小于3 必填
      expressAdNum: 3,
      //轮播间隔事件 30-120秒  选填
      expressTime: 30,
      // 期望view 宽度 dp 必填
      expressViewWidth: width,
      //期望view高度 dp 必填
      expressViewHeight: height,
      //广告事件回调 选填
      callBack: FlutterUnionadBannerCallBack(onShow: () {
        print("banner广告加载完成");
      }, onDislike: (message) {
        print("banner不感兴趣 $message");
        callback(false, "banner不感兴趣 $message");
      }, onFail: (error) {
        print("banner广告加载失败 $error");
      }, onClick: () {
        print("banner广告点击");
      }),
    );
  }
  static Widget loadExpress(
      String pid, double width, double height, OnAdCallback callback) {
    return FlutterUnionad.nativeAdView(
      androidCodeId: pid,
      //android 信息流广告id 必填
      iosCodeId: "",
      //ios banner广告id 必填
      supportDeepLink: true,
      //是否支持 DeepLink 选填
      expressViewWidth: width,
      // 期望view 宽度 dp 必填
      expressViewHeight: height,
      //期望view高度 dp 必填
      expressNum: 1,
      mIsExpress: true,
      //一次请求广告数量 大于1小于3 必填
      callBack: FlutterUnionadNativeCallBack(
        onShow: () {
          print("信息流广告显示");
        },
        onFail: (error) {
          print("信息流广告失败 $error");
        },
        onDislike: (message) {
          print("信息流广告不感兴趣 $message");
          callback(false, "信息流广告不感兴趣 $message");
        },
        onClick: () {
          print("信息流广告点击");
        },
      ),
    );
  }
  static loadReward(String pid, OnAdCallback adCallback) async {
    FlutterUnionad.loadRewardVideoAd(
      mIsExpress: true,
      //是否个性化 选填
      androidCodeId: pid,
      //Android 激励视频广告id  必填
      iosCodeId: "",
      //ios 激励视频广告id  必填
      supportDeepLink: true,
      //是否支持 DeepLink 选填
      rewardName: "vip",
      //奖励名称 选填
      rewardAmount: 1,
      //奖励数量 选填
      userID: "",
      //  用户id 选填
      orientation: FlutterUnionadOrientation.VERTICAL,
      //视屏方向 选填
      mediaExtra: null, //扩展参数 选填
    );
    FlutterUnionadStream.initAdStream(
      //激励广告
      flutterUnionadRewardAdCallBack: FlutterUnionadRewardAdCallBack(
        onShow: () {
          print("激励广告显示");
        },
        onClick: () {
          print("激励广告点击");
        },
        onFail: (error) {
          print("激励广告失败 $error");
          adCallback(false, "激励广告失败 $error");
        },
        onClose: () {
          print("激励广告关闭");
        },
        onSkip: () {
          print("激励广告跳过");
        },
        onVerify: (bool isVerify, int rewardAmount, String rewardName,
            int errorCode, String message) {
          adCallback(true, "获取激励成功");
        },
        onReady: () async {
          print("激励广告预加载准备就绪");
          //显示激励广告
          await FlutterUnionad.showRewardVideoAd();
        },
        onUnReady: () {
          print("激励广告预加载未准备就绪");
        },
      ),
    );
  }
  static loadInterstitial(String pid) async {
    FlutterUnionad.loadFullScreenVideoAdInteraction(
      androidCodeId: pid, //android 全屏广告id 必填
      iosCodeId: "", //ios 全屏广告id 必填
      supportDeepLink: true, //是否支持 DeepLink 选填
      orientation: FlutterUnionadOrientation.VERTICAL, //视屏方向 选填
    );
    FlutterUnionadStream.initAdStream(
      // 新模板渲染插屏广告回调
      flutterUnionadNewInteractionCallBack:
          FlutterUnionadNewInteractionCallBack(
        onShow: () {
          print("新模板渲染插屏广告显示");
        },
        onSkip: () {
          print("新模板渲染插屏广告跳过");
        },
        onClick: () {
          print("新模板渲染插屏广告点击");
        },
        onFinish: () {
          print("新模板渲染插屏广告结束");
        },
        onFail: (error) {
          print("新模板渲染插屏广告错误 $error");
        },
        onClose: () {
          print("新模板渲染插屏广告关闭");
        },
        onReady: () async {
          print("新模板渲染插屏广告预加载准备就绪");
          //显示新模板渲染插屏
          await FlutterUnionad.showFullScreenVideoAdInteraction();
        },
        onUnReady: () {
          print("新模板渲染插屏广告预加载未准备就绪");
        },
      ),
    );
  }
}
class GDTAdUtil {
  static Future init() async {
    var gdtAppId = await ConfigUtil.getConfig(ConfigKey.gdtAppId);
    //初始化广点通
    if (!StringUtil.isNullOrEmpty(gdtAppId)) {
      await FlutterTencentad.register(
        androidId: gdtAppId!, //androidId
        iosId: "", //iosId
        debug: true, //是否显示日志log
      );
    }
  }
  static loadSplash(String? pid, OnAdCallback adCallback) {
    if (pid == null) {
      adCallback(false, "pid为空");
      return;
    }
    FlutterTencentad.splashAdView(
      //android广告id
      androidId: pid,
      //ios广告id
      iosId: "",
      ////设置开屏广告从请求到展示所花的最大时长(并不是指广告曝光时长),取值范围为[1500, 5000]ms
      fetchDelay: 3500,
      //广告回调
      callBack: FlutterTencentadSplashCallBack(
        onShow: () {
          print("开屏广告显示");
        },
        onADTick: (time) {
          print("开屏广告倒计时剩余时间 $time");
        },
        onClick: () {
          print("开屏广告点击");
        },
        onClose: () {
          print("开屏广告关闭");
          adCallback(true, "");
        },
        onExpose: () {
          print("开屏广告曝光");
        },
        onFail: (code, message) {
          adCallback(true, message);
        },
      ),
    );
  }
}
class AdPosition {
  //开屏
  static String splash = "splash";
  //首页插屏
  static String homeInterstitial = "homeInterstitial";
  //我的页面原生
  static String mineExpress = "mineExpress";
  //搜索页面原生
  static String searchExpress = "searchExpress";
  //搜索结果banner
  static String searchResultBanner = "searchResultBanner";
  //轨迹分享页插屏
  static String travelShareInterstitial = "travelShareInterstitial";
  //会员激励视频
  static String vipReward = "vipReward";
}
lib/utils/app_util.dart
@@ -1,14 +1,15 @@
import 'dart:io';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:fluwx_no_pay/fluwx_no_pay.dart';
import 'package:flutter_baidu_mapapi_base/src/map/bmf_map_sdk.dart';
import 'package:flutter_baidu_mapapi_base/src/map/bmf_types.dart';
import 'package:flutter/services.dart';
import 'package:fluwx_no_pay/fluwx_no_pay.dart';
import 'package:jpush_flutter/jpush_flutter.dart';
import 'package:locations/utils/ad_util.dart';
import 'package:locations/utils/global.dart';
import 'package:locations/utils/push_util.dart';
import 'package:package_info/package_info.dart';
class AppUtil {
  static JPush _jpush = JPush();
@@ -23,21 +24,29 @@
    //地图
    if (Platform.isIOS) {
      BMFMapSDK.setApiKeyAndCoordType(
          '请输入百度开放平台申请的iOS端API KEY', BMF_COORD_TYPE.BD09LL);
          '请输入百度开放平台申请的iOS端API KEY', BMF_COORD_TYPE.COMMON);
    } else if (Platform.isAndroid) {
// Android 目前不支持接口设置Apikey,
// 请在主工程的Manifest文件里设置,详细配置方法请参考[https://lbs.baidu.com/ 官网][https://lbs.baidu.com/)demo
      BMFMapSDK.setCoordType(BMF_COORD_TYPE.BD09LL);
      BMFMapSDK.setCoordType(BMF_COORD_TYPE.COMMON);
    }
    //初始化广告
    await  AdUtil.init();
    //初始化本地应用
    await _initNativeApp();
    print("初始化完成");
    //初始化极光
    try {
      await PushUtil.init();
      PushUtil.init(context);
    } catch (e) {}
    //初始化应用
    return true;
  }
  //初始化广告
  static _initAd() async {
  }
  //本地应用初始化
@@ -49,6 +58,13 @@
      } on PlatformException catch (e) {
        print(e.toString());
      }
      //填充utdid
      await Global.loadUtdId();
    }
  }
  static Future<int> getVersionCode() async {
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    return int.parse(packageInfo.buildNumber);
  }
}
lib/utils/cache_util.dart
New file
@@ -0,0 +1,57 @@
import 'dart:io';
import 'package:path_provider/path_provider.dart';
/// 缓存管理类
/// ./lib/utils/cache_util.dart
class CacheUtil {
  /// 获取缓存大小
  static Future<int> total() async {
    Directory tempDir = await getTemporaryDirectory();
    if (tempDir == null) return 0;
    int total = await _reduce(tempDir);
    return total;
  }
  /// 清除缓存
  static Future<void> clear() async {
    Directory tempDir = await getTemporaryDirectory();
    if (tempDir == null) return;
    await _delete(tempDir);
  }
  /// 递归缓存目录,计算缓存大小
  static Future<int> _reduce(final FileSystemEntity file) async {
    /// 如果是一个文件,则直接返回文件大小
    if (file is File) {
      int length = await file.length();
      return length;
    }
    /// 如果是目录,则遍历目录并累计大小
    if (file is Directory) {
      final List<FileSystemEntity> children = file.listSync();
      int total = 0;
      if (children != null && children.isNotEmpty)
        for (final FileSystemEntity child in children)
          total += await _reduce(child);
      return total;
    }
    return 0;
  }
  /// 递归删除缓存目录和文件
  static Future<void> _delete(FileSystemEntity file) async {
    if (file is Directory) {
      final List<FileSystemEntity> children = file.listSync();
      for (final FileSystemEntity child in children) {
        await _delete(child);
      }
    } else {
      await file.delete();
    }
  }
}
lib/utils/config_util.dart
New file
@@ -0,0 +1,59 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:locations/api/http.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ConfigUtil {
  ///保存配置信息
  static void saveConfig(Map<String, dynamic> map) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setString("config_value", jsonEncode(map));
  }
  static Future<String?> getConfig(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String? result = prefs.getString("config_value");
    if (result != null) {
      Map<String, dynamic> map = jsonDecode(result);
      return map[key];
    } else {
      //重新请求
      ConfigApiUtil.getConfig().then((value) {
        if (value == null) {
          return;
        }
        if (value["code"] == 0) {
          saveConfig(value["data"]);
        }
      });
    }
    return null;
  }
}
class ConfigKey {
  //客服
  static const String kefu = "kefu";
  //教程
  static const String course = "course";
  //注销
  static const String unRegister = "unRegister";
  //隐私投诉
  static const String privacyComplain = "privacyComplain";
  //会员链接
  static const String vipLink = "vipLink";
  //三方SDK链接
  static const String sdkList = "sdkList";
  //穿山甲APPID
  static const String csjAppId = "csjAppId";
  //广点通APPID
  static const String gdtAppId = "gdtAppId";
}
lib/utils/event_bus_util.dart
@@ -1,7 +1,7 @@
import 'package:locations/model/map/location_model.dart';
import 'package:locations/model/user/user_info.dart';
import 'package:event_bus/event_bus.dart';
import 'package:permission_handler/permission_handler.dart';
EventBus eventBus = EventBus();
class UserLocationInfoEventBus {
@@ -14,3 +14,11 @@
  final bool isLogin;
  LoginEventBus(this.isLogin);
}
class UserLocationPermissionEventBus{
  final PermissionStatus status;
  UserLocationPermissionEventBus(this.status);
}
lib/utils/global.dart
@@ -1,5 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
//全局跳转
final GlobalKey<NavigatorState> navigatorKey =  GlobalKey<NavigatorState>();
class Global {
  static const messageChannel =
      BasicMessageChannel('DeviceUtil', StandardMessageCodec());
  static BMFCoordinate? currentPosition;
  static String? utdId;
  static Future loadUtdId() async {
    String? value =
        await messageChannel.send({"method": "getUtdid"}) as String?;
    utdId = value;
  }
}
lib/utils/jsinterface.dart
@@ -2,6 +2,8 @@
import 'package:flutter/widgets.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:locations/utils/ad_util.dart';
import 'package:locations/utils/user_util.dart';
import 'package:webview_flutter/platform_interface.dart';
import 'package:webview_flutter/webview_flutter.dart';
@@ -17,6 +19,7 @@
    list.add(JavascriptChannel(
        name: 'yestv',
        onMessageReceived: (JavascriptMessage message) {
          print("onMessageReceived");
          var data = jsonDecode(message.message);
          String method = data["method"];
          var params = data["params"];
@@ -31,20 +34,26 @@
            case "getBaseRequestParams":
              getBaseRequestParams(params, _callback);
              break;
            case "showRewardVideoAd":
              showRewardVideoAd(_callback);
              break;
          }
        }));
    return list.toSet();
  }
  callback(String method, String params) {
    _controller!.evaluateJavascript("$method('$params')");
    _controller!.evaluateJavascript("delete $method");
  callback(String method, var params) {
    _controller!.runJavascript("$method('$params')");
    _controller!.runJavascript("delete $method");
  }
  //获取用户ID
  getUid(var params, String? callbackName) {
    if (callbackName != null) {
      callback(callbackName, "购");
      UserUtil.getUid().then((value) {
        callback(callbackName, value != null ? value.toString() : "");
      });
    }
  }
@@ -67,4 +76,20 @@
      callback(callbackName, result);
    }
  }
  //展示激励视频
  showRewardVideoAd( String? callbackName) {
    CSJAdUtil.loadReward("947239184", (success, msg) {
      if (success) {
        //成功
        if (callbackName != null) {
          callback(callbackName, true);
        }
      } else {
        if (callbackName != null) {
          callback(callbackName, false);
        }
      }
    });
  }
}
lib/utils/location_util.dart
@@ -6,6 +6,7 @@
import 'package:flutter_bmflocation/flutter_baidu_location_ios_option.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/map/location_model.dart';
import 'package:locations/utils/permission_util.dart';
import 'package:locations/utils/user_util.dart';
import 'package:permission_handler/permission_handler.dart';
@@ -158,7 +159,7 @@
  ///请求定位权限
  static Future<PermissionStatus> requestLocationPermission() async {
    final status = await Permission.location.request();
    final status = await PermissionUtil.openPermission(Permission.locationAlways);
    return status;
  }
lib/utils/map_util.dart
@@ -1,5 +1,6 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import 'package:flutter_baidu_mapapi_utils/flutter_baidu_mapapi_utils.dart';
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
@@ -12,7 +13,7 @@
  ///画线
  ///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 Future drawLine(List<BMFCoordinate> points, Color lineColor,
  static Future<String> drawLine(List<BMFCoordinate> points, Color lineColor,
      BMFMapController? _mapController,
      {int width = 16}) async {
    List<Color> colors = [lineColor];
@@ -29,6 +30,11 @@
        lineCapType: BMFLineCapType.LineCapRound,
        lineJoinType: BMFLineJoinType.LineJoinRound);
    await _mapController?.addPolyline(colorsPolyline);
    return colorsPolyline.toMap()["id"].toString();
  }
  static Future removeLine(BMFMapController? _mapController, String id) async {
    await _mapController?.removeOverlay(id);
  }
  //获取地图的显示参数
@@ -132,9 +138,9 @@
    _mapController!.removeMarker(marker);
  }
  static removeMarkers(
      BMFMapController? _mapController, List<BMFMarker> markers) {
    _mapController!.removeMarkers(markers);
  static Future<bool> removeMarkers(
      BMFMapController? _mapController, List<BMFMarker> markers) async {
    return await _mapController!.removeMarkers(markers);
  }
  static cleanAllMarkers(BMFMapController? _mapController) {
@@ -145,6 +151,14 @@
      BMFCoordinate pointA, BMFCoordinate pointB) async {
    return await BMFCalculateUtils.getLocationDistance(pointA, pointB);
  }
  static String getDistanceDesc(int distance) {
    if (distance >= 1000) {
      return "${(distance / 1000).toStringAsFixed(1)}米";
    } else {
      return "$distance米";
    }
  }
}
///路径录制管理
@@ -154,14 +168,18 @@
  List<BMFCoordinate> positionList = [];
  List<BMFMarker> startMarkers = [];
  List<String> lineIds = [];
  TravelRECManager(this.mapController);
  //画起始点
  _addStartMarker(BMFCoordinate position) {}
  //画路径
  _drawLine(BMFCoordinate start, BMFCoordinate end) {
    MapUtil.drawLine([start, end], ColorConstant.theme, mapController);
  _drawLine(BMFCoordinate start, BMFCoordinate end) async {
    String lineId = await MapUtil.drawLine(
        [start, end], ColorConstant.theme, mapController);
    lineIds.add(lineId);
  }
  //开始录制
@@ -170,7 +188,7 @@
    positionList.add(currentPosition);
    //画起始点
    MapMarkerUtil.addTravelStartMarker(mapController, currentPosition!)
    MapMarkerUtil.addTravelStartMarker(mapController, currentPosition)
        .then((value) {
      startMarkers = value;
    });
@@ -178,7 +196,7 @@
  //添加位置
  addLocation(BMFCoordinate lication) async {
    if (positionList.length < 1) {
    if (positionList.isEmpty) {
      return;
    }
    //计算距离,如果距离在10米内就不添加到数组
@@ -193,10 +211,109 @@
  }
  //停止录制
  stopREC() {
  stopREC() async {
    //删除marker
    await MapUtil.removeMarkers(mapController, startMarkers);
    //删除线
    lineIds.forEach((element) {
      MapUtil.removeLine(mapController, element);
    });
    //清除数据
    positionList.clear();
    //删除线
    mapController!.cleanAllMarkers();
  }
}
//测距
class MeasureManager {
  BMFMapController? mapController;
  List<BMFCoordinate> positionList = [];
  List<BMFMarker> markers = [];
  String? lineId;
  final List<String> POINTS = [
    "assets/images/map/icon_map_meaure_node_1.png",
    "assets/images/map/icon_map_meaure_node_2.png",
    "assets/images/map/icon_map_meaure_node_3.png"
  ];
  MeasureManager(this.mapController);
  //清除所有覆盖物
  clear() async {
    positionList.clear();
    if (markers.isNotEmpty) {
      await MapUtil.removeMarkers(mapController, markers);
    }
    if (lineId != null) {
      await MapUtil.removeLine(mapController, lineId!);
      lineId = null;
    }
    markers.clear();
  }
  Future<double> _recycle() async {
    print("_recycle");
    //删除覆盖物
    if (markers.isNotEmpty) {
      await MapUtil.removeMarkers(mapController, markers);
    }
    if (lineId != null) {
      try {
        MapUtil.removeLine(mapController, lineId!);
      } catch (e) {}
      lineId = null;
    }
    //添加覆盖物
    if (positionList.length > 1) {
      //绘制线
      lineId = await MapUtil.drawLine(
          positionList, ColorConstant.theme, mapController,
          width: 8);
      print("lineID:$lineId");
    }
    //绘制marker
    await mapController?.addMarkers(markers);
    //计算距离
    double distance = 0;
    if (positionList.length > 1) {
      for (var i = 1; i < positionList.length; i++) {
        distance +=
            (await MapUtil.getDistance(positionList[i - 1], positionList[i]))!;
      }
    }
    return distance;
  }
  //添加位置
  Future<double> addPoint(BMFCoordinate location) async {
    print("addPoint");
    int markerIndex = positionList.length % 3;
    //添加点
    positionList.add(location);
    BMFMarker marker = BMFMarker(
        position: location,
        icon: POINTS[markerIndex],
        zIndex: 2,
        centerOffset: BMFPoint(15, 15));
    markers.add(marker);
    return await _recycle();
  }
  //回退位置
  Future<double> backPoint() async {
    if (positionList.isEmpty || markers.isEmpty) {
      return 0;
    }
    positionList.removeAt(positionList.length - 1);
    if (markers.isNotEmpty) {
      await MapUtil.removeMarker(mapController, markers[markers.length - 1]);
      markers.removeAt(markers.length - 1);
    }
    return await _recycle();
  }
}
lib/utils/pageutils.dart
@@ -3,6 +3,8 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'global.dart';
typedef PageDataLisener = void Function(dynamic data);
//滑动效果
@@ -46,6 +48,12 @@
    dataLisener!(result);
  }
  static void navigateToNextPagePush(Widget page) {
    navigatorKey.currentState!.push(
        MaterialPageRoute(builder: (BuildContext context) => page));
  }
  static void navigateToNextPageWithFinish(
      BuildContext context, PageRoute route) {
    Navigator.of(context).pushReplacement(route);
lib/utils/permission_util.dart
New file
@@ -0,0 +1,139 @@
import 'dart:convert';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'event_bus_util.dart';
class PermissionUtil {
  static var deniedSets = Set();
  static Future _loadDeniedPermissions() async {
    //加载
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String>? list = prefs.getStringList("permission_delay_list");
    deniedSets.clear();
    if (list != null) {
      deniedSets.addAll(list);
    }
    print(jsonEncode(list));
  }
  static Future _denyPermission(Permission permission) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String>? list = prefs.getStringList("permission_delay_list");
    list ??= [];
    bool hasAdd = false;
    list.forEach((element) {
      if (element == permission.value.toString()) {
        hasAdd = true;
      }
    });
    if (!hasAdd) {
      list.add(permission.value.toString());
    }
    await prefs.setStringList("permission_delay_list", list);
  }
  static Future _removeDenyPermission(Permission permission) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String>? list = prefs.getStringList("permission_delay_list");
    list ?? [];
    for (int i = 0; i < list!.length; i++) {
      if (list[i] == permission.value.toString()) {
        list.removeAt(i);
        i--;
      }
    }
    await prefs.setStringList("permission_delay_list", list);
  }
  ///打开应用设置
  static openAppSetting() async {
    await openAppSettings();
  }
  ///打开权限,
  ///force:强制打开,权限被拒绝后记录
  static Future<PermissionStatus> openPermission(Permission permission,
      {force: false}) async {
    await _loadDeniedPermissions();
    PermissionStatus status = await permission.status;
    PermissionStatus? resultStatus;
    switch (status) {
    //Android授权
      case PermissionStatus.granted:
        resultStatus = status;
        break;
    //Android拒绝
      case PermissionStatus.denied:
        if (deniedSets.contains(permission.value.toString()) && !force) {
          resultStatus = PermissionStatus.denied;
        } else {
          resultStatus = await permission.request();
        }
        break;
    //Android禁止后不再提示
      case PermissionStatus.permanentlyDenied:
        if (deniedSets.contains(permission.value.toString()) && !force) {
          resultStatus = PermissionStatus.permanentlyDenied;
        } else {
          bool success = await openAppSettings();
          resultStatus = await permission.status;
        }
        break;
    //IOS
      case PermissionStatus.limited:
        if (deniedSets.contains(permission.value.toString()) && !force) {
          resultStatus = PermissionStatus.limited;
        } else {
          resultStatus = await permission.request();
        }
        break;
    //IOS
      case PermissionStatus.restricted:
        if (deniedSets.contains(permission.value.toString()) && !force) {
          resultStatus = PermissionStatus.restricted;
        } else {
          resultStatus = await permission.request();
        }
        break;
    }
    if(permission==Permission.locationAlways){
      eventBus.fire(UserLocationPermissionEventBus(resultStatus));
    }
    if (resultStatus == PermissionStatus.granted) {
      _removeDenyPermission(permission);
    } else {
      _denyPermission(permission);
    }
    return resultStatus;
  }
  ///批量打开权限
  static openPermissions(List<Permission> permissions, {force: true}) async {
    await _loadDeniedPermissions();
    List<Permission> requestPS = [];
    permissions.forEach((element) {
      if (!deniedSets.contains(element.value.toString())) {
        requestPS.add(element);
      }
    });
    if (requestPS.isNotEmpty) {
      await requestPS.request();
    }
  }
  ///关闭权限
  static Future<PermissionStatus> closePermission(Permission permission) async {
    bool success = await openAppSettings();
    return await permission.status;
  }
}
lib/utils/push_util.dart
@@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:io';
import 'dart:ui';
@@ -8,19 +9,36 @@
import 'package:flutter_baidu_mapapi_base/src/map/bmf_types.dart';
import 'package:flutter/services.dart';
import 'package:jpush_flutter/jpush_flutter.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/user/user_info.dart';
import 'package:locations/ui/main/location.dart';
import 'package:locations/ui/sos/sos.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/user_util.dart';
class PushUtil {
  static JPush _jpush = JPush();
import 'global.dart';
  static init() async {
class PushUtil {
  static final JPush _jpush = JPush();
  static init(BuildContext context) async {
    try {
      _jpush.addEventHandler(
          onReceiveNotification: (Map<String, dynamic> message) async {
        print("flutter onReceiveNotification: $message");
      }, onOpenNotification: (Map<String, dynamic> message) async {
        print("flutter onOpenNotification: $message");
        var extra = message["extras"]["cn.jpush.android.EXTRA"];
        extra = jsonDecode(extra);
        //打开页面
        var type = extra["type"];
        if (type == "sos") {
          //SOS
          NavigatorUtil.navigateToNextPagePush(SOSPage(title: ""));
        } else if (type == "requestLocation") {
          //请求定位
          LocationPage.getLocationInvite(navigatorKey.currentState!.context);
        }
      }, onReceiveMessage: (Map<String, dynamic> message) async {
        print("flutter onReceiveMessage: ${message["message"]}");
      }, onReceiveNotificationAuthorization:
@@ -41,6 +59,7 @@
    // Platform messages may fail, so we use a try/catch PlatformException.
    _jpush.getRegistrationID().then((rid) {
      print("flutter get registration id : $rid");
      UserApiUtil.uploadPushRegId(context, rid);
    });
    //如果登录了就设置用户的uid
lib/utils/setting_util.dart
New file
@@ -0,0 +1,46 @@
import 'package:shared_preferences/shared_preferences.dart';
class SettingUtil {
  //设置推送
  static Future<bool> setPush(bool enable) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return await prefs.setBool("setting_push", enable);
  }
  ///是否允许推送
  static Future<bool> isEnablePush() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    bool? result = prefs.getBool("setting_push");
    result ??= true;
    return result;
  }
  //设置推荐广告
  static Future<bool> setRecommendAd(bool enable) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return await prefs.setBool("setting_recommend_ad", enable);
  }
  ///是否允许推荐广告
  static Future<bool> isEnableRecommendAd() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    bool? result = prefs.getBool("setting_recommend_ad");
    result ??= true;
    return result;
  }
  //设置推荐广告
  static Future<bool> setLocationSpan(int second) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return await prefs.setInt("setting_location_span", second);
  }
  ///是否允许推荐广告
  static Future<int> getLocationSpan() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    int? result = prefs.getInt("setting_location_span");
    result ??= 30;
    return result;
  }
}
lib/utils/ui_constant.dart
@@ -5,6 +5,8 @@
//滑动效果
class ColorConstant {
  static const Color theme = Color(0xFF0E95FE);
  static const Color title = Color(0xFF333333);
}
class Constant {
lib/utils/ui_utils.dart
@@ -18,3 +18,11 @@
        });
  }
}
class DimenUtil {
  //获取像素比
  static double getPixelRatio(BuildContext context) {
    MediaQueryData mediaQuery = MediaQuery.of(context);
    return mediaQuery.devicePixelRatio;
  }
}
pubspec.lock
@@ -180,7 +180,7 @@
      name: flutter_html
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.1.5"
    version: "2.2.0"
  flutter_layout_grid:
    dependency: transitive
    description:
@@ -243,7 +243,7 @@
      name: flutter_tencentad
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.0.4"
    version: "1.1.0"
  flutter_test:
    dependency: "direct dev"
    description: flutter
@@ -255,7 +255,7 @@
      name: flutter_unionad
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.1.7"
    version: "1.1.9"
  flutter_web_plugins:
    dependency: transitive
    description: flutter
@@ -295,7 +295,7 @@
      name: jpush_flutter
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.1.7"
    version: "2.1.8"
  js:
    dependency: transitive
    description:
@@ -303,6 +303,13 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.6.3"
  launch_review:
    dependency: "direct dev"
    description:
      name: launch_review
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.0.1"
  lints:
    dependency: transitive
    description:
@@ -345,6 +352,13 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.1.1"
  package_info:
    dependency: "direct dev"
    description:
      name: package_info
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.2"
  path:
    dependency: transitive
    description:
@@ -652,12 +666,26 @@
    source: hosted
    version: "1.3.0"
  url_launcher:
    dependency: transitive
    dependency: "direct dev"
    description:
      name: url_launcher
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "6.0.12"
    version: "6.0.17"
  url_launcher_android:
    dependency: transitive
    description:
      name: url_launcher_android
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "6.0.13"
  url_launcher_ios:
    dependency: transitive
    description:
      name: url_launcher_ios
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "6.0.13"
  url_launcher_linux:
    dependency: transitive
    description:
@@ -769,7 +797,7 @@
      name: webview_flutter
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.3.0"
    version: "2.3.1"
  webview_flutter_android:
    dependency: transitive
    description:
pubspec.yaml
@@ -71,11 +71,12 @@
  flutter_bmflocation: ^2.0.0-nullsafety.1
  #  flutter_bmflocation: ^2.0.0-nullsafety.1
  webview_flutter: ^2.1.2
  webview_flutter: ^2.3.1
  #穿山甲广告
  flutter_unionad: ^1.1.6
  flutter_tencentad: ^1.0.4
  flutter_html: ^2.1.5
  flutter_unionad: ^1.1.9
  #广点通广告
  flutter_tencentad: ^1.1.0
  flutter_html: ^2.2.0
  #时间日期选择
  flutter_datetime_picker: ^1.5.1
@@ -92,7 +93,7 @@
  event_bus: ^2.0.0
  #极光推送
  jpush_flutter: ^2.1.7
  jpush_flutter: ^2.1.8
  #获取设备信息
  device_info: ^2.0.3
@@ -105,6 +106,13 @@
  #下拉刷新
  pull_to_refresh: ^2.0.0
  url_launcher: ^6.0.17
  package_info: ^2.0.2
  launch_review: ^3.0.1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec