admin
2022-01-12 8327000a0cce5e47226372e0e25c1e6faec497e7
推送功能完善
6个文件已删除
43个文件已修改
21个文件已添加
5065 ■■■■■ 已修改文件
android/app/build.gradle 68 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/arm64-v8a/libcrashsdk.so 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/arm64-v8a/libumeng-spy.so 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/armeabi-v7a/libcrashsdk.so 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/armeabi-v7a/libumeng-spy.so 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/armeabi/libcrashsdk.so 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/armeabi/libumeng-spy.so 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/auth_number_product-2.12.3-log-online-standard-release.aar 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/auth_number_product-2.12.3.4-log-online-standard-release.aar 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/crashshield-2.1.2-release.aar 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/crashshield-2.1.3.2-release.aar 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/logger-2.1.2-release.aar 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/logger-2.1.3.2-release.aar 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/main-2.1.2-release.aar 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/main-2.1.3.2-release.aar 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/utdid4all-1.5.2.1-proguard.jar 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/x86/libcrashsdk.so 补丁 | 查看 | 原始文档 | blame | 历史
android/app/lib/x86/libumeng-spy.so 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/AndroidManifest.xml 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/MainActivity.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/MyApplication.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/plugins/DeviceUtilPlugins.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/plugins/LocationPlugins.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/plugins/POISearchFlutterPlugins.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/java/com/yeshi/location/utils/LocationUtils.java 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/src/main/res/values/styles.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/common/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/common/ic_share_app_bg.png 补丁 | 查看 | 原始文档 | blame | 历史
assets/images/common/icon_refresh.png 补丁 | 查看 | 原始文档 | blame | 历史
lib/api/http.dart 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/main.dart 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/map/location_model.dart 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/map/location_user_model.dart 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/map/map_model.dart 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/map/poi_model.dart 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/model/user/user_info.dart 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/common/browser.dart 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/location.dart 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/main.dart 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/mine.dart 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/main/travel_main.dart 161 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/map/location_search.dart 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/map/map.dart 246 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/map/travel.dart 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/add_location_person.dart 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/login.dart 442 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/permission.dart 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/settings.dart 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/mine/share_to_friends.dart 302 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/sos/sos.dart 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/dialog.dart 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/map_marker.dart 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/ui/widget/nav.dart 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/ad_util.dart 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/app_util.dart 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/event_bus_util.dart 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/global.dart 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/jsinterface.dart 150 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/location_util.dart 192 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/map_js_util.dart 488 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/map_util.dart 319 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/pageutils.dart 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/permission_util.dart 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/push_util.dart 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/search_util.dart 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/share_utils.dart 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/ui_constant.dart 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/utils/user_util.dart 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pubspec.lock 290 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pubspec.yaml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
android/app/build.gradle
@@ -43,11 +43,11 @@
        applicationId "com.dw.zzql"
        minSdkVersion 19
        targetSdkVersion 31
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        versionCode 22  //flutterVersionCode.toInteger()
        versionName "2.6.0" //flutterVersionName
        ndk {
            //兼容64位与32位系统
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
            abiFilters 'armeabi','armeabi-v7a', 'arm64-v8a' //,'arm64-v8a', 'x86', 'x86_64'
        }
        manifestPlaceholders = [
@@ -75,6 +75,44 @@
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.release
            minifyEnabled false
            shrinkResources false
        }
    }
    //配置自定义打包名称
    applicationVariants.all { variant ->
        variant.outputs.all {
            def fileName
//            if (variant.buildTypes.name.equals('release')) {
                fileName = "location_${variant.mergedFlavor.versionName}_release.apk"
//            } else if (variant.buildTypes.name.equals('debug')) {
//                fileName = "${variant.mergedFlavor.versionName}_debug.apk"
//            }
            outputFileName = fileName
        }
    }
}
project.afterEvaluate {
    project.android.applicationVariants.all { variant ->
        variant.outputs.each { output ->
            output.processResources.doFirst { pm ->
                String manifestPath = output.processResources.manifestFile;
                def manifestContent = file(manifestPath).getText()
                //删除注释,防止注释里面的中文乱码导致更改后的内容系统无法正常解析
//                manifestContent = manifestContent.replaceAll("<!--[\\s\\S]*?-->", "")
//                manifestContent = manifestContent.replace('<uses-permission android:name="android.permission.CAMERA" />', '')
//                manifestContent = manifestContent.replace('<uses-permission android:name="android.permission.BLUETOOTH" />', '')
//                manifestContent = manifestContent.replace('<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />', '')
//                manifestContent = manifestContent.replace('<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />', '')
//                manifestContent = manifestContent.replace('<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />', '')
//                manifestContent = manifestContent.replace('<uses-permission android:name="android.permission.REORDER_TASKS" />', '')
                println "AndroidManifest-Content:"
                println manifestContent
                file(manifestPath).write(manifestContent)
            }
        }
    }
}
@@ -87,18 +125,26 @@
    api fileTree(include: '*', dir: 'lib')
    implementation 'com.android.support:multidex:1.0.3'
    //umeng
    api files('lib/umeng-apm-armeabi-v1.5.2.aar')
    api files('lib/umeng-common-9.4.4.jar')
    api files('lib/umeng-asms-armeabi-v1.4.1.aar')
    implementation files('lib/umeng-apm-armeabi-v1.5.2.aar')
    implementation files('lib/umeng-common-9.4.4.jar')
    implementation files('lib/umeng-asms-armeabi-v1.4.1.aar')
    implementation files('lib/utdid4all-1.5.2.1-proguard.jar')
//    implementation files('lib/umeng-asms-v1.4.1.jar')
    //阿里云手机号认证
    implementation files('lib/crashshield-2.1.2-release.aar')
    implementation files('lib/logger-2.1.2-release.aar')
    implementation files('lib/main-2.1.2-release.aar')
    implementation files('lib/auth_number_product-2.12.3-log-online-standard-release.aar')
    implementation files('lib/crashshield-2.1.3.2-release.aar')
    implementation files('lib/logger-2.1.3.2-release.aar')
    implementation files('lib/main-2.1.3.2-release.aar')
    implementation files('lib/auth_number_product-2.12.3.4-log-online-standard-release.aar')
    //QQ互联
    implementation files('lib/open_sdk_3.5.7.4_r1bc9afe_lite.jar')
    implementation 'com.taobao.android:utdid4all:1.5.2'
    implementation 'com.google.code.gson:gson:2.8.0'
}
repositories {
    flatDir {
        dirs 'libs'
    }
}
android/app/lib/arm64-v8a/libcrashsdk.so
Binary files differ
android/app/lib/arm64-v8a/libumeng-spy.so
Binary files differ
android/app/lib/armeabi-v7a/libcrashsdk.so
Binary files differ
android/app/lib/armeabi-v7a/libumeng-spy.so
Binary files differ
android/app/lib/armeabi/libcrashsdk.so
Binary files differ
android/app/lib/armeabi/libumeng-spy.so
Binary files differ
android/app/lib/auth_number_product-2.12.3-log-online-standard-release.aar
Binary files differ
android/app/lib/auth_number_product-2.12.3.4-log-online-standard-release.aar
Binary files differ
android/app/lib/crashshield-2.1.2-release.aar
Binary files differ
android/app/lib/crashshield-2.1.3.2-release.aar
Binary files differ
android/app/lib/logger-2.1.2-release.aar
Binary files differ
android/app/lib/logger-2.1.3.2-release.aar
Binary files differ
android/app/lib/main-2.1.2-release.aar
Binary files differ
android/app/lib/main-2.1.3.2-release.aar
Binary files differ
android/app/lib/utdid4all-1.5.2.1-proguard.jar
Binary files differ
android/app/lib/x86/libcrashsdk.so
Binary files differ
android/app/lib/x86/libumeng-spy.so
Binary files differ
android/app/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
    xmlns:tools="http://schemas.android.com/tools"
    package="com.yeshi.location">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
@@ -11,6 +13,7 @@
        android:name=".MyApplication"
        android:label="@string/app_name"
        android:usesCleartextTraffic="true"
        android:theme="@style/NormalTheme"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
@@ -100,11 +103,13 @@
           android:exported="false"
           android:launchMode="singleTop" />
        <!--百度地图 -->
        <meta-data
            android:name="UMENG_APPKEY"
            android:value="@string/umeng_key"></meta-data>
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="cOlR8Kpr9SKqL3gghONxxltcZOxEqFh0" />
            android:name="UMENG_CHANNEL"
            android:value="QQ"></meta-data>
    </application>
</manifest>
android/app/src/main/java/com/yeshi/location/MainActivity.java
@@ -5,8 +5,8 @@
import com.yeshi.location.plugins.DeviceUtilPlugins;
import com.yeshi.location.plugins.FlutterAliyunPhoneNumberAuthPlugins;
import com.yeshi.location.plugins.InitAppFlutterPlugins;
import com.yeshi.location.plugins.LocationPlugins;
import com.yeshi.location.plugins.LoginFlutterPlugins;
import com.yeshi.location.plugins.POISearchFlutterPlugins;
import com.yeshi.location.plugins.ShareFlutterPlugins;
import androidx.annotation.NonNull;
@@ -35,9 +35,10 @@
        InitAppFlutterPlugins.registerWith(flutterEngine.getDartExecutor().getBinaryMessenger());
        //登录插件
        LoginFlutterPlugins.registerWith(MainActivity.this, flutterEngine.getDartExecutor().getBinaryMessenger());
//        //POI搜索
//        POISearchFlutterPlugins.registerWith(MainActivity.this,flutterEngine.getDartExecutor().getBinaryMessenger());
        //设备帮助插件
        DeviceUtilPlugins.registerWith(getApplicationContext(), flutterEngine.getDartExecutor().getBinaryMessenger());
        //定位插件
        LocationPlugins.registerWith(getApplicationContext(), flutterEngine.getDartExecutor().getBinaryMessenger());
    }
}
android/app/src/main/java/com/yeshi/location/MyApplication.java
@@ -7,7 +7,6 @@
import android.telephony.TelephonyManager;
import android.util.Log;
import com.baidu.mapapi.SDKInitializer;
import com.tencent.tauth.Tencent;
import com.umeng.analytics.MobclickAgent;
import com.umeng.commonsdk.UMConfigure;
@@ -47,8 +46,6 @@
        } catch (Throwable e) {
        }
        //百度地图
        SDKInitializer.initialize(application);
        //初始化QQ登录
        Tencent.setIsPermissionGranted(true);
    }
android/app/src/main/java/com/yeshi/location/plugins/DeviceUtilPlugins.java
@@ -1,19 +1,10 @@
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 com.yeshi.location.utils.ManifestDataUtil;
import java.util.HashMap;
import java.util.Map;
import androidx.annotation.NonNull;
@@ -29,9 +20,7 @@
    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) {
@@ -47,7 +36,6 @@
    @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) {
@@ -55,6 +43,10 @@
            case "getUtdid":
                reply.reply(UTDevice.getUtdid(activity));
                break;
            case "getChannel":
                String channel = ManifestDataUtil.getAppMetaData(activity, "UMENG_CHANNEL");
                reply.reply(channel);
                break;
        }
    }
}
android/app/src/main/java/com/yeshi/location/plugins/LocationPlugins.java
New file
@@ -0,0 +1,70 @@
package com.yeshi.location.plugins;
import android.content.Context;
import android.location.Address;
import android.util.Log;
import android.widget.Toast;
import com.google.gson.Gson;
import com.yeshi.location.utils.LocationUtils;
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 LocationPlugins implements BasicMessageChannel.MessageHandler<Object> {
    private static final String TAG = "LocationUtil";
    private final Context activity;
    private final BasicMessageChannel<Object> messageChannel;
    private LocationPlugins(Context activity, BinaryMessenger messager) {
        this.activity = activity;
        this.messageChannel = new BasicMessageChannel<Object>(messager, "LocationUtil", StandardMessageCodec.INSTANCE);
        messageChannel.setMessageHandler(this);
    }
    public static LocationPlugins registerWith(Context activity, BinaryMessenger messager) {
        return new LocationPlugins(activity, messager);
    }
    @Override
    public void onMessage(@Nullable Object message, @NonNull BasicMessageChannel.Reply<Object> reply) {
        LocationUtils.getInstance(activity).setAddressCallback(new LocationUtils.AddressCallback() {
            @Override
            public void onGetAddress(Address address) {
                Map<String, String> map = new HashMap<>();
                map.put("lat", address.getLatitude() + "");
                map.put("lng", address.getLongitude() + "");
                String addressLine = "";
                for (int i = 0; address.getAddressLine(i) != null; i++) {
                    addressLine = address.getAddressLine(i);
                }
                map.put("address", addressLine);
                map.put("addressDetail", address.getFeatureName());
                Log.i(TAG, new Gson().toJson(address));
                try {
                    reply.reply(map);
                } catch (Exception e) {
                }
            }
            @Override
            public void onGetLocation(double lat, double lng) {
                Log.i(TAG, String.format("定位经纬度 lat:%s lng:%s", lat, lng));
                Map<String, String> map = new HashMap<>();
                map.put("lat", lat + "");
                map.put("lng", lng + "");
                reply.reply(map);
            }
        });
    }
}
android/app/src/main/java/com/yeshi/location/plugins/POISearchFlutterPlugins.java
File was deleted
android/app/src/main/java/com/yeshi/location/utils/LocationUtils.java
New file
@@ -0,0 +1,249 @@
package com.yeshi.location.utils;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
import android.widget.Toast;
import com.google.gson.Gson;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import androidx.core.app.ActivityCompat;
public class LocationUtils {
    private volatile static LocationUtils uniqueInstance;
    private LocationManager locationManager;
    private Context mContext;
    private static ArrayList<AddressCallback> addressCallbacks;
    private AddressCallback addressCallback;
    public AddressCallback getAddressCallback() {
        return addressCallback;
    }
    public void setAddressCallback(AddressCallback addressCallback) {
        this.addressCallback = addressCallback;
        if (isInit) {
            showLocation();
        } else {
            isInit = true;
        }
    }
    private static Location location;
    private boolean isInit = false;//是否加载过
    private LocationUtils(Context context) {
        mContext = context;
        getLocation();
    }
    //采用Double CheckLock(DCL)实现单例
    public static LocationUtils getInstance(Context context) {
        if (uniqueInstance == null) {
            synchronized (LocationUtils.class) {
                if (uniqueInstance == null) {
                    addressCallbacks = new ArrayList<>();
                    uniqueInstance = new LocationUtils(context);
                }
            }
        }
        return uniqueInstance;
    }
    /**
     * 添加回调事件
     *
     * @param addressCallback
     */
    private void addAddressCallback(AddressCallback addressCallback) {
        addressCallbacks.add(addressCallback);
        if (isInit) {
            showLocation();
        }
    }
    /**
     * 移除回调事件
     *
     * @param addressCallback
     */
    public void removeAddressCallback(AddressCallback addressCallback) {
        if (addressCallbacks.contains(addressCallback)) {
            addressCallbacks.remove(addressCallback);
        }
    }
    /**
     * 清空回调事件
     */
    public void cleareAddressCallback() {
        removeLocationUpdatesListener();
        addressCallbacks.clear();
    }
    private void getLocation() {
        //1.获取位置管理器
        locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
        //添加用户权限申请判断
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        //2.获取位置提供器,GPS或是NetWork
        // 获取所有可用的位置提供器
        List<String> providerList = locationManager.getProviders(true);
        String locationProvider;
        if (providerList.contains(LocationManager.GPS_PROVIDER)) {
            //GPS 定位的精准度比较高,但是非常耗电。
            System.out.println("=====GPS_PROVIDER=====");
            locationProvider = LocationManager.GPS_PROVIDER;
        } else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {//Google服务被墙不可用
            //网络定位的精准度稍差,但耗电量比较少。
            System.out.println("=====NETWORK_PROVIDER=====");
            locationProvider = LocationManager.NETWORK_PROVIDER;
        } else {
            System.out.println("=====NO_PROVIDER=====");
            // 当没有可用的位置提供器时,弹出Toast提示用户
            Intent intent = new Intent();
            intent.setAction(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            mContext.startActivity(intent);
            return;
        }
        //3.获取上次的位置,一般第一次运行,此值为null
        location = locationManager.getLastKnownLocation(locationProvider);
        if (location != null) {
            // 显示当前设备的位置信息
            System.out.println("==显示当前设备的位置信息==");
            showLocation();
        } else {//当GPS信号弱没获取到位置的时候可从网络获取
            System.out.println("==Google服务被墙的解决办法==");
            getLngAndLatWithNetwork();//Google服务被墙的解决办法
        }
        // 监视地理位置变化,第二个和第三个参数分别为更新的最短时间minTime和最短距离minDistace
        //LocationManager 每隔 5 秒钟会检测一下位置的变化情况,当移动距离超过 10 米的时候,
        // 就会调用 LocationListener 的 onLocationChanged() 方法,并把新的位置信息作为参数传入。
//        locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
        locationManager.requestSingleUpdate(locationProvider,locationListener,null);
    }
    //获取经纬度
    private void showLocation() {
        if (location == null) {
            getLocation();
        } else {
            double latitude = location.getLatitude();//纬度
            double longitude = location.getLongitude();//经度
//            for(AddressCallback addressCallback:addressCallbacks){
//                addressCallback.onGetLocation(latitude,longitude);
//            }
            if (!getAddress(latitude, longitude)) {
                if (addressCallback != null) {
                    addressCallback.onGetLocation(latitude, longitude);
                }
            }
        }
    }
    private boolean getAddress(double latitude, double longitude) {
        //Geocoder通过经纬度获取具体信息
        Geocoder gc = new Geocoder(mContext, Locale.getDefault());
        try {
            List<Address> locationList = gc.getFromLocation(latitude, longitude, 1);
            if (locationList != null) {
                Address address = locationList.get(0);
                String countryName = address.getCountryName();//国家
                String countryCode = address.getCountryCode();
                String adminArea = address.getAdminArea();//省
                String locality = address.getLocality();//市
                String subLocality = address.getSubLocality();//区
                String featureName = address.getFeatureName();//街道
                for (int i = 0; address.getAddressLine(i) != null; i++) {
                    String addressLine = address.getAddressLine(i);
                    //街道名称:广东省深圳市罗湖区蔡屋围一街深圳瑞吉酒店
                    System.out.println("addressLine=====" + addressLine);
                }
                if (addressCallback != null) {
                    addressCallback.onGetAddress(address);
                }
                return true;
//                for(AddressCallback addressCallback:addressCallbacks){
//                    addressCallback.onGetAddress(address);
//                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
    private void removeLocationUpdatesListener() {
        if (locationManager != null) {
            uniqueInstance = null;
            locationManager.removeUpdates(locationListener);
        }
    }
    private LocationListener locationListener = new LocationListener() {
        // Provider的状态在可用、暂时不可用和无服务三个状态直接切换时触发此函数
        @Override
        public void onStatusChanged(String provider, int status, Bundle arg2) {
        }
        // Provider被enable时触发此函数,比如GPS被打开
        @Override
        public void onProviderEnabled(String provider) {
        }
        // Provider被disable时触发此函数,比如GPS被关闭
        @Override
        public void onProviderDisabled(String provider) {
        }
        //当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
        @Override
        public void onLocationChanged(Location loc) {
            System.out.println("==onLocationChanged==");
//            location = loc;
//            showLocation();
        }
    };
    //从网络获取经纬度
    private void getLngAndLatWithNetwork() {
        //添加用户权限申请判断
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
        locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER,locationListener,null);//requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 10, locationListener);
        location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
        showLocation();
    }
    public interface AddressCallback {
        void onGetAddress(Address address);
        void onGetLocation(double lat, double lng);
    }
}
android/app/src/main/res/values/styles.xml
@@ -15,4 +15,7 @@
    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
        <item name="android:windowBackground">?android:colorBackground</item>
    </style>
</resources>
assets/images/common/ic_launcher.png
assets/images/common/ic_share_app_bg.png
assets/images/common/icon_refresh.png
lib/api/http.dart
@@ -28,7 +28,7 @@
  DialogUtil.showDialog(context, LoadingDialog(""));
}
_dismissDialog(BuildContext context) {
dismissDialog(BuildContext context) {
  Navigator.pop(context);
}
@@ -87,6 +87,10 @@
      params["device"] = _iosInfo!.identifierForVendor;
    }
    if (Global.channel != null) {
      params["channel"] = Global.channel;
    }
    params["sign"] = _getSign(params);
    return params;
@@ -102,10 +106,18 @@
    httpClient.connectionTimeout = const Duration(seconds: 20);
    var uri = Uri(
        scheme: "http",
        host: "192.168.3.122",
        host: "api.location.izzql.com",
        path: api,
        port: 8082,
        port: 8090,
        queryParameters: params);
    // var uri = Uri(
    //     scheme: "http",
    //     host: "192.168.3.122",
    //     path: api,
    //     port: 8082,
    //     queryParameters: params);
    print("uri:$uri");
    if (onStart != null) {
@@ -143,7 +155,7 @@
        await HttpUtil.baseRequest("/api/v1/sms/sendSMS", {"phone": phone}, () {
      showLoading(context);
    });
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -183,7 +195,7 @@
        await HttpUtil.baseRequest("/api/v1/user/loginPhone", params, () {
      showLoading(context);
    }, notifyError: true);
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -220,7 +232,7 @@
class LocationApiUtil {
  ///上传位置
  static Future<Map<String, dynamic>?> uploadLocation(
      BaiduLocation location) async {
      SimpleLocation location) async {
    var uid = await UserUtil.getUid();
    var result = await HttpUtil.baseRequest(
        "/api/v1/location/uploadLocation",
@@ -228,7 +240,7 @@
          "latitude": location.latitude.toString(),
          "longitude": location.longitude.toString(),
          "address": location.address,
          "addressDetail": location.locationDetail,
          "addressDetail": location.addressDetail,
          "uid": uid.toString()
        },
        () {});
@@ -287,7 +299,7 @@
      showLoading(context);
    });
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -311,7 +323,7 @@
        "/api/v1/location/updateLocationUser", params, () {
      showLoading(context);
    });
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -326,7 +338,7 @@
        "/api/v1/location/deleteLocationUser", params, () {
      showLoading(context);
    });
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -390,6 +402,29 @@
    }
    return null;
  }
  //拒绝定位邀请
  static Future<Map<String, dynamic>?> searchNearBy(
      String kw, double lat, double lon) async {
    var params = {"kw": kw, "lat": lat.toString(), "lon": lon.toString()};
    var result = await HttpUtil.baseRequest(
        "/api/v1/location/searchNearBy", params, () {});
    if (result.success) {
      return result.data;
    }
    return null;
  }
  ///地理位置编码
  static Future<Map<String, dynamic>?> geoCode(double lat, double lon) async {
    var params = {"lat": lat.toString(), "lon": lon.toString()};
    var result =
        await HttpUtil.baseRequest("/api/v1/location/geoCode", params, () {});
    if (result.success) {
      return result.data;
    }
    return null;
  }
}
class SOSApiUtil {
@@ -418,7 +453,7 @@
        "/api/v1/sos/addEmergencyContacts", params, () {
      showLoading(context);
    });
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -462,7 +497,7 @@
        "/api/v1/sos/deleteEmergencyContacts", params, () {
      showLoading(context);
    });
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -495,7 +530,7 @@
        "/api/v1/sos/updateEmergencyContacts", params, () {
      showLoading(context);
    });
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -516,13 +551,14 @@
    params["latitude"] = location.latitude.toString();
    params["longitude"] = location.longitude.toString();
    params["address"] = location.address.toString();
    params["addressDetail"] = location.addressDetail.toString();
    params["addressDetail"] =
        location.addressDetail == null ? "" : location.addressDetail.toString();
    var result =
        await HttpUtil.baseRequest("/api/v1/sos/addSOSRecord", params, () {
      showLoading(context);
    });
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -565,7 +601,7 @@
        await HttpUtil.baseRequest("/api/v1/sos/clearSOSRecord", params, () {
      showLoading(context);
    });
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
@@ -633,7 +669,7 @@
      showLoading(context);
    }, notifyError: true);
    _dismissDialog(context);
    dismissDialog(context);
    if (result.success) {
      return result.data;
    }
lib/main.dart
@@ -22,9 +22,9 @@
void main() {
  if (Platform.isAndroid) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
    SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      systemNavigationBarColor: Color(0xFF000000),
      systemNavigationBarColor: Colors.white,
      systemNavigationBarDividerColor: null,
      systemNavigationBarIconBrightness: Brightness.light,
      statusBarIconBrightness: Brightness.dark,
@@ -39,7 +39,7 @@
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        title: Constant.APP_NAME,
        theme: ThemeData(
            // This is the theme of your application.
            //
@@ -51,7 +51,7 @@
            // Notice that the counter didn't reset back to zero; the application
            // is not restarted.
            primaryColor: Color(0xFFFFFFFF)),
            primaryColor: const Color(0xFFFFFFFF)),
        home: SplashPage(title: ''),
        navigatorKey: navigatorKey);
  }
@@ -80,11 +80,12 @@
  int selectIndex = 1;
  Widget? _splash;
  @override
  void initState() {
    super.initState();
  }
  bool _jumpToMain = false;
  void init() async {
    //请求配置信息
@@ -93,17 +94,29 @@
      ConfigUtil.saveConfig(value["data"]);
    }
    bool result = await AppUtil.initApp(context);
    await AppUtil.initApp(context);
    double width = MediaQuery.of(context).size.width;
    double height = MediaQuery.of(context).size.height;
    Widget? splash = AdUtil.loadSplash(await AdUtil.getAdInfo(AdPosition.splash),width,height-94, (success, msg) {
      NavigatorUtil.navigateToNextPageWithFinish(context,
          MaterialPageRoute(builder: (context) {
        return MainPage(title: "");
      }));
    Widget splash = AdUtil.loadSplash(
        await AdUtil.getAdInfo(AdPosition.splash),
        MediaQuery.of(context).size.width,
        MediaQuery.of(context).size.height - 94, (success, msg) {
      if (_jumpToMain == true) {
        return;
      }
      _jumpToMain = true;
      if (success) {
        NavigatorUtil.navigateToNextPageWithFinish(context,
            CupertinoPageRoute(builder: (context) {
          return MainPage(title: "");
        }));
      } else {
        Future.delayed(const Duration(seconds: 2), () {
          NavigatorUtil.navigateToNextPageWithFinish(context,
              CupertinoPageRoute(builder: (context) {
            return MainPage(title: "");
          }));
        });
      }
    });
    setState(() {
@@ -113,7 +126,7 @@
  Future requestPermission() async {
    await PermissionUtil.openPermission(Permission.phone);
    await PermissionUtil.openPermission(PermissionUtil.getLocationPermission());
    // await PermissionUtil.openPermission(PermissionUtil.getLocationPermission());
    return;
  }
@@ -122,32 +135,36 @@
    UserUtil.isAgreeProtocol().then((value) {
      if (!value) {
        //弹出用户协议框
        String content =
            "欢迎您使用百度地图服务!<a href='${Constant.PROTOCOL_URL}'> 用户协议 </a>和<a href='${Constant.PRIVACY_URL}'>隐私政策</a>   我们非常重视您的隐私保护和个人信息保护。本隐私政策适用于您通过任何方式对百度地图各项服务的访问和使用。您可以利用百度地图搜索路况信息、商家信息、定位您所在的位置,进行路径规划、导航您想去的地址,搜索周边的服务等(以下统称“百度地图产品或服务”)。您具体获得的百度地图服务内容可能因为您使用的百度地图的版本及搭载设备不同而有所差异,如果在部分版本或搭载设备中不涵盖某些服务内容或未提供特定功能(例如:部分版本不支持登录,我们可能无法为您提供第三方服务以及其他登录后才能使用的功能;手表等穿戴设备暂时只支持步骑行的路线规划及导航,所以我们在穿戴设备上搭载的地图无法为您提供如驾车路线规划及导航等其他功能),本隐私政策中涉及到上述服务/功能及相关个人信息的内容将不适用。您可以通过多种不同的方式来使用我们的产品和服务,包括百度地图的网站、软件、供第三方网站和应用程序使用的百度地图软件开发工具包(SDK)和应用程序编程接口(API)、车载导航仪、智能后视镜等智能硬件设备。";
        String content = "尊敬的用户:<br>" +
            "感谢您对定位App一直以来的信任!<br>" +
            "我们非常重视保护您的个人信息和隐私。在您使用我们的软件前,为了更好的保障您的权利,请务必阅读<a href='${Constant.PROTOCOL_URL}'>《用户协议》</a>和<a href='${Constant.PRIVACY_URL}'>《隐私政策》</a>,您同意并接受所有条款后,才能使用我们软件。若您注册成为本软件会员或接受好友的邀请即表明您允许向本软件中的好友提供您的地理位置和轨迹等信息。为确保软件正常运行我们需要获得,尤其是:<br>" +
            "1、我们对您的个人信息(包括但不限于<b style='color:red'>设备MAC地址、IMEI/Android ID、位置信息</b>等信息)的收集/保存/使用/对外提供/保护等规则条款,以及您的用户权利等条款;<br>" +
            "2、约定我们的限制责任、免责条款;<br>" +
            "3、其他以加粗或斜体字进行标识的重要条款。<br>" +
            "4、特别申明:本软件定位守护等功能需双方都下载本软件并同意授权后才可正常使用,不涉及侵犯个人隐私问题。获取地理信息必须在用户双方知情并同意的情况下进行,本软件仅限用于家庭/亲友/熟人/朋友间使用。<br>" +
            "如您对协议有任何疑虑,可通过电子邮箱:dw365@foxmail.com 向我们询问,我们将为您竭诚解答。您点击“同意并继续”的行为代表您已阅读完毕并接受以上协议全部条款。如您同意以上协议内容,请您点击“同意并继续”,开始使用本软件。";
        DialogUtil.showDialog(
            context,
            NotifyDialog(
              "用户协议&隐私政策",
              content,
              () {
                Navigator.of(context).pop();
              },
              () {
                UserUtil.setAgreeProtocol().then((value) {
                  if (value) {
                    //弹出权限框
                    DialogUtil.showDialog(context, PermissionNotifyDialog(() {
                      Navigator.of(context).pop();
                      requestPermission().then((value) {
                        init();
                      });
                    }));
                  } else {}
                });
              },
              richText: true,
              height: 400,
            ));
            NotifyDialog("用户协议&隐私政策", content, () {
              SystemNavigator.pop();
            }, () {
              UserUtil.setAgreeProtocol().then((value) {
                if (value) {
                  //弹出权限框
                  DialogUtil.showDialog(context, PermissionNotifyDialog(() {
                    Navigator.of(context).pop();
                    requestPermission().then((value) {
                      init();
                    });
                  }));
                } else {}
              });
            },
                richText: true,
                height: 400,
                cancelName: "不同意",
                sureName: "同意并继续"));
      } else {
        //已经同意了
        init();
@@ -182,7 +199,7 @@
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: const [
                      Text(
                        "定位追踪轨迹",
                        Constant.APP_NAME,
                        style:
                            TextStyle(color: Color(0xFF03B5FF), fontSize: 23),
                      ),
lib/model/map/location_model.dart
@@ -11,7 +11,6 @@
/// address : "中国重庆市渝北区礼嘉街道嘉康路"
/// locationDetail : "在天际湾附近"
///
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
class BaiduLocation {
  BaiduLocation({
    String? locTime,
@@ -162,9 +161,6 @@
    return map;
  }
  BMFCoordinate toBMFCoordinate(){
    return BMFCoordinate(_latitude!,_longitude!);
  }
}
lib/model/map/location_user_model.dart
@@ -58,8 +58,9 @@
        _userType = LocationUserType.other;
        break;
    }
    _userInfo = UserInfo.fromJson(json['userInfo']);
    if (json['userInfo'] != null) {
      _userInfo = UserInfo.fromJson(json['userInfo']);
    }
  }
  String? _id;
lib/model/map/map_model.dart
@@ -1,9 +1,89 @@
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
import 'location_model.dart';
class MapShowInfo {
  final BMFCoordinate center;
  final SimpleLocation center;
  final int zoomLevel;
  MapShowInfo(this.center, this.zoomLevel);
}
class Marker {
  String? _id;
  SimpleLocation position;
  String icon;
  int? zIndex;
  double? width;
  double? height;
  Offset? centerOffset;
  Marker(
      {required this.position,
      required this.icon,
      String? id,
      this.zIndex,
      this.width,
      this.height,
      this.centerOffset}) {
    if (id == null) {
      var timeStamp = DateTime.now().millisecondsSinceEpoch;
      _id = '$timeStamp' '_' '$hashCode';
    } else {
      _id = id;
    }
  }
  /// 获取id
  String get Id => _id!;
  Map<String, Object?> toMap() {
    Map<String, Object?> map = {
      'id': Id,
      'position': position.toJson(),
      'zIndex': zIndex,
      'icon': icon,
    };
    if (width != null) {
      map["width"] = width;
    }
    if (height != null) {
      map["height"] = height;
    }
    if (centerOffset != null) {
      map["centerOffset"] = centerOffset!.toMap();
    }
    return map;
  }
}
class Offset {
  double x;
  double y;
  Offset(this.x, this.y);
  Map<String, Object?> toMap() {
    Map<String, Object?> map = {'x': x, 'y': y};
    return map;
  }
}
class MapClickEventBus {
  SimpleLocation? location;
  MapClickEventBus(this.location);
}
class CaptureEventBus {
  String path;
  CaptureEventBus(this.path);
}
class Coordinate {
  final double latitude;
  final double longitude;
  Coordinate(this.latitude, this.longitude);
}
lib/model/map/poi_model.dart
New file
@@ -0,0 +1,51 @@
import 'package:locations/model/map/location_model.dart';
class PoiModel {
  PoiModel({
    String? address,
    int? distance,
    String? phone,
    String? name,
    SimpleLocation? position,
  }) {
    _address = address;
    _distance = distance;
    _phone = phone;
    _name = name;
    _position = position;
  }
  PoiModel.fromJson(dynamic json) {
    _address = json['address'];
    _distance = int.parse(json['distance'].toString());
    _phone = json['phone'];
    _name = json['name'];
    _position = SimpleLocation.fromJson(json['position']);
  }
  String? _address;
  int? _distance;
  String? _phone;
  String? _name;
  SimpleLocation? _position;
  String? get address => _address;
  int? get distance => _distance;
  String? get phone => _phone;
  String? get name => _name;
  SimpleLocation? get position => _position;
  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['address'] = _address;
    map['distance'] = _distance;
    map['phone'] = _phone;
    map['name'] = _name;
    map['position'] = _position;
    return map;
  }
}
lib/model/user/user_info.dart
@@ -1,10 +1,16 @@
///用户信息
class UserInfo {
  UserInfo({int? id, String? nickName, String? portrait,  int? vipExpireTime}) {
  UserInfo(
      {int? id,
      String? nickName,
      String? portrait,
      int? vipExpireTime,
      bool? needAlwaysLocation}) {
    _id = id;
    _nickName = nickName;
    _portrait = portrait;
    _vipExpireTime = vipExpireTime;
    _needAlwaysLocation = needAlwaysLocation;
  }
  UserInfo.fromJson(dynamic json) {
@@ -12,12 +18,14 @@
    _nickName = json['nickName'];
    _portrait = json['portrait'];
    _vipExpireTime = json['vipExpireTime'];
    _needAlwaysLocation = json['needAlwaysLocation'];
  }
  int? _id;
  String? _nickName;
  String? _portrait;
  int? _vipExpireTime;
  bool? _needAlwaysLocation;
  int? get id => _id;
@@ -27,13 +35,15 @@
  int? get vipExpireTime => _vipExpireTime;
  bool? get needAlwaysLocation => _needAlwaysLocation;
  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['id'] = _id;
    map['nickName'] = _nickName;
    map['portrait'] = _portrait;
    map['vipExpireTime'] = _vipExpireTime;
    map['needAlwaysLocation'] = _needAlwaysLocation;
    return map;
  }
}
lib/ui/common/browser.dart
@@ -6,6 +6,7 @@
import 'package:locations/utils/jsinterface.dart';
import 'package:locations/utils/string_util.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
class BrowserPage extends StatefulWidget {
  BrowserPage({Key? key, required this.title, required this.url})
@@ -35,66 +36,99 @@
  WebViewController? _webViewController;
  _back() {
    if (_webViewController == null) {
      Navigator.of(context).pop();
    } else {
      _webViewController!.canGoBack().then((value) {
        if (value) {
          _webViewController!.goBack();
        } else {
          Navigator.of(context).pop();
        }
      });
    }
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.white,
        body: Flex(
          direction: Axis.vertical,
          children: [
            TopNavBar(title: title),
            SizedBox(
              height: 1,
              child: LinearProgressIndicator(
                backgroundColor: Colors.white,
                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/location/abountOur/tousu.html",
              onWebViewCreated: (WebViewController webViewController) {
                _webViewController = webViewController;
              },
              javascriptMode: JavascriptMode.unrestricted,
              javascriptChannels:
                  JavascriptInterface(context, _webViewController)
                      .getInterfaces(),
              navigationDelegate: (NavigationRequest request) {
                print("链接:${request.url}");
                if (!request.url.startsWith("http")) {
                  return NavigationDecision.prevent;
                }
                return NavigationDecision.navigate;
              },
              onPageStarted: (url) {
                print("process:onPageStarted-$url");
              },
              onPageFinished: (url) {
                print("process:onPageFinished-$url");
                _webViewController!.getTitle().then((value) {
                  if (value != null) {
                    setState(() {
                      title = value;
    return WillPopScope(
        onWillPop: () async {
          _back();
          return false;
        },
        child: Scaffold(
            backgroundColor: Colors.white,
            body: Flex(
              direction: Axis.vertical,
              children: [
                TopNavBar(
                  title: title,
                  back: () {
                    _back();
                  },
                  rightIcon: Image.asset(
                    "assets/images/common/icon_refresh.png",
                    width: 22,
                  ),
                  rightClick: (){
                    if(_webViewController!=null){
                      _webViewController!.reload();
                    }
                  },
                ),
                SizedBox(
                  height: 1,
                  child: LinearProgressIndicator(
                    backgroundColor: Colors.white,
                    valueColor: const AlwaysStoppedAnimation(Color(0xFF0E96FF)),
                    value: progress,
                  ),
                ),
                Expanded(
                    child: WebView(
                  //http://192.168.3.122:8848/test/JsTest.html
                  initialUrl: url,
                  onWebViewCreated: (WebViewController webViewController) {
                    _webViewController = webViewController;
                  },
                  javascriptMode: JavascriptMode.unrestricted,
                  javascriptChannels:
                      JavascriptInterface(context, _webViewController)
                          .getInterfaces(),
                  navigationDelegate: (NavigationRequest request) {
                    print("链接:${request.url}");
                    if (!request.url.startsWith("http")) {
                      launch(request.url);
                      return NavigationDecision.prevent;
                    }
                    return NavigationDecision.navigate;
                  },
                  onPageStarted: (url) {
                    print("process:onPageStarted-$url");
                  },
                  onPageFinished: (url) {
                    print("process:onPageFinished-$url");
                    _webViewController!.getTitle().then((value) {
                      if (value != null) {
                        setState(() {
                          title = value;
                        });
                      }
                    });
                  }
                });
              },
              onProgress: (int process) {
                print("process:$process");
                setState(() {
                  if (process == 100) {
                    progress = 0;
                  } else {
                    progress = process / 100.0;
                  }
                });
              },
            ))
          ],
        ));
                  },
                  onProgress: (int process) {
                    print("process:$process");
                    setState(() {
                      if (process == 100) {
                        progress = 0;
                      } else {
                        progress = process / 100.0;
                      }
                    });
                  },
                ))
              ],
            )));
  }
}
lib/ui/main/location.dart
@@ -5,11 +5,10 @@
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/model/map/location_user_model.dart';
import 'package:locations/model/map/map_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';
@@ -24,19 +23,20 @@
import 'package:locations/utils/event_bus_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/map_js_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';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:date_format/date_format.dart';
typedef OnPositionHiidenChange = void Function(bool hidden);
BMFMapController? _mapController;
WebViewController? _webViewController;
class LocationPage extends StatefulWidget {
  LocationPage({Key? key, required this.title}) : super(key: key);
@@ -61,8 +61,8 @@
    if (uid == null) {
      return;
    }
    Map<String, dynamic>? result =
    await LocationApiUtil.getInviteLocation(uid);
    print("getLocationInvite");
    Map<String, dynamic>? result = await LocationApiUtil.getInviteLocation(uid);
    if (result!["code"] == 0) {
      LocationUserModel model = LocationUserModel.fromJson(result["data"]);
      DialogUtil.showDialog(
@@ -91,23 +91,18 @@
class _LocationPageState extends State<LocationPage>
    with AutomaticKeepAliveClientMixin {
  GlobalKey rootWidgetKey = GlobalKey();
  CaptureController _captureController = CaptureController();
  final CaptureController _captureController = CaptureController();
  //用户marker
  BMFMarker? userMarker;
  Marker? userMarker;
  String? portrait;
  SimpleLocation? location;
  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: 200));
  //定位
  var eventBusLocation;
  //定位邀请
  var eventBusLocationInvite;
  //定位权限
  var eventBusLocationPermission;
@@ -117,6 +112,9 @@
  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) {
      WebView.platform = SurfaceAndroidWebView();
    }
    eventBusLocation = eventBus.on<UserLocationInfoEventBus>().listen((event) {
      //设置用户头像
      setState(() {
@@ -126,8 +124,13 @@
            : event.user!.portrait);
      });
      location = event.location;
      drawMarker(
          BMFCoordinate(event.location!.latitude!, event.location!.longitude!));
      drawMarker(event.location!);
    });
    eventBusLocationInvite =
        eventBus.on<LocationInviteEventBus>().listen((event) {
          print("接收到 LocationInviteEventBus");
      LocationPage.getLocationInvite(context);
    });
    eventBusLocationPermission =
@@ -146,13 +149,12 @@
    LocationPage.getLocationInvite(context);
  }
  @override
  void dispose() {
    super.dispose();
    (eventBusLocation as StreamSubscription).cancel();
    (eventBusLocationPermission as StreamSubscription).cancel();
    (eventBusLocationInvite as StreamSubscription).cancel();
  }
  @override
@@ -163,6 +165,30 @@
        body: Stack(
          children: [
            getMapView(),
            Positioned(
                left: 10,
                bottom: 80,
                child: InkWell(
                    onTap: () {
                      if (location == null) {
                        print("还没定到位");
                        LocationUtil.startLocation(0, (state, map) {
                          if (state == LocationState.success) {
                            location = map;
                            eventBus
                                .fire(UserLocationInfoEventBus(location, null));
                          }
                        });
                      } else {
                        //移动到中心点
                        MapUtil.setCenter(_webViewController, location!);
                      }
                    },
                    child: Image.asset(
                      "assets/images/map/icon_current_position.png",
                      width: 21,
                    ))),
            PositionInfoPage(),
            Positioned(
                top: -100,
@@ -174,7 +200,6 @@
                      width: 33,
                    ))),
            locationPermissionStatus != null &&
                    locationPermissionStatus != PermissionStatus.granted
                ? Align(
@@ -182,7 +207,7 @@
                    child: InkWell(
                        onTap: () {
                          PermissionUtil.openPermission(
                              PermissionUtil.getLocationPermission(),
                                  PermissionUtil.getLocationPermission(),
                                  force: true)
                              .then((value) {
                            setState(() {
@@ -249,38 +274,29 @@
    ];
  }
  Widget? _mapView;
  //获取地图视图
  Widget getMapView() {
    return Container(
      child: BMFMapWidget(
        onBMFMapCreated: (controller) {
          _mapController = controller;
          if (location != null) {
            drawMarker(
                BMFCoordinate(location!.latitude!, location!.longitude!));
          }
        },
        mapOptions: mapOptions,
      ),
    );
    _mapView ??= getMapWebView(context, (controller) {
      _webViewController = controller;
    });
    return _mapView!;
  }
  //画marker
  void drawMarker(BMFCoordinate location) async {
    if (_mapController == null) {
      return;
    }
  void drawMarker(SimpleLocation location) async {
    String base64Img = await _captureController.capturePng();
    if (userMarker == null) {
      userMarker = await MapUtil.addMarker(_mapController, location, base64Img);
      userMarker = await MapUtil.addMarker(
          _webViewController, location, base64Img,
          height: 40, width: 40, offset: Offset(0, 20));
    } else {
      userMarker!.updateIcon(base64Img);
      userMarker!.updatePosition(location);
      MapMarkerUtil.update(_webViewController, userMarker!.Id,
          icon: base64Img, position: location);
    }
    _mapController!.setCenterCoordinate(location, true);
    MapUtil.setCenter(_webViewController, location);
  }
  @override
  bool get wantKeepAlive => true;
@@ -318,8 +334,27 @@
  GlobalKey rootWidgetKey = GlobalKey();
  var eventBusLogin;
  var eventBusLocation;
  List<LocationUserModel>? userList;
  _startLocation() {
    //开始定位
    LocationUtil.startLocation(0, (state, map) {
      if (state == LocationState.success) {
        setState(() {
          _userLocationInfo = UserLocationInfo(
              uid: user != null ? user!.id : null,
              location: map,
              updateTime: formatDate(
                  DateTime.now(), [yyyy, '-', mm, '-', dd, ' ', HH, ':', nn]),
              locationCount: 1);
        });
        eventBus
            .fire(UserLocationInfoEventBus(_userLocationInfo!.location, user));
      }
    });
  }
  @override
  void initState() {
@@ -328,20 +363,8 @@
      setState(() {
        user = value;
      });
      //开始定位
      LocationUtil.startLocation(0, (state, map) {
        if (state == LocationState.success) {
          BaiduLocation location = BaiduLocation.fromJson(map);
          setState(() {
            _userLocationInfo = UserLocationInfo(
                uid: user != null ? user!.id : null,
                location: SimpleLocation.fromBaiDuLocation(location),
                updateTime: location.locTime,
                locationCount: 1);
          });
          eventBus.fire(
              UserLocationInfoEventBus(_userLocationInfo!.location, user));
        }
      Future.delayed(const Duration(seconds: 1), () {
        _startLocation();
      });
    });
    //注册eventbus
@@ -358,6 +381,19 @@
        });
      }
    });
    //注册eventbus
    eventBusLocation = eventBus.on<RequestLocationEventBus>().listen((event) {
      if (event.location) {
        _startLocation();
      }
      if (event.center &&
          _userLocationInfo != null &&
          _userLocationInfo!.location != null) {
        MapUtil.setCenter(_webViewController, _userLocationInfo!.location!);
      }
    });
  }
  @override
@@ -365,6 +401,7 @@
    super.dispose();
    //取消订阅
    eventBusLogin.cancel();
    eventBusLocation.cancel();
  }
  void _selectLocationUser() async {
@@ -730,14 +767,12 @@
                                      ]))))
                ],
              )),
          Expanded(child:
          Align(
              alignment: Alignment.bottomCenter,
              child: getAddLocationObjectView())),
          Expanded(
              child: Align(
                  alignment: Alignment.bottomCenter,
                  child: getAddLocationObjectView())),
        ]));
  }
  //添加想定位的人
  Widget getAddLocationObjectView() {
@@ -751,10 +786,9 @@
            }
            NavigatorUtil.navigateToNextPage(
                context, AddLocationPersonPage(title: ""), (data) {
                  if(data!=null) {
                    _setSelectUserData(data);
                  }
              if (data != null) {
                _setSelectUserData(data);
              }
            });
          });
        },
@@ -1041,3 +1075,10 @@
    }
  }
}
class RequestLocationEventBus {
  bool center;
  bool location;
  RequestLocationEventBus(this.center, this.location);
}
lib/ui/main/main.dart
@@ -12,6 +12,7 @@
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/event_bus_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
@@ -91,14 +92,40 @@
        TabController(length: _pages.length, initialIndex: 1, vsync: this);
    super.initState();
    AdUtil.getAdInfo(AdPosition.homeInterstitial).then((value) {
      AdUtil.loadInterstitial(value, (success, msg) {});
    Timer(const Duration(seconds: 1), () {
      AdUtil.getAdInfo(AdPosition.homeInterstitial).then((value) {
        AdUtil.loadInterstitial(value, (success, msg) {});
      });
    });
    UserUtil.updateUserInfo(context);
  }
  int lastCenterClick = 0;
  //设置选中的导航栏
  void setSelectIndex(int i) {
    if (i == selectIndex) {
      if (selectIndex == 1) {
        if (DateTime.now().millisecondsSinceEpoch - lastCenterClick < 400) {
          //双击
          eventBus.fire(RequestLocationEventBus(true, false));
          print("双击");
        } else {
          //单击
          Future.delayed(const Duration(milliseconds: 400), () {
            if (DateTime.now().millisecondsSinceEpoch - lastCenterClick <
                400) {
              return;
            }
            eventBus.fire(RequestLocationEventBus(true, true));
            print("单击");
          });
        }
        lastCenterClick = DateTime.now().millisecondsSinceEpoch;
      }
      return;
    }
    setState(() {
@@ -108,115 +135,127 @@
    _tabController!.animateTo(i);
  }
  int lastBack = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      resizeToAvoidBottomInset: false,
      body: Container(
        child: Stack(
          children: [
            //内容栏
            Container(
              color: Colors.white,
              child: Flex(
                direction: Axis.vertical,
                children: [
                  Expanded(
                      child: TabBarView(
                    controller: _tabController,
                    children: _pages,
                    physics: const NeverScrollableScrollPhysics(),
                  )),
                  Container(
                    height: 60,
                  )
                ],
              ),
            ),
    return WillPopScope(
        onWillPop: () async {
          if (DateTime.now().millisecondsSinceEpoch - lastBack > 1000 * 5) {
            lastBack = DateTime.now().millisecondsSinceEpoch;
            ToastUtil.toast("再按一次退出应用");
            return false;
          } else {
            return true;
          }
        },
        child: Scaffold(
          backgroundColor: Colors.white,
          resizeToAvoidBottomInset: false,
          body: Container(
            child: Stack(
              children: [
                //内容栏
                Container(
                  color: Colors.white,
                  child: Flex(
                    direction: Axis.vertical,
                    children: [
                      Expanded(
                          child: TabBarView(
                        controller: _tabController,
                        children: _pages,
                        physics: const NeverScrollableScrollPhysics(),
                      )),
                      Container(
                        height: 60,
                      )
                    ],
                  ),
                ),
            //底部导航栏
            Positioned(
                bottom: 0,
                child: Container(
                    child: Stack(
                  alignment: Alignment.bottomCenter,
                  children: [
                    Container(
                      height: 60,
                      width: MediaQuery.of(context).size.width,
                      alignment: Alignment.center,
                      decoration:
                          BoxDecoration(color: Colors.white, boxShadow: [
                        BoxShadow(
                          blurRadius: 10,
                          spreadRadius: 1,
                          color: Color(0x4D0E96FF),
                        )
                      ]),
                      child: Flex(
                        direction: Axis.horizontal,
                        children: [
                          Expanded(
                              child: getNavItem(
                                  icon: Image.asset(
                                    "assets/images/main/icon_main_nav_history.png",
                                    width: 31,
                                  ),
                                  iconHighlight: Image.asset(
                                    "assets/images/main/icon_main_nav_history_highlight.png",
                                    width: 31,
                                  ),
                                  text: "轨迹",
                                  index: 0)),
                          Container(
                            width: 54,
                //底部导航栏
                Positioned(
                    bottom: 0,
                    child: Container(
                        child: Stack(
                      alignment: Alignment.bottomCenter,
                      children: [
                        Container(
                          height: 60,
                          width: MediaQuery.of(context).size.width,
                          alignment: Alignment.center,
                          decoration:
                              BoxDecoration(color: Colors.white, boxShadow: [
                            BoxShadow(
                              blurRadius: 10,
                              spreadRadius: 1,
                              color: Color(0x4D0E96FF),
                            )
                          ]),
                          child: Flex(
                            direction: Axis.horizontal,
                            children: [
                              Expanded(
                                  child: getNavItem(
                                      icon: Image.asset(
                                        "assets/images/main/icon_main_nav_history.png",
                                        width: 31,
                                      ),
                                      iconHighlight: Image.asset(
                                        "assets/images/main/icon_main_nav_history_highlight.png",
                                        width: 31,
                                      ),
                                      text: "轨迹",
                                      index: 0)),
                              Container(
                                width: 54,
                              ),
                              Expanded(
                                  child: getNavItem(
                                      icon: Image.asset(
                                          "assets/images/main/icon_main_nav_mine.png",
                                          width: 31),
                                      iconHighlight: Image.asset(
                                          "assets/images/main/icon_main_nav_mine_highlight.png",
                                          width: 31),
                                      text: "我的",
                                      index: 2)),
                            ],
                          ),
                          Expanded(
                              child: getNavItem(
                                  icon: Image.asset(
                                      "assets/images/main/icon_main_nav_mine.png",
                                      width: 31),
                                  iconHighlight: Image.asset(
                                      "assets/images/main/icon_main_nav_mine_highlight.png",
                                      width: 31),
                                  text: "我的",
                                  index: 2)),
                        ],
                      ),
                    ),
                    Positioned(
                      child: Container(
                        height: 72,
                        width: 72,
                        decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: BorderRadius.circular(36),
                            boxShadow: [
                              BoxShadow(
                                  blurRadius: 10,
                                  spreadRadius: 1,
                                  color: Color(0x4D0E96FF))
                            ]),
                      ),
                    ),
                    Container(
                      height: 60,
                      width: 100,
                      color: Colors.white,
                    ),
                    Container(
                      height: 72,
                      width: 72,
                      alignment: Alignment.center,
                      child: getNavLocationItem(),
                    )
                  ],
                )))
          ],
        ),
      ),
    );
                        ),
                        Positioned(
                          child: Container(
                            height: 72,
                            width: 72,
                            decoration: BoxDecoration(
                                color: Colors.white,
                                borderRadius: BorderRadius.circular(36),
                                boxShadow: [
                                  BoxShadow(
                                      blurRadius: 10,
                                      spreadRadius: 1,
                                      color: Color(0x4D0E96FF))
                                ]),
                          ),
                        ),
                        Container(
                          height: 60,
                          width: 100,
                          color: Colors.white,
                        ),
                        Container(
                          height: 72,
                          width: 72,
                          alignment: Alignment.center,
                          child: getNavLocationItem(),
                        )
                      ],
                    )))
              ],
            ),
          ),
        ));
  }
  Widget getNavItem(
@@ -280,7 +319,7 @@
              Container(
                height: 3,
              ),
              Text(
              const Text(
                "定位",
                style: TextStyle(color: Colors.white, fontSize: 9),
              )
lib/ui/main/mine.dart
@@ -78,10 +78,10 @@
    Functions("assets/images/mine/icon_mine_kefu.png", "在线客服", "kefu", false),
    Functions(
        "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_course.png", "使用教程", "course", false),
    // Functions("assets/images/mine/icon_mine_functions.png", "功能使用", "functions",
    //     true),
    // Functions(
    //     "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),
@@ -97,8 +97,9 @@
    if (function.needLogin) {
      bool login = await UserUtil.isLogin();
      if (!login) {
        NavigatorUtil.navigateToNextPage(
            context, LoginPage(title: ""), (data) {});
        NavigatorUtil.navigateToNextPage(context, LoginPage(title: ""), (data) {
          _getUserInfo();
        });
        return;
      }
    }
@@ -123,8 +124,10 @@
            context, MyTravelPage(uid: uid!), (data) {});
        break;
      case "functions":
        NavigatorUtil.navigateToNextPage(
            context, TryFunctionsPage(title: ""), (data) {});
        NavigatorUtil.navigateToNextPage(context, TryFunctionsPage(title: ""),
            (data) {
          _getUserInfo();
        });
        break;
      case "course":
        ConfigUtil.getConfig(ConfigKey.course).then((value) {
@@ -211,10 +214,10 @@
      return;
    }
    Widget? ad = AdUtil.loadExpress(
        await AdUtil.getAdInfo(AdPosition.mineExpress), MediaQuery.of(context).size.width - 20, 200,
        (success, msg) {
        await AdUtil.getAdInfo(AdPosition.mineExpress),
        MediaQuery.of(context).size.width - 20,
        200, (success, msg) {
      adDeleted = true;
      setState(() {
        adView = null;
@@ -231,6 +234,7 @@
    if (user == null) {
      setState(() {
        isLogin = false;
        userInfo = null;
      });
      return;
    }
@@ -244,12 +248,13 @@
    var code = result!["code"];
    if (code == 0) {
      UserInfo user = UserInfo.fromJson(result["data"]);
      print(user);
      //保存用户信息
      UserUtil.setUserInfo(user);
      setState(() {
        userInfo = user;
        if (userInfo != null &&
            userInfo!.vipExpireTime != null &&
            userInfo!.vipExpireTime! > DateTime.now().millisecond) {
            userInfo!.vipExpireTime! > DateTime.now().millisecondsSinceEpoch) {
          isVIP = true;
        } else {
          isVIP = false;
@@ -261,12 +266,14 @@
        isLogin = false;
      });
      ToastUtil.toast("账号已被封禁");
      await UserUtil.logout();
    } else if (code == 80002) {
      //账号被删除
      setState(() {
        isLogin = false;
      });
      ToastUtil.toast("账号已被删除");
      await UserUtil.logout();
    }
  }
@@ -323,24 +330,46 @@
    );
  }
  Widget _getVIPLeftTime() {
    List<TextSpan> spanList = [const TextSpan(text: '剩余')];
    if (userInfo != null && userInfo!.vipExpireTime != null) {
      int leftTime =
          userInfo!.vipExpireTime! - DateTime.now().millisecondsSinceEpoch;
      leftTime = leftTime ~/ 1000;
      if (leftTime >= 60 * 60 * 24) {
        spanList.add(TextSpan(
            text: '${leftTime ~/ (60 * 60 * 24)}',
            style: const TextStyle(fontWeight: FontWeight.bold)));
        spanList.add(const TextSpan(text: '天到期'));
      } else if (leftTime >= 60 * 60) {
        spanList.add(TextSpan(
            text: '${leftTime ~/ (60 * 60)}',
            style: const TextStyle(fontWeight: FontWeight.bold)));
        spanList.add(const TextSpan(text: '小时到期'));
      } else {
        spanList.add(TextSpan(
            text: '${leftTime ~/ (60)}',
            style: const TextStyle(fontWeight: FontWeight.bold)));
        spanList.add(const TextSpan(text: '分钟到期'));
      }
    }
    return Text.rich(TextSpan(
      children: spanList,
      style: const TextStyle(color: Colors.white, fontSize: 17.5),
    ));
  }
  List<Widget> getVIPContent() {
    List<Widget> list = [];
    list.add(Image.asset(
      "assets/images/mine/icon_vip.png",
      width: 44,
    ));
    if (isVIP) {
    if (isVIP&&userInfo!=null) {
      list.add(Container(
          margin: const EdgeInsets.fromLTRB(6, 0, 0, 0),
          child: const Text.rich(TextSpan(
            children: <TextSpan>[
              TextSpan(text: '剩余'),
              TextSpan(
                  text: '10', style: TextStyle(fontWeight: FontWeight.bold)),
              TextSpan(text: '天到期'),
            ],
            style: TextStyle(color: Colors.white, fontSize: 17.5),
          ))));
          child: _getVIPLeftTime()));
    } else {
      list.add(Container(
        margin: const EdgeInsets.fromLTRB(6, 0, 0, 0),
@@ -448,9 +477,10 @@
                                        UserUtil.isLogin().then((value) {
                                          if (!value) {
                                            NavigatorUtil.navigateToNextPage(
                                                context,
                                                LoginPage(title: ""),
                                                (data) {});
                                                context, LoginPage(title: ""),
                                                (data) {
                                              _getUserInfo();
                                            });
                                            return;
                                          }
@@ -462,8 +492,10 @@
                                              NavigatorUtil.navigateToNextPage(
                                                  context,
                                                  BrowserPage(
                                                      title: "会员", url: value!),
                                                  (data) {});
                                                      title: "会员",
                                                      url: value!), (data) {
                                                _getUserInfo();
                                              });
                                            }
                                          });
                                        });
lib/ui/main/travel_main.dart
@@ -5,22 +5,23 @@
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:flutter_baidu_mapapi_search/flutter_baidu_mapapi_search.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:locations/model/map/location_model.dart';
import 'package:locations/model/map/map_model.dart';
import 'package:locations/ui/map/location_search.dart';
import 'package:locations/ui/map/travel.dart';
import 'package:locations/ui/mine/login.dart';
import 'package:locations/ui/widget/button.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/map_js_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'package:path_provider/path_provider.dart';
import 'package:webview_flutter/webview_flutter.dart';
//控件阴影
List<BoxShadow> getViewShadow() {
@@ -40,16 +41,7 @@
class _MainTravelPageState extends State<MainTravelPage>
    with SingleTickerProviderStateMixin {
  BMFMapController? _mapController;
  BMFMapOptions mapOptions = BMFMapOptions(
      showMapScaleBar: false,
      mapType: BMFMapType.Standard,
      center: Global.currentPosition != null
          ? Global.currentPosition
          : BMFCoordinate(39.917215, 116.380341),
      mapScaleBarPosition: BMFPoint(0, 200),
      zoomLevel: 12,
      mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 200));
  WebViewController? _webViewController;
  //选择地图类型索引(0-2D 1-3D 2-卫星)
  int selectMapTypeIndex = 0;
@@ -60,20 +52,23 @@
  TravelRECManager? _travelRECManager;
  //用戶目前的位置
  BMFCoordinate? currentPosition;
  SimpleLocation? currentPosition;
  AnimationController? _animationController;
  Animation<double>? _animation;
  BMFMarker? userMarker;
  Marker? userMarker;
  //当前用户头像
  CaptureController _captureController = CaptureController();
  final CaptureController _captureController = CaptureController();
  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) {
      WebView.platform = SurfaceAndroidWebView();
    }
    _animationController = AnimationController(
        duration: const Duration(milliseconds: 300), vsync: this);
    final CurvedAnimation curve =
@@ -87,63 +82,75 @@
  @override
  void dispose() {
    super.dispose();
    if (null != subscription) {
      subscription!.cancel();
    if (timer != null) {
      timer!.cancel();
      timer = null;
    }
    LocationUtil.stopLocation();
  }
  WebView? _webview;
  //获取地图视图
  Widget getMapView() {
    return Container(
      child: BMFMapWidget(
        onBMFMapCreated: (controller) {
          _mapController = controller;
          _travelRECManager = TravelRECManager(_mapController);
          _startLocation(0);
        },
        mapOptions: mapOptions,
      ),
    );
    _webview ??= getMapWebView(context, (controller) {
      _webViewController = controller;
      _travelRECManager = TravelRECManager(controller);
    });
    return _webview!;
  }
  StreamSubscription<Map<String, Object>?>? subscription;
  Timer? timer;
  void _startLocation(int scanspan) async {
    //启动定位
    if (subscription != null) {
      subscription!.cancel();
    }
    subscription = await LocationUtil.startLocation(scanspan, (state, map) {
      if (state == LocationState.success) {
        double lat = map!["latitude"] as double;
        double lng = map["longitude"] as double;
        currentPosition = BMFCoordinate(lat, lng);
        _mapController!.setCenterCoordinate(BMFCoordinate(lat, lng), true);
        setCurrentPosition(BMFCoordinate(lat, lng));
        _travelRECManager!.addLocation(BMFCoordinate(lat, lng));
    if (scanspan > 0) {
      if (timer != null) {
        timer!.cancel();
        timer = null;
      }
    });
      timer = Timer(Duration(milliseconds: scanspan), () {
        //启动定位
        LocationUtil.startLocation(scanspan, (state, map) {
          if (state == LocationState.success) {
            currentPosition = map;
            MapUtil.setCenter(_webViewController, map!);
            setCurrentPosition(currentPosition!);
            _travelRECManager!.addLocation(map);
          }
        });
      });
    } else {
      //启动定位
      await LocationUtil.startLocation(scanspan, (state, map) {
        if (state == LocationState.success) {
          currentPosition = map;
          MapUtil.setCenter(_webViewController, map!);
          setCurrentPosition(currentPosition!);
          _travelRECManager!.addLocation(map);
        }
      });
    }
  }
  void stopLocation() async {
    if (subscription != null) {
      subscription!.cancel();
    if (timer != null) {
      timer!.cancel();
      timer = null;
    }
    LocationUtil.stopLocation();
  }
  setCurrentPosition(BMFCoordinate position) async {
  setCurrentPosition(SimpleLocation position) async {
    if (userMarker == null) {
      _captureController.capturePng().then((value) {
        MapUtil.addMarker(_mapController, position, value,
                offset: BMFPoint(0, 40), zIndex: 10)
        MapUtil.addMarker(_webViewController, position, value,
                offset: Offset(0, 20), zIndex: 10, width: 40, height: 40)
            .then((value) {
          userMarker = value;
        });
      });
    } else {
      await userMarker!.updatePosition(position);
      await MapMarkerUtil.update(_webViewController, userMarker!.Id,
          position: position);
    }
  }
@@ -210,6 +217,12 @@
                  onTap: () {
                    UserUtil.getUid().then((value) {
                      if (value == null) {
                        NavigatorUtil.navigateToNextPage(
                            context,
                            LoginPage(
                              title: "",
                            ),
                            (data) {});
                        return;
                      }
                      NavigatorUtil.navigateToNextPage(
@@ -326,24 +339,18 @@
  Widget getMapTypeView(String name, String imgPath, int index) {
    return InkWell(
        onTap: () {
          if (selectMapTypeIndex == index) {
            return;
          }
          setState(() {
            selectMapTypeIndex = index;
          });
          if (index == 2) {
            _mapController?.updateMapOptions(
                BMFMapOptions(mapType: BMFMapType.Satellite));
            MapUtil.setStarLayer(_webViewController);
          } else if (index == 1) {
            _mapController?.updateMapOptions(BMFMapOptions(
                mapType: BMFMapType.Standard,
                buildingsEnabled: true,
                baseIndoorMapEnabled: true,
                overlookEnabled: true));
            _mapController?.showBaseIndoorMap(true);
            MapUtil.setCommonLayer(_webViewController);
          } else if (index == 0) {
            _mapController?.updateMapOptions(BMFMapOptions(
                mapType: BMFMapType.Standard,
                buildingsEnabled: false,
                baseIndoorMapEnabled: false));
            MapUtil.setCommonLayer(_webViewController);
          }
        },
        child: Column(
@@ -394,12 +401,11 @@
                      //开始定位
                      LocationUtil.startLocation(0, (state, map) {
                        if (state == LocationState.success) {
                          double lat = map!["latitude"] as double;
                          double lng = map["longitude"] as double;
                          currentPosition = BMFCoordinate(lat, lng);
                          _mapController!.setCenterCoordinate(
                              BMFCoordinate(lat, lng), true);
                          setCurrentPosition(BMFCoordinate(lat, lng));
                          currentPosition = map;
                          MapUtil.setCenter(
                              _webViewController, currentPosition!);
                          setCurrentPosition(currentPosition!);
                          //开始录制
                          _travelRECManager!.startREC(currentPosition!);
@@ -463,7 +469,7 @@
                      Container(
                        height: 2,
                      ),
                      Text("选择图层",
                      const Text("选择图层",
                          style:
                              TextStyle(fontSize: 7, color: Color(0xFF9DAAB3)))
                    ],
@@ -474,14 +480,13 @@
                ),
                InkWell(
                  onTap: () {
                    if(travelRECIng){
                    if (travelRECIng) {
                      //正在录制轨迹
                      if( Global.currentPosition!=null) {
                        _mapController!.updateMapOptions(BMFMapOptions(center:
                        Global.currentPosition
                        ));
                      if (Global.currentPosition != null) {
                        MapUtil.setCenter(
                            _webViewController, Global.currentPosition!);
                      }
                    }else {
                    } else {
                      _startLocation(0);
                    }
                  },
@@ -494,7 +499,7 @@
                      Container(
                        height: 2,
                      ),
                      Text("我的位置",
                      const Text("我的位置",
                          style:
                              TextStyle(fontSize: 7, color: Color(0xFF9DAAB3)))
                    ],
lib/ui/map/location_search.dart
@@ -1,20 +1,25 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.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:flutter_baidu_mapapi_search/flutter_baidu_mapapi_search.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/map/location_model.dart';
import 'package:locations/model/map/map_model.dart';
import 'package:locations/model/map/poi_model.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/map_js_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';
import 'package:webview_flutter/webview_flutter.dart';
//控件阴影
List<BoxShadow> getViewShadow() {
@@ -35,16 +40,19 @@
class _LocationSearchPageState extends State<LocationSearchPage>
    with SingleTickerProviderStateMixin {
  BMFMapController? _mapController;
  BMFMapOptions mapOptions = BMFMapOptions(
      showMapScaleBar: false,
      mapType: BMFMapType.Standard,
      center: Global.currentPosition != null
          ? Global.currentPosition
          : BMFCoordinate(39.917215, 116.380341),
      mapScaleBarPosition: BMFPoint(0, 200),
      zoomLevel: 12,
      mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 200));
  WebViewController? _webViewController;
  // BMFMapOptions mapOptions = BMFMapOptions(
  //     showMapScaleBar: false,
  //     mapType: BMFMapType.Standard,
  //     center: Global.currentPosition != null
  //         ? Global.currentPosition
  //         : BMFCoordinate(39.917215, 116.380341),
  //     mapScaleBarPosition: BMFPoint(0, 200),
  //     zoomLevel: 14,
  //     rotateEnabled: false,
  //     compassPosition: BMFPoint(20, 20),
  //     mapPadding: BMFEdgeInsets(left: 40, top: 0, right: 40, bottom: 0));
  //1-搜索页面  2-建议搜索页面 3-结果展示页面
  int state = 1;
@@ -52,20 +60,22 @@
  Widget? expressAd;
  TextEditingController? editingController;
  List<BMFPoiInfo>? suggestList;
  List<BMFPoiInfo>? recordList;
  BMFCoordinate? currentPosition;
  List<PoiModel>? suggestList;
  List<PoiModel>? recordList;
  //选中位置信息
  BMFPoiInfo? selectedPoiInfo;
  PoiModel? selectedPoiInfo;
  BMFMarker? selectedPoiInfoMarker;
  Marker? selectedPoiInfoMarker;
  bool deleteAd = false;
  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) {
      WebView.platform = SurfaceAndroidWebView();
    }
    editingController = TextEditingController();
    loadRecord();
  }
@@ -97,22 +107,14 @@
    });
  }
  WebView? _webview;
  //获取地图视图
  Widget getMapView() {
    return Container(
      child: BMFMapWidget(
        onBMFMapCreated: (controller) {
          _mapController = controller;
          LocationUtil.startLocation(0, (state, map) {
            if (state == LocationState.success) {
              currentPosition = BMFCoordinate.fromMap(map!);
              _mapController!.setCenterCoordinate(currentPosition!, true);
            }
          });
        },
        mapOptions: mapOptions,
      ),
    );
    _webview ??= getMapWebView(context, (controller) {
      _webViewController = controller;
    });
    return _webview!;
  }
  @override
@@ -182,7 +184,7 @@
                      if (state == 3) {
                        if (selectedPoiInfoMarker != null) {
                          MapUtil.removeMarker(
                              _mapController, selectedPoiInfoMarker!);
                              _webViewController, selectedPoiInfoMarker!);
                          selectedPoiInfoMarker = null;
                        }
                        setState(() {
@@ -413,6 +415,9 @@
  // }
  startPOISearch(String key) async {
    if (Global.currentPosition == null) {
      return;
    }
    setState(() {
      suggestList = null;
      if (key.isNotEmpty)
@@ -421,37 +426,19 @@
        state = 1;
    });
    BMFPoiNearbySearchOption poiNearbySearchOption = BMFPoiNearbySearchOption(
        keywords: <String>[key],
        location: currentPosition != null
            ? currentPosition
            : BMFCoordinate(29.674509, 106.517571),
        radius: 10000,
        isRadiusLimit: true);
    BMFPoiNearbySearch nearbySearch = BMFPoiNearbySearch();
    nearbySearch.onGetPoiNearbySearchResult(
        callback: (BMFPoiSearchResult result, BMFSearchErrorCode errorCode) {
      print("搜索结果回调:$result");
      setState(() {
        suggestList = result.poiInfoList;
    Map<String, dynamic>? result = await LocationApiUtil.searchNearBy(key,
        Global.currentPosition!.latitude!, Global.currentPosition!.longitude!);
    if (result != null) {
      List<dynamic> list = result["data"]["list"];
      List<PoiModel> clist = [];
      list.forEach((element) {
        clist.add(PoiModel.fromJson(element));
      });
    });
    bool flag = await nearbySearch.poiNearbySearch(poiNearbySearchOption);
    // BMFSuggestionSearchOption suggestionSearchOption =
    //     BMFSuggestionSearchOption(keyword: key,cityLimit: false);
    // BMFSuggestionSearch suggestionSearch = BMFSuggestionSearch();
    // suggestionSearch.onGetSuggestSearchResult(callback:
    //     (BMFSuggestionSearchResult result, BMFSearchErrorCode errorCode) {
    //   print("result:$result errorCode:$errorCode");
    //   setState(() {
    //     suggestList = result.suggestionList;
    //   });
    // });
    // bool flag = await suggestionSearch.suggestionSearch(suggestionSearchOption);
    print("结束$flag");
      setState(() {
        suggestList = clist;
      });
    }
  }
  //建议搜索视图
@@ -485,14 +472,7 @@
        ));
  }
  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();
  void _showPOIDetail(PoiModel info) async {
    //显示地址详情
    SearchUtil.addSearchRecord(info);
    setState(() {
@@ -502,21 +482,24 @@
    //添加marker
    if (selectedPoiInfoMarker == null) {
      MapUtil.addMarker(_mapController, BMFCoordinate.fromMap(info.pt!.toMap()),
              "assets/images/common/icon_location_marker.png")
      ByteData byteData = await rootBundle
          .load("assets/images/common/icon_location_marker.png");
      Uint8List pngBytes = byteData.buffer.asUint8List();
      MapUtil.addMarker(
              _webViewController, info.position!, base64.encode(pngBytes),
              width: 43, height: 60)
          .then((value) {
        selectedPoiInfoMarker = value;
      });
    } else {
      selectedPoiInfoMarker!
          .updatePosition(BMFCoordinate.fromMap(info.pt!.toMap()));
      MapMarkerUtil.update(_webViewController, selectedPoiInfoMarker!.Id,
          position: info.position);
    }
    _mapController!
        .setCenterCoordinate(BMFCoordinate.fromMap(info.pt!.toMap()), true);
    MapUtil.setCenter(_webViewController, info.position!);
  }
  //历史记录项目
  Widget getHistoryItem(String title, String content, BMFPoiInfo info,
  Widget getHistoryItem(String title, String content, PoiModel info,
      {bool showDistance = false}) {
    return InkWell(
        onTap: () {
lib/ui/map/map.dart
@@ -1,29 +1,29 @@
import 'dart:async';
import 'dart:io';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package: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/model/map/map_model.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/event_bus_util.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/map_js_util.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:event_bus/event_bus.dart';
//控件阴影
List<BoxShadow> getViewShadow() {
  return [
    BoxShadow(
    const BoxShadow(
      blurRadius: 6.5,
      spreadRadius: 1,
      color: Color(0x4D0E96FF),
@@ -56,14 +56,7 @@
  _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));
  WebViewController? _webViewController;
  //选择地图类型索引(0-2D 1-3D 2-卫星)
  int selectMapTypeIndex = 0;
@@ -82,13 +75,13 @@
  SimpleLocation? _currentTargetPosition;
  BMFMarker? myMarker;
  Marker? myMarker;
  BMFMarker? targetUserMarker;
  Marker? targetUserMarker;
  BMFMarker? myAddressMarker;
  Marker? myAddressMarker;
  BMFMarker? targetAddressUserMarker;
  Marker? targetAddressUserMarker;
  //是否显示地址信息
  bool _showAddress = true;
@@ -101,6 +94,8 @@
  Timer? _timer;
  //
  final CaptureController _captureMyController = CaptureController();
  final CaptureController _captureTargetController = CaptureController();
@@ -109,7 +104,21 @@
  double? pixelRatio;
  var eventBusMapClick;
  void _init() {
    if (location != null) {
      MapUtil.setCenter(_webViewController, location!);
    }
    eventBusMapClick = eventBus.on<MapClickEventBus>().listen((event) {
      //测距
      if (_mesure) {
        _measureManager!.addPoint(event.location!).then((value) {
          _showDistance(value);
        });
      }
    });
    //定位
    LocationUtil.startLocation(0, (state, map) {
      if (state == LocationState.success) {
@@ -126,12 +135,10 @@
        _showTargetLocationMarker();
        //设置地图中心点与缩放等级
        if (_currentTargetPosition != null && _currentMyPosition != null) {
          MapUtil.getMapShowParams([
            _currentTargetPosition!.toBMFCoordinate(),
            _currentMyPosition!.toBMFCoordinate()
          ]).then((value) {
            _mapController!.updateMapOptions(BMFMapOptions(
                zoomLevel: value.zoomLevel, center: value.center));
          MapUtil.getMapShowParams(
              [_currentTargetPosition!, _currentMyPosition!]).then((value) {
            MapUtil.setCenter(_webViewController, value.center);
            MapUtil.setZoom(_webViewController, value.zoomLevel);
          });
        }
      });
@@ -149,8 +156,7 @@
          });
          if (travelRECIng) {
            _travelRECManager!.addLocation(BMFCoordinate(
                value.location!.latitude!, value.location!.longitude!));
            _travelRECManager!.addLocation(value.location!);
          }
          //设置现在的位置
@@ -165,31 +171,40 @@
    if (_currentTargetPosition != null) {
      //画头像
      if (targetUserMarker != null) {
        targetUserMarker!
            .updatePosition(_currentTargetPosition!.toBMFCoordinate());
        MapMarkerUtil.update(_webViewController, targetUserMarker!.Id,
            position: _currentTargetPosition);
      } else {
        String value = await _captureTargetController.capturePng();
        if (StringUtil.isNullOrEmpty(value)) {
          return;
        }
        targetUserMarker = await MapUtil.addMarker(
            _mapController, _currentTargetPosition!.toBMFCoordinate(), value);
            _webViewController, _currentTargetPosition!, value,
            width: 40, height: 40);
      }
      //画地址
      if (targetAddressUserMarker != null) {
        targetAddressUserMarker!
            .updatePosition(_currentTargetPosition!.toBMFCoordinate());
        MapMarkerUtil.update(_webViewController, targetAddressUserMarker!.Id,
            position: _currentTargetPosition);
        String value = await _captureTargetAddressController.capturePng();
        await targetAddressUserMarker!.updateIcon(value);
        await MapMarkerUtil.update(
            _webViewController, targetAddressUserMarker!.Id,
            icon: 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));
            _webViewController, _currentTargetPosition!, value,
            offset: Offset(0, 36 + 2), width: 132, height: 36);
      }
      //只有一个人
      if (myMarker == null) {
        MapUtil.setCenter(_webViewController, _currentTargetPosition!);
      }
    }
  }
@@ -204,23 +219,27 @@
    if (_currentMyPosition != null) {
      //画头像
      if (myMarker != null) {
        myMarker!.updatePosition(_currentMyPosition!.toBMFCoordinate());
        MapMarkerUtil.update(_webViewController, myMarker!.Id,
            position: _currentMyPosition);
      } else {
        String value = await _captureMyController.capturePng();
        myMarker = await MapUtil.addMarker(
            _mapController, _currentMyPosition!.toBMFCoordinate(), value);
            _webViewController, _currentMyPosition!, value,
            width: 40, height: 40);
      }
      //画地址
      if (myAddressMarker != null) {
        myAddressMarker!.updatePosition(_currentMyPosition!.toBMFCoordinate());
        MapMarkerUtil.update(_webViewController, myAddressMarker!.Id,
            position: _currentMyPosition);
        String value = await _captureMyAddressController.capturePng();
        myAddressMarker!.updateIcon(value);
        MapMarkerUtil.update(_webViewController, myAddressMarker!.Id,
            icon: value);
      } else {
        String value = await _captureMyAddressController.capturePng();
        myAddressMarker = await MapUtil.addMarker(
            _mapController, _currentMyPosition!.toBMFCoordinate(), value,
            offset: BMFPoint(0, 64 * 2));
            _webViewController, _currentMyPosition!, value,
            offset: Offset(0, 36 + 2), width: 132, height: 36);
      }
    }
  }
@@ -228,16 +247,18 @@
  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) {
      WebView.platform = SurfaceAndroidWebView();
    }
    _currentTargetPosition = location;
  }
  @override
  void dispose() {
    super.dispose();
    if (null != subscription) {
      subscription!.cancel();
    if (eventBusMapClick != null) {
      eventBusMapClick.cancel();
    }
    LocationUtil.stopLocation();
    if (_timer != null) {
      _timer!.cancel();
    }
@@ -259,47 +280,45 @@
    }
  }
  WebView? _webView;
  //获取地图视图
  Widget getMapView() {
    return Container(
      child: BMFMapWidget(
        onBMFMapCreated: (controller) {
          _mapController = controller;
          _travelRECManager = TravelRECManager(_mapController);
          _measureManager = MeasureManager(_mapController);
    _webView ??= getMapWebView(context, (controller) {
      _webViewController = controller;
          _init();
      //TODO 测量模式
          _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,
      ),
    );
      // _webViewController?.setMapOnClickedMapPoiCallback(
      //     callback: (BMFMapPoi poi) {
      //       //是否为测量模式
      //       if (!_mesure) {
      //         return;
      //       }
      //       _measureManager!.addPoint(poi.pt!).then((value) {
      //         _showDistance(value);
      //       });
      //     });
      // _webViewController?.setMapOnClickedMapBlankCallback(
      //     callback: (BMFCoordinate coordinate) {
      //       //是否为测量模式
      //       if (!_mesure) {
      //         return;
      //       }
      //
      //       print("地图点击:${coordinate.toString()}");
      //
      //       _measureManager!.addPoint(coordinate).then((value) {
      //         _showDistance(value);
      //       });
      //     });
    }, pageFinish: (url) {
      _travelRECManager = TravelRECManager(_webViewController);
      _measureManager = MeasureManager(_webViewController);
      _init();
    });
    return _webView!;
  }
  StreamSubscription<Map<String, Object>?>? subscription;
  @override
  Widget build(BuildContext context) {
@@ -560,58 +579,6 @@
        ]));
  }
  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
@@ -620,7 +587,7 @@
                right: 10,
                top: 10,
                child: Container(
                  padding: EdgeInsets.only(top: 13, bottom: 13),
                  padding: const EdgeInsets.only(top: 13, bottom: 13),
                  width: 43,
                  decoration: BoxDecoration(
                      color: Colors.white,
@@ -635,8 +602,8 @@
                              return;
                            }
                            _travelRECManager!.startREC(
                                _currentTargetPosition!.toBMFCoordinate());
                            _travelRECManager!
                                .startREC(_currentTargetPosition!);
                          } else {
                            //结束录制
                            _travelRECManager!.stopREC();
@@ -672,12 +639,15 @@
                      InkWell(
                        onTap: () {
                          if (myAddressMarker != null) {
                            myAddressMarker!.updateVisible(!_showAddress);
                            MapMarkerUtil.update(
                                _webViewController, myAddressMarker!.Id,
                                visibility: !_showAddress);
                          }
                          if (targetAddressUserMarker != null) {
                            targetAddressUserMarker!
                                .updateVisible(!_showAddress);
                            MapMarkerUtil.update(
                                _webViewController, targetAddressUserMarker!.Id,
                                visibility: !_showAddress);
                          }
                          setState(() {
lib/ui/map/travel.dart
@@ -7,26 +7,28 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.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:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/map/location_model.dart';
import 'package:locations/model/map/map_model.dart';
import 'package:locations/ui/widget/base_ui.dart';
import 'package:locations/ui/widget/button.dart';
import 'package:locations/ui/widget/map_marker.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/utils/ad_util.dart';
import 'package:locations/utils/event_bus_util.dart';
import 'package:locations/utils/global.dart';
import 'package:locations/utils/map_util.dart';
import 'package:locations/utils/map_js_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/share_utils.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'package:path_provider/path_provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:share_plus/share_plus.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:webview_flutter/webview_flutter.dart';
class MyTravelPage extends StatefulWidget {
  final int? uid;
@@ -430,7 +432,7 @@
  //控件阴影
  List<BoxShadow> getViewShadow() {
    return [
      BoxShadow(
      const BoxShadow(
        blurRadius: 6.5,
        spreadRadius: 1,
        color: Color(0x4D0E96FF),
@@ -453,19 +455,22 @@
  final List<UserLocationInfo> locationList;
  BMFMapOptions mapOptions = BMFMapOptions(
      showMapScaleBar: false,
      mapType: BMFMapType.Standard,
      center: Global.currentPosition ?? BMFCoordinate(39.917215, 116.380341),
      mapScaleBarPosition: BMFPoint(0, 200),
      zoomLevel: 12,
      zoomEnabled: false,
      zoomEnabledWithTap: false,
      scrollEnabled: false,
      //不支持手势
      gesturesEnabled: false,
      mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 0));
  BMFMapController? _mapController;
  //
  // BMFMapOptions mapOptions = BMFMapOptions(
  //     showMapScaleBar: false,
  //     mapType: BMFMapType.Standard,
  //     center: Global.currentPosition ?? BMFCoordinate(39.917215, 116.380341),
  //     mapScaleBarPosition: BMFPoint(0, 200),
  //     zoomLevel: 14,
  //     zoomEnabled: false,
  //     zoomEnabledWithTap: false,
  //     scrollEnabled: false,
  //     rotateEnabled: false,
  //     compassPosition: BMFPoint(20, 20),
  //     //不支持手势
  //     gesturesEnabled: false,
  //     mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 0));
  WebViewController? _webViewController;
  //截图组件
  GlobalKey rootWidgetKey = GlobalKey();
@@ -479,9 +484,16 @@
  double? mapHeight;
  bool cancap = false;
  var eventBusCapture;
  String? sharePlatformName;
  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) {
      WebView.platform = SurfaceAndroidWebView();
    }
    controller = AnimationController(
        duration: const Duration(milliseconds: 300), vsync: this);
    final CurvedAnimation curve =
@@ -490,89 +502,81 @@
      ..addListener(() {
        setState(() {});
      });
    Timer(const Duration(seconds: 1), () {
      AdUtil.getAdInfo(AdPosition.travelShareInterstitial).then((value) {
        //插屏广告
        AdUtil.loadInterstitial(value, (success, msg) {});
      });
    });
    AdUtil.getAdInfo(AdPosition.travelShareInterstitial).then((value) {
      //插屏广告
      AdUtil.loadInterstitial(value, (success, msg) {});
    //监听地图截图
    eventBusCapture = eventBus.on<CaptureEventBus>().listen((event) {
      //  event为 event.obj 即为 eventBus.dart 文件中定义的 EventFn 类中监听的数据
      print("收到地图截图消息:" + event.path);
      setState(() {
        mapPicturePath = event.path;
        Future.delayed(Duration(milliseconds: 100), () {
          Future<File?> future = _capturePng();
          future.then((value) {
            if (sharePlatformName.toString().contains("QQ")) {
              ShareUtil.shareImg(context, value!, SharePlatform.qq);
            } else if (sharePlatformName.toString().contains("微信")) {
              ShareUtil.shareImg(context, value!, SharePlatform.wx);
            } else if (sharePlatformName.toString().contains("微博")) {
              ShareUtil.shareImg(context, value!, SharePlatform.sina);
            }
          });
        });
      });
    });
  }
  WebView? _webView;
  //获取地图视图
  Widget getMapView() {
    _webView ??= getMapWebView(context, (controller) {
      _webViewController = controller;
    }, pageFinish: (url) {
      //绘制路径
      List<SimpleLocation> points = [];
      locationList.forEach((element) {
        points.add(element.location!);
      });
      MapUtil.drawLine(points, ColorConstant.theme, _webViewController)
          .then((value) {
        //画起始点与结束点
        MapMarkerUtil.addTravelStartMarker(_webViewController, points[0])
            .then((value) {});
        MapMarkerUtil.addTravelEndMarker(
                _webViewController, points[points.length - 1])
            .then((value) {})
            .then((value) {});
        //画文字
        if (points.length > 1) {
          MapMarkerUtil.addText(_webViewController, points[0],
              locationList[0].location!.addressDetail!);
        }
        MapMarkerUtil.addText(_webViewController, points[points.length - 1],
                locationList[points.length - 1].location!.addressDetail!,
                bgColor: const Color(0xAAFF1F35))
            .then((value) {
          cancap = true;
        });
      });
      //设置中心点
      MapUtil.getMapShowParams(points).then((value) {
        MapUtil.setZoom(_webViewController, value.zoomLevel);
        MapUtil.setCenter(_webViewController, value.center);
      });
    });
    return Stack(children: [
      BMFMapWidget(
        onBMFMapCreated: (controller) {
          // onBMFMapCreated(controller);
          _mapController = controller;
          //绘制路径
          List<BMFCoordinate> points = [];
          locationList.forEach((element) {
            points.add(BMFCoordinate(
                element.location!.latitude!, element.location!.longitude!));
          });
          MapUtil.drawLine(points, ColorConstant.theme, _mapController)
              .then((value) {
            //画起始点与结束点
            MapMarkerUtil.addTravelStartMarker(_mapController, points[0])
                .then((value) {});
            MapMarkerUtil.addTravelEndMarker(
                    _mapController, points[points.length - 1])
                .then((value) {})
                .then((value) {});
            //画文字
            if (points.length > 1) {
              MapMarkerUtil.addText(_mapController, points[0],
                  locationList[0].location!.addressDetail!);
            }
            MapMarkerUtil.addText(_mapController, points[points.length - 1],
                    locationList[points.length - 1].location!.addressDetail!,
                    bgColor: const Color(0xAAFF1F35))
                .then((value) {
              cancap = true;
            });
          });
          //设置中心点
          MapUtil.getMapShowParams(points).then((value) {
            _mapController!.updateMapOptions(BMFMapOptions(
              zoomLevel: value.zoomLevel,
              center: value.center,
              zoomEnabled: false,
              zoomEnabledWithTap: false,
              scrollEnabled: false,
            ));
          });
          _mapController?.setMapDidFinishedRenderCallback(
              callback: (bool success) {
            print("地图渲染完成");
            if (!cancap) {
              return;
            }
            //截图
            _mapController?.takeSnapshot().then((value) {
              Uint8List? pngBytes = value;
              getTemporaryDirectory().then((value) {
                String temMapPicturePath = value.path +
                    "/${DateTime.now().millisecondsSinceEpoch}_map.png";
                if (File(temMapPicturePath).existsSync()) {
                  File(temMapPicturePath).deleteSync();
                }
                File(temMapPicturePath).writeAsBytes(pngBytes!);
                setState(() {
                  mapPicturePath = temMapPicturePath;
                  print("地图路径:$mapPicturePath");
                });
              });
            });
            //渲染完成
          });
        },
        mapOptions: mapOptions,
      ),
      _webView!,
      mapPicturePath != null && mapPicturePath!.length > 0
          ? Image.file(
              File(mapPicturePath!),
@@ -589,6 +593,7 @@
  void dispose() {
    super.dispose();
    controller!.dispose();
    eventBusCapture.cancel();
    if (mapPicturePath != null) {
      if (File(mapPicturePath!).existsSync()) {
        File(mapPicturePath!).deleteSync();
@@ -703,7 +708,7 @@
                                                      children: [
                                                        Text(
                                                          "${locationList[0].updateTime}",
                                                          style: TextStyle(
                                                          style: const TextStyle(
                                                              color: Color(
                                                                  0xFF9DAAB3),
                                                              fontSize: 12),
@@ -713,7 +718,7 @@
                                                        ),
                                                        Text(
                                                          "${locationList[locationList.length - 1].updateTime}",
                                                          style: TextStyle(
                                                          style: const TextStyle(
                                                              color: Color(
                                                                  0xFF9DAAB3),
                                                              fontSize: 12),
@@ -738,8 +743,8 @@
                                                                .circular(10)),
                                                  ),
                                                  QrImage(
                                                      data:
                                                          "http://www.baidu.com",
                                                      data: Constant
                                                          .APP_DOWNLOAD_LINK,
                                                      size: 54,
                                                      padding:
                                                          const EdgeInsets.all(
@@ -774,7 +779,7 @@
                        height: 55,
                        decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: BorderRadius.only(
                            borderRadius: const BorderRadius.only(
                                topLeft: Radius.circular(10),
                                topRight: Radius.circular(10)),
                            boxShadow: getViewShadow()),
@@ -789,7 +794,7 @@
                            Container(
                              width: 8,
                            ),
                            Text(
                            const Text(
                              "点击分享",
                              style: TextStyle(
                                  color: ColorConstant.theme, fontSize: 15),
@@ -799,7 +804,7 @@
                      )),
                  Container(
                      color: Colors.white,
                      padding: EdgeInsets.fromLTRB(23, 15, 23, 15),
                      padding: const EdgeInsets.fromLTRB(23, 15, 23, 15),
                      child: Column(children: [
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceAround,
@@ -838,16 +843,8 @@
  Widget getSharePlatform(text, iconAsset) {
    return InkWell(
        onTap: () {
          Future<File?> future = _capturePng();
          future.then((value) {
            if (text.toString().contains("QQ")) {
              _onShare(context, value!, SharePlatform.qq);
            } else if (text.toString().contains("微信")) {
              _onShare(context, value!, SharePlatform.wx);
            } else if (text.toString().contains("微博")) {
              _onShare(context, value!, SharePlatform.sina);
            }
          });
          sharePlatformName=text;
          MapUtil.capture(_webViewController);
        },
        child: Column(
          children: [
@@ -860,7 +857,7 @@
            ),
            Text(
              text,
              style: TextStyle(color: Color(0xFF9DAAB3), fontSize: 12),
              style: TextStyle(color: const Color(0xFF9DAAB3), fontSize: 12),
            )
          ],
        ));
@@ -869,7 +866,7 @@
  //控件阴影
  List<BoxShadow> getViewShadow() {
    return [
      BoxShadow(
      const BoxShadow(
        blurRadius: 6.5,
        spreadRadius: 1,
        color: Color(0x4D0E96FF),
@@ -896,37 +893,6 @@
      return File(shareImgPath!);
    } catch (e) {}
    return null;
  }
  void _onShare(
      BuildContext context, File f, SharePlatform sharePlatform) async {
    if (Platform.isAndroid) {
      const platform = MethodChannel("com.yeshi.location/share"); //分析1
      bool result = false;
      try {
        var params = {
          "path": f.path,
          "platform": sharePlatform.runtimeType.toString()
        };
        switch (sharePlatform) {
          case SharePlatform.qq:
            params["platform"] = "qq";
            break;
          case SharePlatform.wx:
            params["platform"] = "wx";
            break;
          case SharePlatform.sina:
            params["platform"] = "sina";
            break;
        }
        result = await platform.invokeMethod("shareImg", params); //分析2
      } on PlatformException catch (e) {
        print(e.toString());
      }
    } else {
      Share.shareFiles([f.path]);
    }
  }
}
lib/ui/mine/add_location_person.dart
@@ -4,10 +4,15 @@
import 'package:flutter/material.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/map/location_user_model.dart';
import 'package:locations/model/user/user_info.dart';
import 'package:locations/ui/common/browser.dart';
import 'package:locations/ui/mine/login.dart';
import 'package:locations/ui/widget/base_ui.dart';
import 'package:locations/ui/widget/button.dart';
import 'package:locations/ui/widget/dialog.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/utils/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';
@@ -331,6 +336,24 @@
  }
  void showAddPersonDialog(int index) async {
    //查询是否为VIP
    UserInfo? user = await UserUtil.getUserInfo();
    if (user == null) {
      return;
    }
    if (DateTime.now().millisecondsSinceEpoch > user.vipExpireTime!) {
      ToastUtil.toast("请先开通会员");
      NavigatorUtil.navigateToNextPage(
          context,
          BrowserPage(
              title: "开通会员",
              url: (await ConfigUtil.getConfig(ConfigKey.vipLink))!), (data) {
        //请求用户数据
        UserUtil.updateUserInfo(context);
      });
      return;
    }
    if (index == 0) {
      selectedUserType = LocationUserType.father;
    } else if (index == 1) {
lib/ui/mine/login.dart
@@ -1,10 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx;
import 'package:html/dom.dart' as dom;
@@ -17,12 +17,13 @@
import 'package:locations/utils/push_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:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
@@ -37,6 +38,10 @@
}
class LoginPage extends StatefulWidget {
  //阿里云一键登录
  static const messageChannel =
      BasicMessageChannel('AliyunPhoneNumberAuth', StandardMessageCodec());
  LoginPage({Key? key, required this.title}) : super(key: key);
  // This widget is the home page of your application. It is stateful, meaning
@@ -87,45 +92,48 @@
    });
  }
  //阿里云一键登录
  static const messageChannel = BasicMessageChannel(
      'AliyunPhoneNumberAuth', StandardMessageCodec());
  bool _aliyunLogin = false;
  void aliyunOneKeyLogin() async {
    Map value = await messageChannel.send({
      "method": "init",
      "secret": Constant.ALIYUN_AUTH_SECRETINFO,
      "privacy": Constant.PRIVACY_URL,
      "protocol": Constant.PROTOCOL_URL
    }) as Map;
    if (value["code"] == 0) {
      value = await messageChannel.send({"method": "checkEnv"}) as Map;
      if (value["code"] == 0) {
        value = await messageChannel.send({"method": "startLogin"}) as Map;
        if (value["code"] == 0) {
          await messageChannel.send({"method": "closeLogin"});
          // Fluttertoast.showToast(msg: "token:" + value["token"]);
          Map<String, dynamic>? resultValue =
          await UserApiUtil.login(context, "", "", value["token"]);
          if (resultValue == null) return;
    if (_aliyunLogin) {
      return;
    }
    _aliyunLogin = true;
    Future.delayed(const Duration(milliseconds: 1000), () {
      _aliyunLogin = false;
    });
          if (resultValue["code"] == 0) {
            UserInfo user = UserInfo.fromJson(resultValue["data"]);
            _loginSuccess(user);
          } else {
            Fluttertoast.showToast(msg: value["msg"]);
          }
        } else if (value["code"] == 700000) {
          //Fluttertoast.showToast(msg: "取消登录了");
    showLoading(context);
    Map value =
        await LoginPage.messageChannel.send({"method": "checkEnv"}) as Map;
    dismissDialog(context);
    if (value["code"] == 0) {
      value =
          await LoginPage.messageChannel.send({"method": "startLogin"}) as Map;
      print("返回内容:${jsonEncode(value)}");
      if (value["code"] == 0) {
        Map<String, dynamic>? resultValue =
            await UserApiUtil.login(context, "", "", value["token"]);
        LoginPage.messageChannel.send({"method": "closeLogin"});
        if (resultValue == null) return;
        if (resultValue["code"] == 0) {
          UserInfo user = UserInfo.fromJson(resultValue["data"]);
          _loginSuccess(user);
        } else {
          Fluttertoast.showToast(msg: value["msg"]);
        }
      } else {
        Fluttertoast.showToast(msg: value["msg"]);
        setState(() {
          oneKeyLogin = false;
        });
      } else if (value["code"] == 700000) {
        //Fluttertoast.showToast(msg: "取消登录了");
      }
    } else {
      Fluttertoast.showToast(msg: value["msg"]);
      setState(() {
        oneKeyLogin = false;
      });
    }
  }
@@ -161,7 +169,7 @@
              blurRadius: 2.0,
              offset: Offset(0.0, 5.0), //阴影y轴偏移量
              spreadRadius: 1 //阴影扩散程度
          )
              )
        ]);
  }
@@ -204,7 +212,7 @@
                                ),
                                Row(
                                  mainAxisAlignment:
                                  MainAxisAlignment.spaceAround,
                                      MainAxisAlignment.spaceAround,
                                  children: [
                                    // getThirdLoginItem("微信登录",
                                    //     "assets/images/login/ic_login_wx.png"),
@@ -212,9 +220,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,28 +241,28 @@
                ),
                Expanded(
                    child: Container(
                        child: Html(
                            data:
                            "<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: const Color(0xFF999999),
                                  fontSize: FontSize.medium)
                            },
                            onLinkTap: (String? url,
                                RenderContext context1,
                                Map<String, String> attributes,
                                dom.Element? element) {
                              NavigatorUtil.navigateToNextPage(
                                  context, BrowserPage(title: element!.innerHtml, url: url!,), (
                                  data) {});
                            }))),
                        child: HtmlWidget(
                            "<p>登录即表明同意&nbsp;<a href='${Constant.PROTOCOL_URL}'>用户协议</a>&nbsp;和&nbsp;<a href='${Constant.PRIVACY_URL}'>隐私政策</a>&nbsp;</p>",
                            textStyle:
                                const TextStyle(color: Color(0xFF999999)),
                            onTapUrl: (url) {
                  String? title = "";
                  if (url == Constant.PROTOCOL_URL) {
                    title = "用户协议";
                  } else {
                    title = "隐私政策";
                  }
                  NavigatorUtil.navigateToNextPage(
                      context,
                      BrowserPage(
                        title: title,
                        url: url,
                      ),
                      (data) {});
                  return true;
                }))),
              ])
            ]),
            //关闭按钮
@@ -276,178 +284,180 @@
  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,
      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,
            crossAxisAlignment: CrossAxisAlignment.start,
            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,
              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(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,
              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: 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();
                                }
                              });
                            });
                          } else {
                            ToastUtil.toast(value!["msg"]);
                          }
                        });
                      },
                    ),
                  ],
                ),
              ),
              Container(height: 10),
              const Text("未注册的手机号注册后系统会自动创建账户",
                  style: TextStyle(color: Color(0xFFA0A0A0), fontSize: 12)),
              Container(height: 20),
              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)),
                height: 45,
                color: const Color(0xFFFF2B4B),
                fontSize: 17,
                enable: StringUtil.isMobile(phoneController!.value.text) &&
                    codeController!.value.text.length >= 4,
                onClick: () {
                  if (!(reSendSMSTimeLeft! < 1 &&
                      StringUtil.isMobile(phoneController!.value.text))) {
                  if (!(StringUtil.isMobile(phoneController!.value.text) &&
                      codeController!.value.text.length >= 4)) {
                    return;
                  }
                  //发送验证码
                  UserApiUtil.sendSMS(
                      context, phoneController!.value.text)
                  if (!checked) {
                    Fluttertoast.showToast(msg: "请同意用户协议与隐私政策");
                    return;
                  }
                  UserApiUtil.login(context, phoneController!.value.text,
                          codeController!.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();
                              }
                            });
                      });
                    print("结果: $value");
                    if (value!["code"] == 0) {
                      UserInfo user = UserInfo.fromJson(value["data"]);
                      _loginSuccess(user);
                    } else {
                      Fluttertoast.showToast(msg: value["msg"]);
                    }
                  });
                },
              ),
            ],
          ),
        ),
        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
@@ -149,21 +149,28 @@
                subTitle: '此模式下本软件运行可能会收到限制\n定位期间尽量确保电量充足',
                notify: '建议勿开启',
                checked: false,
                changed: (bool value) {},
                changed: (bool value) {
                  openAppSettings();
                },
              ),
              getItemView(
                title: '电池优化白名单',
                subTitle: '此模式下本软件运行可能会收到限制\n定位期间尽量确保电量充足',
                notify: '快速设置',
                checked: false,
                changed: (bool value) {},
                changed: (bool value) {
                  openAppSettings();
                },
              ),
              getItemView(
                title: '应用启动管理',
                subTitle: '此模式下本软件运行可能会收到限制\n定位期间尽量确保电量充足',
                notify: '快速设置',
                checked: true,
                changed: (bool value) {},
                changed: (bool value) {
                  openAppSettings();
                },
              ),
            ],
          ),
lib/ui/mine/settings.dart
@@ -114,12 +114,12 @@
    var uid = await UserUtil.getUid();
    Map<String, dynamic>? map = await UserApiUtil.logout(context, uid);
    if (map!["code"] == 0) {
      await UserUtil.logout();
      await PushUtil.removeAlias();
      eventBus.fire(LoginEventBus(false));
      UserUtil.logout();
      setState(() {
        login = false;
      });
      ToastUtil.toast("退出成功");
      Navigator.of(context).pop();
    } else {
      ToastUtil.toast(map["msg"]);
    }
@@ -200,18 +200,18 @@
                    }
                  });
                }),
            getCommonItemView(
                title: "第三方SDK列表",
                content: "",
                onClick: () {
                  ConfigUtil.getConfig(ConfigKey.sdkList).then((value) {
                    if (!StringUtil.isNullOrEmpty(value)) {
                      NavigatorUtil.navigateToNextPage(
                          context, BrowserPage(title: "第三方SDK列表", url: value!), (data) {});
                    }
                  });
                }),
            // getCommonItemView(
            //     title: "第三方SDK列表",
            //     content: "",
            //     onClick: () {
            //
            //       ConfigUtil.getConfig(ConfigKey.sdkList).then((value) {
            //         if (!StringUtil.isNullOrEmpty(value)) {
            //           NavigatorUtil.navigateToNextPage(
            //               context, BrowserPage(title: "第三方SDK列表", url: value!), (data) {});
            //         }
            //       });
            //     }),
            Expanded(
              child: Container(),
            ),
lib/ui/mine/share_to_friends.dart
@@ -1,9 +1,20 @@
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:locations/ui/map/travel.dart';
import 'package:locations/ui/mine/add_location_person.dart';
import 'package:locations/ui/widget/button.dart';
import 'package:locations/ui/widget/nav.dart';
import 'package:locations/utils/permission_util.dart';
import 'package:locations/utils/share_utils.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:path_provider/path_provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
  runApp(MyApp());
@@ -41,13 +52,40 @@
class _ShareToFriendsPageState extends State<ShareToFriendsPage>
    with SingleTickerProviderStateMixin {
  String? shareImgPath;
  AnimationController? controller;
  Animation<double>? animation;
  //截图组件
  GlobalKey rootWidgetKey = GlobalKey();
  @override
  void initState() {
    super.initState();
    controller = AnimationController(
        duration: const Duration(milliseconds: 300), vsync: this);
    final CurvedAnimation curve =
        CurvedAnimation(parent: controller!, curve: Curves.easeIn);
    animation = Tween(begin: -165.0, end: 0.0).animate(curve)
      ..addListener(() {
        setState(() {});
      });
  }
  @override
  void dispose() {
    super.dispose();
    if (shareImgPath != null && File(shareImgPath!).existsSync()) {
      File(shareImgPath!).deleteSync();
    }
  }
  @override
  Widget build(BuildContext context) {
    double imgWidth = MediaQuery.of(context).size.width - 10 * 2;
    double imgHeight = imgWidth * 258 / 355;
    return Scaffold(
        backgroundColor: Color(0xFFB4E4FF),
        body: Container(
@@ -58,52 +96,228 @@
            Expanded(
                child: Container(
              padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
              child: Flex(
                direction: Axis.vertical,
                children: [
                  Expanded(
                    child: Center(
                        child: ClipRRect(
                            borderRadius: BorderRadius.circular(10),
                            child: Image.network(
                                "https://t7.baidu.com/it/u=4240641596,3235181048&fm=193&f=GIF"))),
                  ),
                  Container(
                      alignment: Alignment.center,
                      child: InkWell(
                        onTap: () {
                          print("share");
                        },
                        child: Container(
                          height: 55,
                          decoration: BoxDecoration(
                              color: Colors.white,
                              borderRadius: BorderRadius.only(
                                topLeft: Radius.circular(10),
                                topRight: Radius.circular(10),
                              )),
                          child: Flex(
                            direction: Axis.horizontal,
                            mainAxisAlignment: MainAxisAlignment.center,
                            crossAxisAlignment: CrossAxisAlignment.center,
                            children: [
                              Image.asset(
                                "assets/images/common/icon_btn_share.png",
                                height: 16,
                              ),
                              Text(
                                " 点击分享",
                                style: TextStyle(
                                    color: ColorConstant.theme, fontSize: 15),
                              )
                            ],
                          ),
                        ),
                      )),
                ],
              ),
              child: Stack(children: [
                Flex(
                  direction: Axis.vertical,
                  children: [
                    Expanded(
                      child: Center(
                          child: RepaintBoundary(
                              key: rootWidgetKey,
                              child: ClipRRect(
                                  borderRadius: BorderRadius.circular(10),
                                  child: Container(
                                      height: imgHeight + 122,
                                      child: Column(
                                        mainAxisAlignment:
                                            MainAxisAlignment.center,
                                        mainAxisSize: MainAxisSize.min,
                                        children: [
                                          Stack(
                                              alignment: Alignment.bottomCenter,
                                              children: [
                                                Image.asset(
                                                  "assets/images/common/ic_share_app_bg.png",
                                                  width: imgWidth,
                                                  height: imgHeight,
                                                ),
                                                Container(
                                                    margin: const EdgeInsets
                                                        .only(bottom: 10),
                                                    decoration:
                                                        BoxDecoration(
                                                            color: Colors.white,
                                                            borderRadius:
                                                                BorderRadius
                                                                    .circular(
                                                                        10)),
                                                    padding: EdgeInsets.all(5),
                                                    child: QrImage(
                                                        data:
                                                        Constant.APP_DOWNLOAD_LINK,
                                                        size: 67,
                                                        padding:
                                                            const EdgeInsets
                                                                .all(0),
                                                        foregroundColor:
                                                            ColorConstant
                                                                .theme))
                                              ]),
                                          Container(
                                            margin: const EdgeInsets.only(
                                                left: 0, right: 0),
                                            color: Colors.white,
                                            width: imgWidth,
                                            height: 122,
                                            child: Column(
                                              mainAxisAlignment:
                                                  MainAxisAlignment.center,
                                              children: [
                                                Image.asset(
                                                    "assets/images/common/ic_launcher.png",
                                                    height: 67),
                                                const SizedBox(
                                                  height: 8,
                                                ),
                                                const Text(
                                                  Constant.APP_NAME,
                                                  style: TextStyle(
                                                      color:
                                                          ColorConstant.theme,
                                                      fontSize: 13),
                                                )
                                              ],
                                            ),
                                          ),
                                        ],
                                      ))))),
                    ),
                    const SizedBox(
                      height: 50,
                    )
                  ],
                ),
                Positioned(
                    bottom: animation!.value,
                    left: 0,
                    right: 0,
                    child: Column(children: [
                      InkWell(
                          onTap: () {
                            if (controller!.status ==
                                AnimationStatus.dismissed) {
                              controller!.forward();
                            } else if (controller!.status ==
                                AnimationStatus.completed) {
                              controller!.reverse();
                            }
                          },
                          child: Container(
                            alignment: Alignment.center,
                            height: 55,
                            decoration: BoxDecoration(
                                color: Colors.white,
                                borderRadius: const BorderRadius.only(
                                    topLeft: Radius.circular(10),
                                    topRight: Radius.circular(10)),
                                boxShadow: getViewShadow()),
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: [
                                Image.asset(
                                  "assets/images/common/icon_share.png",
                                  height: 17.5,
                                ),
                                Container(
                                  width: 8,
                                ),
                                Text(
                                  "点击分享",
                                  style: TextStyle(
                                      color: ColorConstant.theme, fontSize: 15),
                                )
                              ],
                            ),
                          )),
                      Container(
                          color: Colors.white,
                          padding: EdgeInsets.fromLTRB(23, 15, 23, 15),
                          child: Column(children: [
                            Row(
                              mainAxisAlignment: MainAxisAlignment.spaceAround,
                              children: [
                                getSharePlatform(
                                  "微信好友",
                                  "assets/images/common/icon_share_wx.png",
                                ),
                                getSharePlatform("QQ好友",
                                    "assets/images/common/icon_share_qq.png"),
                                getSharePlatform("微博",
                                    "assets/images/common/icon_share_sina.png")
                              ],
                            ),
                            Container(
                              height: 20,
                            ),
                            MyOutlineButton(
                              "取消",
                              10,
                              height: 40,
                              fontSize: 18,
                              onClick: () {
                                if (controller!.status ==
                                    AnimationStatus.completed) {
                                  controller!.reverse();
                                }
                              },
                            )
                          ]))
                    ]))
              ]),
            ))
          ],
        )));
  }
  Widget getSharePlatform(text, iconAsset) {
    return InkWell(
        onTap: () {
          _capturePng().then((value) {
            if (value == null) {
              return;
            }
            if (text.toString().contains("QQ")) {
              ShareUtil.shareImg(context, value, SharePlatform.qq);
            } else if (text.toString().contains("微信")) {
              ShareUtil.shareImg(context, value, SharePlatform.wx);
            } else if (text.toString().contains("微博")) {
              ShareUtil.shareImg(context, value, SharePlatform.sina);
            }
          });
        },
        child: Column(
          children: [
            Image.asset(
              iconAsset,
              width: 49,
            ),
            Container(
              height: 5,
            ),
            Text(
              text,
              style: TextStyle(color: Color(0xFF9DAAB3), fontSize: 12),
            )
          ],
        ));
  }
  //截图,返回数据
  Future<File?> _capturePng() async {
    PermissionStatus status =
        await PermissionUtil.openPermission(Permission.storage, force: true);
    if (status != PermissionStatus.granted) {
      return null;
    }
    try {
      RenderRepaintBoundary? boundary = rootWidgetKey.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();
      Directory tempDir = await getTemporaryDirectory();
      String tempPath = tempDir.path;
      shareImgPath = "$tempPath/share.jpg";
      if (File(shareImgPath!).existsSync()) {
        File(shareImgPath!).deleteSync();
      }
      File(shareImgPath!).createSync();
      File(shareImgPath!).writeAsBytesSync(pngBytes);
      return File(shareImgPath!);
    } catch (e) {}
    return null;
  }
}
lib/ui/sos/sos.dart
@@ -149,10 +149,7 @@
                            if (!sos) {
                              LocationUtil.startLocation(0, (state, map) {
                                if (LocationState.success == state) {
                                  SOSApiUtil.addSOSRecord(
                                          context,
                                          SimpleLocation.fromBaiDuLocation(
                                              BaiduLocation.fromJson(map)))
                                  SOSApiUtil.addSOSRecord(context, map!)
                                      .then((value) {
                                    if (value!["code"] == 0) {
                                      setState(() {
@@ -274,34 +271,36 @@
                            Container(
                              width: 12.5,
                            ),
                            _totalCount>0?
                            InkWell(
                              onTap: () {
                                DialogUtil.showDialog(
                                    context,
                                    NotifyDialog(
                                      "温馨提示",
                                      "确定要清空记录吗?",
                                      () {},
                                      () {
                                        SOSApiUtil.clearSOSRecord(context)
                                            .then((value) {
                                          if (value!["code"] == 0) {
                                            _onRefresh();
                                          } else {
                                            ToastUtil.toast(value["msg"]);
                                          }
                                        });
                                      },
                                      height: 200,
                                    ));
                              },
                              child: const Text(
                                "清空记录",
                                style: TextStyle(
                                    color: Color(0xFF999999), fontSize: 12),
                              ),
                            ):Container(),
                            _totalCount > 0
                                ? InkWell(
                                    onTap: () {
                                      DialogUtil.showDialog(
                                          context,
                                          NotifyDialog(
                                            "温馨提示",
                                            "确定要清空记录吗?",
                                            () {},
                                            () {
                                              SOSApiUtil.clearSOSRecord(context)
                                                  .then((value) {
                                                if (value!["code"] == 0) {
                                                  _onRefresh();
                                                } else {
                                                  ToastUtil.toast(value["msg"]);
                                                }
                                              });
                                            },
                                            height: 200,
                                          ));
                                    },
                                    child: const Text(
                                      "清空记录",
                                      style: TextStyle(
                                          color: Color(0xFF999999),
                                          fontSize: 12),
                                    ),
                                  )
                                : Container(),
                            Expanded(
                              child: SmartRefresher(
                                  enablePullDown: true,
@@ -448,7 +447,7 @@
            height: 6,
          ),
          Text(
            record.targetDesc!,
            record.targetDesc==null?"":    record.targetDesc!,
            style: TextStyle(fontSize: 12, color: const Color(0xFF8CAFC8)),
          ),
          Container(
@@ -472,8 +471,15 @@
          ),
          InkWell(
              onTap: () {
                NavigatorUtil.navigateToNextPage(context, MapPage("位置",share: false,location:record.location,uid: record.targetUid,), (data) { });
                NavigatorUtil.navigateToNextPage(
                    context,
                    MapPage(
                      "位置",
                      share: false,
                      location: record.location,
                      uid: record.targetUid,
                    ),
                    (data) {});
              },
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
@@ -485,16 +491,14 @@
                    width: 6,
                  ),
                  Expanded(
                      child: FittedBox(
                          fit: BoxFit.fitWidth,
                          child: Text(
                      child: Text(
                            record.location!.address! +
                                record.location!.addressDetail!,
                            softWrap: false,
                            overflow: TextOverflow.ellipsis,
                            style: const TextStyle(
                                fontSize: 12, color: Color(0xFF999999)),
                          ))),
                          )),
                  Container(
                    width: 6,
                  ),
lib/ui/widget/dialog.dart
@@ -1,10 +1,12 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:html/dom.dart' as dom;
import 'package:locations/ui/common/browser.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
//通用弹框
class NotifyDialog extends Dialog {
@@ -25,20 +27,24 @@
      {this.fontSize = 16.0,
      this.richText = false,
      this.height = 240,
      this.contentColor = const Color(0xFF7E7E7E),this.cancelName="取消",this.sureName="确定"});
      this.contentColor = const Color(0xFF7E7E7E),
      this.cancelName = "取消",
      this.sureName = "确定"});
  Widget getContent() {
  Widget getContent(BuildContext context) {
    if (richText) {
      return SingleChildScrollView(
          child: Html(
              data: content,
              style: {
                "a": Style(
                  textDecoration: TextDecoration.none,
                )
              },
              onLinkTap: (String? url, RenderContext context,
                  Map<String, String> attributes, dom.Element? element) {}));
          child: HtmlWidget(content, onTapUrl: (String url) {
        NavigatorUtil.navigateToNextPage(
            context,
            BrowserPage(
              title: "",
              url: url,
            ),
            (data) {});
        return true;
      }));
    } else {
      return Text(
        content,
@@ -95,8 +101,8 @@
                      Expanded(
                          child: Container(
                        alignment: Alignment.center,
                        padding: EdgeInsets.fromLTRB(15, 5, 15, 5),
                        child: getContent(),
                        padding: const EdgeInsets.fromLTRB(15, 5, 15, 5),
                        child: getContent(context),
                      )),
                      //------按钮区域--------
lib/ui/widget/map_marker.dart
@@ -2,10 +2,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:locations/utils/map_util.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import 'dart:math' as math;
//三角形
@@ -123,49 +120,3 @@
  }
}
class MapMarkerUtil {
  ///添加轨迹开始点
  static Future<List<BMFMarker>> addTravelStartMarker(
      BMFMapController? _mapController, BMFCoordinate position) async {
    BMFMarker marker = await MapUtil.addMarker(
        _mapController, position, "assets/images/map/icon_travel_start.png",
        zIndex: 1);
    BMFMarker marker1 = await MapUtil.addMarker(
        _mapController, position, "assets/images/map/icon_travel_start_1.png",
        offset: BMFPoint(0, 38));
    return [marker, marker1];
  }
  //添加轨迹结束点
  static Future<List<BMFMarker>> addTravelEndMarker(
      BMFMapController? _mapController, BMFCoordinate position) async {
    BMFMarker marker = await MapUtil.addMarker(
        _mapController, position, "assets/images/map/icon_travel_end.png",
        zIndex: 1);
    BMFMarker marker1 = await MapUtil.addMarker(
        _mapController, position, "assets/images/map/icon_travel_end_1.png",
        offset: BMFPoint(0, 38));
    return [marker, marker1];
  }
  ///添加文字图层
  static Future addText(
      BMFMapController? _mapController, BMFCoordinate position,String text,
      {Color bgColor = const Color(0xAA0E95FE),
      Color fontColor = Colors.white}) async {
    BMFText bmfText = BMFText(
        text: "     "+text+"  ",
        position: position,
        bgColor: bgColor,
        fontColor: fontColor,
        fontSize: 30,
        typeFace: BMFTypeFace(
            familyName: BMFFamilyName.sMonospace,
            textStype: BMFTextStyle.BOLD_ITALIC),
        alignY: BMFVerticalAlign.ALIGN_CENTER_VERTICAL,
        alignX: BMFHorizontalAlign.ALIGN_LEFT);
    /// 添加text
    await _mapController!.addText(bmfText);
  }
}
lib/ui/widget/nav.dart
@@ -7,6 +7,7 @@
  final String title;
  GestureTapCallback? back;
  String? rightText;
  Widget? rightIcon;
  GestureTapCallback? rightClick;
  final Color textColor;
  final Color backGround;
@@ -16,6 +17,7 @@
      {required this.title,
      GestureTapCallback? this.back,
      String? this.rightText,
      Widget? this.rightIcon,
      GestureTapCallback? this.rightClick,
      Color this.textColor = const Color(0xFF333333),
      Color this.backGround = Colors.white,
@@ -51,19 +53,17 @@
                        Expanded(
                            child: Center(
                                child: Text(
                          title,
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                          style:
                              TextStyle(fontSize: 18, color: textColor),
                          style: TextStyle(fontSize: 18, color: textColor),
                        ))),
                        Container(
                          width: 50,
                        )
                      ],
                    ))),
            rightText != null && rightText!.length > 0
            ((rightText != null && rightText!.isNotEmpty) || rightIcon != null)
                ? Positioned(
                    right: 0,
                    top: 0,
@@ -74,20 +74,21 @@
                        },
                        child: Container(
                          alignment: Alignment.center,
                          padding: EdgeInsets.only(right: 10),
                          child: Text(
                            rightText!,
                            style: TextStyle(
                                fontSize: 15, color: textColor),
                          ),
                          padding: const EdgeInsets.only(right: 10),
                          child: rightIcon ?? Text(
                                  rightText!,
                                  style:
                                      TextStyle(fontSize: 15, color: textColor),
                                ),
                        )))
                : Container(),
            InkWell(
                onTap: () {
                  if (back != null)
                  if (back != null) {
                    back!();
                  else
                  } else {
                    Navigator.pop(context);
                  }
                },
                child: Container(
                  alignment: Alignment.center,
lib/utils/ad_util.dart
@@ -7,30 +7,34 @@
import 'package:locations/utils/string_util.dart';
import 'package:flutter_tencentad/flutter_tencentad.dart';
import 'ui_constant.dart';
//紧急联系人输入框确定事件
typedef OnAdCallback = void Function(bool success, String msg);
typedef OnRewardAdCallback = void Function(RewardAdStatus status, String msg);
enum RewardAdStatus { fail, click, ready, verify, close }
class AdUtil {
  static AdinfoModel? splashAdInfo;
  static Future init() async {
    await CSJAdUtil.init();
    await GDTAdUtil.init();
    splashAdInfo = await getAdInfo(AdPosition.splash);
  }
  static void showSplashAd(
      double width, double height, OnAdCallback adCallback) async {
    //加载广告
  }
  static Widget? loadSplash(AdinfoModel? adInfo, double width, double height,
  static Widget loadSplash(AdinfoModel? adInfo, double width, double height,
      OnAdCallback adCallback) {
    if (adInfo == null) {
      adCallback(false, "广告信息为空");
      return null;
      return Container();
    }
    if (StringUtil.isNullOrEmpty(adInfo.type)) {
      adCallback(false, "广告类型为空");
      return null;
      return Container();
    }
    if (adInfo.type == "csj") {
@@ -78,14 +82,14 @@
    }
  }
  static loadReward(AdinfoModel? adInfo, OnAdCallback callback) async {
  static loadReward(AdinfoModel? adInfo, OnRewardAdCallback callback) async {
    if (adInfo == null) {
      callback(false, "广告信息为空");
      callback(RewardAdStatus.fail, "广告信息为空");
      return null;
    }
    if (StringUtil.isNullOrEmpty(adInfo.type)) {
      callback(false, "广告类型为空");
      callback(RewardAdStatus.fail, "广告类型为空");
      return null;
    }
@@ -157,7 +161,7 @@
          //穿山甲广告 ios appid 必填
          useTextureView: false,
          //使用TextureView控件播放视频,默认为SurfaceView,当有SurfaceView冲突的场景,可以使用TextureView 选填
          appName: "定位追踪轨迹",
          appName: Constant.APP_NAME,
          //appname 必填
          allowShowNotify: true,
          //是否允许sdk展示通知栏提示 选填
@@ -174,11 +178,11 @@
    }
  }
  static Widget? loadSplash(
  static Widget loadSplash(
      String? pid, double width, double height, OnAdCallback adCallback) {
    if (pid == null) {
      adCallback(false, "pid为空");
      return null;
      return Container();
    }
    return FlutterUnionad.splashAdView(
      //是否使用个性化模版  设定widget宽高
@@ -286,7 +290,7 @@
    );
  }
  static loadReward(String pid, OnAdCallback adCallback) async {
  static loadReward(String pid, OnRewardAdCallback adCallback) async {
    FlutterUnionad.loadRewardVideoAd(
      mIsExpress: true,
      //是否个性化 选填
@@ -315,23 +319,25 @@
        },
        onClick: () {
          print("激励广告点击");
          adCallback(RewardAdStatus.click, "激励广告点击");
        },
        onFail: (error) {
          print("激励广告失败 $error");
          adCallback(false, "激励广告失败 $error");
          adCallback(RewardAdStatus.fail, "激励广告失败 $error");
        },
        onClose: () {
          print("激励广告关闭");
          adCallback(RewardAdStatus.close, "激励广告关闭");
        },
        onSkip: () {
          print("激励广告跳过");
        },
        onVerify: (bool isVerify, int rewardAmount, String rewardName,
            int errorCode, String message) {
          adCallback(true, "获取激励成功");
          adCallback(RewardAdStatus.verify, "获取激励成功");
        },
        onReady: () async {
          print("激励广告预加载准备就绪");
          adCallback(RewardAdStatus.ready, "激励广告预加载准备就绪");
          //显示激励广告
          await FlutterUnionad.showRewardVideoAd();
        },
@@ -400,7 +406,7 @@
    }
  }
  static Widget? loadSplash(
  static Widget loadSplash(
      String? pid, double width, double height, OnAdCallback adCallback) {
    return FlutterTencentad.splashAdView(
      //android广告id
@@ -499,7 +505,7 @@
        ));
  }
  static loadReward(String pid, OnAdCallback adCallback) async {
  static loadReward(String pid, OnRewardAdCallback adCallback) async {
    await FlutterTencentad.loadRewardVideoAd(
        //android广告id
        androidId: pid,
@@ -521,19 +527,22 @@
        print("激励广告显示");
      }, onClick: () {
        print("激励广告点击");
        adCallback(RewardAdStatus.click, "激励广告点击");
      }, onFail: (code, message) {
        print("激励广告失败 $code $message");
        adCallback(false, "激励广告失败");
        adCallback(RewardAdStatus.fail, "激励广告失败");
      }, onClose: () {
        print("激励广告关闭");
        adCallback(RewardAdStatus.close, "激励广告关闭");
      }, onReady: () async {
        print("激励广告预加载准备就绪");
        adCallback(RewardAdStatus.ready, "激励广告预加载准备就绪");
        await FlutterTencentad.showRewardVideoAd();
      }, onUnReady: () {
        print("激励广告预加载未准备就绪");
      }, onVerify: (transId, rewardName, rewardAmount) {
        print("激励广告奖励  $transId   $rewardName   $rewardAmount");
        adCallback(true, "激励广告奖励");
        adCallback(RewardAdStatus.verify, "激励广告奖励");
      }, onFinish: () {
        print("激励广告完成");
      }),
lib/utils/app_util.dart
@@ -2,35 +2,33 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.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:fluwx_no_pay/fluwx_no_pay.dart';
import 'package:jpush_flutter/jpush_flutter.dart';
import 'package:locations/ui/mine/login.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';
import 'package:device_info/device_info.dart';
import 'ui_constant.dart';
import 'ui_utils.dart';
class AppUtil {
  static JPush _jpush = JPush();
  static final JPush _jpush = JPush();
  static bool _inited = false;
  //初始化应用
  static Future<bool> initApp(BuildContext context) async {
    if (_inited) {
      return true;
    }
    _inited = true;
    print("initApp");
    await registerWxApi(
        appId: "wxd930ea5d5a228f5f",
        universalLink: "https://your.univerallink.com/link/");
    //地图
    if (Platform.isIOS) {
      BMFMapSDK.setApiKeyAndCoordType(
          '请输入百度开放平台申请的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.COMMON);
    }
    //初始化广告
    await AdUtil.init();
@@ -43,11 +41,18 @@
    } catch (e) {}
    //初始化版本
    if (Platform.isAndroid) {
      DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
      AndroidDeviceInfo _androidInfo = await deviceInfo.androidInfo;
      Global.androidSDK = _androidInfo.version.sdkInt;
    }
    //初始化阿里云授权登录
    await LoginPage.messageChannel.send({
      "method": "init",
      "secret": Constant.ALIYUN_AUTH_SECRETINFO,
      "privacy": Constant.PRIVACY_URL,
      "protocol": Constant.PROTOCOL_URL
    }) as Map;
    return true;
  }
@@ -63,6 +68,8 @@
      }
      //填充utdid
      await Global.loadUtdId();
      //填充channel
      await Global.loadChannel();
    }
  }
lib/utils/event_bus_util.dart
@@ -20,5 +20,7 @@
  final PermissionStatus status;
  UserLocationPermissionEventBus(this.status);
}
class LocationInviteEventBus {
}
lib/utils/global.dart
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import 'package:locations/model/map/location_model.dart';
//全局跳转
final GlobalKey<NavigatorState> navigatorKey =  GlobalKey<NavigatorState>();
@@ -10,9 +10,11 @@
  static BMFCoordinate? currentPosition;
  static SimpleLocation? currentPosition;
  static String? utdId;
  static String? channel;
  //android的版本
  static int? androidSDK;
@@ -22,4 +24,10 @@
        await messageChannel.send({"method": "getUtdid"}) as String?;
    utdId = value;
  }
  static Future loadChannel() async {
    String? value =
    await messageChannel.send({"method": "getChannel"}) as String?;
    channel = value;
  }
}
lib/utils/jsinterface.dart
@@ -1,12 +1,24 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:locations/api/http.dart' as http;
import 'package:locations/ui/map/travel.dart';
import 'package:locations/utils/ad_util.dart';
import 'package:locations/utils/encrypt_util.dart';
import 'package:locations/utils/permission_util.dart';
import 'package:locations/utils/share_utils.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:permission_handler/permission_handler.dart';
import 'package:webview_flutter/platform_interface.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:path_provider/path_provider.dart';
class JavascriptInterface {
  final BuildContext context;
@@ -29,17 +41,21 @@
            case "toast":
              toast(params);
              break;
            case "copyText":
              copyText(params);
              break;
            case "getUid":
              getUid(params, _callback);
              break;
            case "getRequestBaseParams":
              getBaseRequestParams(params, _callback);
            case "getAppName":
              getAppName(_callback);
              break;
            case "getRequestBaseParams":
              getRequestBaseParams(params, _callback);
              break;
            case "showRewardVideoAd":
              showRewardVideoAd(_callback);
              break;
            case "showLoading":
              showLoading();
              break;
@@ -49,14 +65,25 @@
            case "finishPage":
              finishPage();
              break;
            case "saveImg":
              String url = params["url"];
              saveImg(url);
              break;
            case "shareImg":
              String url = params["url"];
              String type = params["type"];
              shareImg(url, int.parse(type));
              break;
          }
        }));
    return list.toSet();
  }
  callback(String method, var params) {
  callback(String method, var params, {bool finish = true}) {
    _controller!.runJavascript("$method('$params')");
    _controller!.runJavascript("delete $method");
    if (finish) {
      _controller!.runJavascript("delete $method");
    }
  }
  //获取用户ID
@@ -68,6 +95,13 @@
    }
  }
  //获取用户ID
  getAppName(String? callbackName) {
    if (callbackName != null) {
        callback(callbackName, Constant.APP_NAME);
    }
  }
  //toast
  toast(params) {
    if (params != null && params["msg"] != null) {
@@ -75,8 +109,14 @@
    }
  }
  copyText(params) {
    if (params != null && params["content"] != null) {
      Clipboard.setData(ClipboardData(text: params["content"]));
    }
  }
  //获取基本的网络请求参数
  getBaseRequestParams(var params, String? callbackName) {
  getRequestBaseParams(var params, String? callbackName) {
    var ps = {};
    if (params != null) {
      ps.addAll(params);
@@ -92,16 +132,23 @@
  //展示激励视频
  showRewardVideoAd(String? callbackName) {
    AdUtil.getAdInfo(AdPosition.vipReward).then((value) {
      AdUtil.loadReward(value, (success, msg) {
        if (success) {
          //成功
          if (callbackName != null) {
            callback(callbackName, true);
          }
        } else {
          if (callbackName != null) {
            callback(callbackName, false);
          }
      AdUtil.loadReward(value, (status, msg) {
        switch (status) {
          case RewardAdStatus.verify:
            callback(callbackName!, 3, finish: false);
            break;
          case RewardAdStatus.ready:
            callback(callbackName!, 1, finish: false);
            break;
          case RewardAdStatus.close:
            callback(callbackName!, 10, finish: true);
            break;
          case RewardAdStatus.click:
            callback(callbackName!, 2, finish: false);
            break;
          case RewardAdStatus.fail:
            callback(callbackName!, 0, finish: true);
            break;
        }
      });
    });
@@ -120,13 +167,72 @@
    Navigator.of(context).pop();
  }
  //保存图片
  saveImg(String url) {
    //TODO
  Future<bool> _dowloadImg(String url, String path) async {
    PermissionStatus status =
        await PermissionUtil.openPermission(Permission.storage, force: true);
    if (status != PermissionStatus.granted) {
      return false;
    }
    //下载图片
    Dio dio = Dio();
    //设置连接超时时间
    dio.options.connectTimeout = 20000;
    //设置数据接收超时时间
    dio.options.receiveTimeout = 20000;
    Response response;
    try {
      response = await dio.download(url, path);
      if (response.statusCode == 200) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      ToastUtil.toast("网络连接失败");
      return false;
    }
  }
  //分享图片
  //保存图片
  saveImg(String url) {
    getExternalStorageDirectory().then((value) {
      if (value == null) {
        ToastUtil.toast("获取缓存目录失败");
        return;
      }
      String doc = value.path;
      String path = doc + "/" + EncryptUtil.MD5(url) + ".png";
      _dowloadImg(url, path).then((value) {
        if (value) {
          ToastUtil.toast("保存成功");
        }
      });
    });
  }
  ///分享图片
  ///type: 1-微信  2-qq 3-新浪
  shareImg(String url, int type) {
//TODO
    getTemporaryDirectory().then((value) {
      String path = value.path + "/" + EncryptUtil.MD5(url) + ".png";
      _dowloadImg(url, path).then((value) {
        if (value) {
          //开始分享
          switch (type) {
            case 1:
              ShareUtil.shareImg(context, File(path), SharePlatform.wx);
              break;
            case 2:
              ShareUtil.shareImg(context, File(path), SharePlatform.qq);
              break;
            case 3:
              ShareUtil.shareImg(context, File(path), SharePlatform.sina);
              break;
          }
        }
      });
    });
  }
}
lib/utils/location_util.dart
@@ -1,127 +1,26 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import 'package:flutter_bmflocation/bdmap_location_flutter_plugin.dart';
import 'package:flutter_bmflocation/flutter_baidu_location_android_option.dart';
import 'package:flutter_bmflocation/flutter_baidu_location_ios_option.dart';
import 'package:flutter/services.dart';
import 'package:locations/api/http.dart';
import 'package:locations/model/map/location_model.dart';
import 'package:locations/model/user/user_info.dart';
import 'package:locations/utils/permission_util.dart';
import 'package:locations/utils/string_util.dart';
import 'package:locations/utils/ui_utils.dart';
import 'package:locations/utils/user_util.dart';
import 'package:permission_handler/permission_handler.dart';
import 'global.dart';
typedef OnLocationCallback = void Function(LocationState state, Map? map);
typedef OnLocationCallback = void Function(
    LocationState state, SimpleLocation? map);
class LocationUtil {
  static LocationFlutterPlugin? _locationFlutterPlugin =
      LocationFlutterPlugin();
  static const messageChannel =
      BasicMessageChannel('LocationUtil', StandardMessageCodec());
  static Map _getAndroidOption(int scanspan) {
    BaiduLocationAndroidOption androidOption = new BaiduLocationAndroidOption();
    androidOption.setCoorType("bd09ll");
    /// 可选,设置返回经纬度坐标类型,默认gcj02
    /// gcj02:国测局坐标;
    /// bd09ll:百度经纬度坐标;
    /// bd09:百度墨卡托坐标;
    /// 海外地区定位,无需设置坐标类型,统一返回wgs84类型坐标
    androidOption.setIsNeedAltitude(true);
    /// 可选,设置是否需要返回海拔高度信息,true为需要返回
    androidOption.setIsNeedAddres(false);
    /// 可选,设置是否需要返回地址信息,true为需要返回
    androidOption.setIsNeedLocationPoiList(false);
    /// 可选,设置是否需要返回周边poi信息,true为需要返回
    androidOption.setIsNeedNewVersionRgc(false);
    /// 可选,设置是否需要返回新版本rgc信息,true为需要返回
    androidOption.setIsNeedLocationDescribe(true);
    /// 可选,设置是否需要返回位置描述信息,true为需要返回
    androidOption.setOpenGps(true);
    /// 可选,设置是否需要使用gps,true为需要使用
    androidOption.setLocationMode(LocationMode.Hight_Accuracy);
    /// 可选,设置定位模式,可选的模式有高精度、低功耗、仅设备,默认为高精度模式,可选值如下:
    /// 高精度模式: LocationMode.Hight_Accuracy
    /// 低功耗模式:LocationMode.Battery_Saving
    /// 仅设备(Gps)模式:LocationMode.Device_Sensors
    androidOption.setScanspan(scanspan);
    /// 可选,设置发起定位请求的间隔,int类型,单位ms
    /// 如果设置为0,则代表单次定位,即仅定位一次,默认为0
    /// 如果设置非0,需设置1000ms以上才有效
    androidOption.setLocationPurpose(BDLocationPurpose.Sport);
    /// 可选,设置场景定位参数,包括签到场景、运动场景、出行场景,可选值如下:
    /// 签到场景: BDLocationPurpose.SignIn
    /// 运动场景: BDLocationPurpose.Transport
    /// 出行场景: BDLocationPurpose.Sport
    return androidOption.getMap();
  }
  static Map _getIosOption() {
    BaiduLocationIOSOption iosOption = new BaiduLocationIOSOption();
    iosOption.setIsNeedNewVersionRgc(true);
    /// 可选,设置是否需要返回新版本rgc信息,true为需要返回
    iosOption.setBMKLocationCoordinateType("BMKLocationCoordinateTypeBMK09LL");
    ///  可选,设置返回位置的坐标系类型 ,参数为String类型,可选值如下:
    /// "BMKLocationCoordinateTypeBMK09LL" 百度经纬度坐标
    /// "BMKLocationCoordinateTypeBMK09MC" 百度墨卡托坐标
    /// "BMKLocationCoordinateTypeWGS84"  gps坐标
    /// "BMKLocationCoordinateTypeGCJ02" 国测局坐标
    iosOption.setActivityType("CLActivityTypeAutomotiveNavigation");
    /// 可选,设置应用位置类型,参数为String类型,可选值如下:
    /// "CLActivityTypeOther"
    /// "CLActivityTypeAutomotiveNavigation"
    /// "CLActivityTypeFitness"
    /// "CLActivityTypeOtherNavigation"
    iosOption.setDesiredAccuracy("kCLLocationAccuracyBest");
    /// 可选,设置期望定位精度,参数为String类型,可选值如下:
    /// "kCLLocationAccuracyBest"
    /// "kCLLocationAccuracyNearestTenMeters"
    /// "kCLLocationAccuracyHundredMeters"
    /// "kCLLocationAccuracyKilometer"
    iosOption.setLocationTimeout(10);
    /// 可选,设置位置获取超时时间,参数为int类型
    iosOption.setReGeocodeTimeout(10);
    /// 可选,设置获取rgc信息超时时间,参数为int类型
    iosOption.setPauseLocUpdateAutomatically(true);
    /// 可选,指定定位是否会被系统自动暂停,参数为bool类型
    /// true表示定位会被系统自动暂停,false表示不会
    iosOption.setAllowsBackgroundLocationUpdates(true);
    /// 可选,设置是否允许后台定位,参数为bool类型
    /// true表示允许后台定位,false表示不允许后台定位
    iosOption.setDistanceFilter(10);
    /// 可选,设置定位的最小更新距离,参数为double类型
    return iosOption.getMap();
  }
  static Future<StreamSubscription<Map<String, Object>?>?> startLocation(
      int scanSpan, OnLocationCallback callback) async {
  static Future startLocation(int scanSpan, OnLocationCallback callback) async {
    final status = await requestLocationPermission();
    if (status.isGranted) {
      print("定位权限申请通过");
@@ -131,42 +30,57 @@
      return null;
    }
    StreamSubscription<Map<String, Object>?> subscription =
        _locationFlutterPlugin!
            .onResultCallback()
            .listen((Map<String, Object>? result) {
      print("定位结果:$result");
      Global.currentPosition = BMFCoordinate.fromMap(result!);
      UserUtil.isLogin().then((value) {
        if (value) {
          LocationApiUtil.uploadLocation(BaiduLocation.fromJson(result));
    dynamic value = await messageChannel.send("");
    if (value != null) {
      try {
        Map<String, dynamic> valueMap = jsonDecode(jsonEncode(value));
        double lat = double.parse(valueMap["lat"]!.toString());
        double lng = double.parse(valueMap["lng"]!.toString());
        String? address;
        String? addressDetail;
        if (valueMap["address"] != null) {
          address = valueMap["address"].toString();
        }
      });
      callback(LocationState.success, result);
      if (scanSpan == 0) {
        stopLocation();
        if (valueMap["addressDetail"] != null) {
          addressDetail = valueMap["addressDetail"].toString();
        }
        if (StringUtil.isNullOrEmpty(address)) {
          Map<String, dynamic>? map = await LocationApiUtil.geoCode(lat, lng);
          if (map!["code"] == 0) {
            String? address = map["data"];
            Global.currentPosition = SimpleLocation(
                latitude: lat,
                longitude: lng,
                address: address,
                addressDetail: addressDetail);
          }
        } else {
          Global.currentPosition = SimpleLocation(
              latitude: lat,
              longitude: lng,
              address: address,
              addressDetail: addressDetail);
        }
        UserUtil.isLogin().then((value) {
          if (value) {
            LocationApiUtil.uploadLocation(Global.currentPosition!);
          }
        });
        callback(LocationState.success, Global.currentPosition!);
      } catch (e) {
        // ToastUtil.toast("位置处理异常:$e");
      }
    });
    _locationFlutterPlugin!
        .prepareLoc(_getAndroidOption(scanSpan), _getIosOption());
    _locationFlutterPlugin!.startLocation();
    return subscription;
    }
  }
  ///请求定位权限
  static Future<PermissionStatus> requestLocationPermission() async {
    final status = await PermissionUtil.openPermission(PermissionUtil.getLocationPermission());
    final status = await PermissionUtil.openPermission(
        PermissionUtil.getLocationPermission());
    return status;
  }
  static stopLocation() {
    if (_locationFlutterPlugin != null) {
      _locationFlutterPlugin!.stopLocation();
    }
  }
}
lib/utils/map_js_util.dart
New file
@@ -0,0 +1,488 @@
import 'dart:convert';
import 'dart:typed_data';
import 'dart:ui';
import 'dart:io';
import 'dart:math' as Math;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:locations/model/map/location_model.dart';
import 'package:locations/model/map/map_model.dart';
import 'package:locations/utils/ui_constant.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'event_bus_util.dart';
int getLocationDistance(Coordinate p1, Coordinate p2) {
  double EARTH_RADIUS = 6378137;
  int distance = 0;
  double startLongitude = p1.longitude;
  double startLatitude = p1.latitude;
  double endLongitude = p2.longitude;
  double endLatitude = p2.latitude;
  double radLatitude1 = startLatitude * Math.pi / 180.0;
  double radLatitude2 = endLatitude * Math.pi / 180.0;
  double a = (radLatitude1 - radLatitude2).abs();
  double b =
      (startLongitude * Math.pi / 180.0 - endLongitude * Math.pi / 180.0).abs();
  double s = 2 *
      Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
          Math.cos(radLatitude1) *
              Math.cos(radLatitude2) *
              Math.pow(Math.sin(b / 2), 2)));
  s = s * EARTH_RADIUS;
  distance = s.toInt(); // 返回距离单位是米
  return distance;
}
typedef OnWebViewController = void Function(WebViewController controller);
typedef OnPageFinish = void Function(String url);
getMapWebView(BuildContext context, OnWebViewController? controller,
    {OnPageFinish? pageFinish}) {
  return WebView(
    //http://192.168.3.122:8848/test/JsTest.html
    initialUrl: Constant.MAP_LINK,
    onWebViewCreated: (WebViewController webViewController) {
      if (controller != null) {
        controller(webViewController);
      }
    },
    javascriptChannels: {
      JavascriptChannel(
          name: 'mapClick',
          onMessageReceived: (JavascriptMessage message) {
            Map<String, dynamic> map = jsonDecode(message.message);
            eventBus.fire(MapClickEventBus(SimpleLocation(
                latitude: map["lat"] as double,
                longitude: map["lng"] as double)));
          }),
      JavascriptChannel(
          name: 'capture',
          onMessageReceived: (JavascriptMessage message) {
            print("地图截图:${message.message.length}");
            Uint8List list =
                base64.decode(message.message.split(";base64,")[1]);
            getTemporaryDirectory().then((tempDir) {
              String tempPath = tempDir.path;
              String captureImgPath = "$tempPath/capture.jpg";
              if (File(captureImgPath).existsSync()) {
                File(captureImgPath).deleteSync();
              }
              File(captureImgPath).createSync();
              File(captureImgPath).writeAsBytesSync(list);
              eventBus.fire(CaptureEventBus(captureImgPath));
            });
            //保存base64图片
            // //截屏数据
            // message.message;
            //
            // eventBus.fire(MapClickEventBus(SimpleLocation(
            //     latitude: map["lat"] as double,
            //     longitude: map["lng"] as double)));
          }),
    },
    javascriptMode: JavascriptMode.unrestricted,
    navigationDelegate: (NavigationRequest request) {
      print("链接:${request.url}");
      if (!request.url.startsWith("http")) {
        // launch(request.url);
        return NavigationDecision.prevent;
      }
      return NavigationDecision.navigate;
    },
    onPageFinished: (String url) {
      if (pageFinish != null) {
        pageFinish(url);
      }
    },
  );
}
///地图工具类
class MapUtil {
  static Future setStarLayer(WebViewController? _webViewController) async {
    _webViewController!.runJavascript("mapUtil.setBaseLayer(map,3)");
  }
  static Future setCommonLayer(WebViewController? _webViewController) async {
    _webViewController!.runJavascript("mapUtil.setBaseLayer(map,1)");
  }
  static Future capture(WebViewController? _webViewController) async {
    _webViewController!.runJavascript("mapUtil.capture(map)");
  }
  static Future<String> drawLine(List<SimpleLocation> points, Color lineColor,
      WebViewController? _webViewController,
      {int width = 16, bool showPoint = false}) async {
    List pointList = [];
    points.forEach((element) {
      pointList.add([element.longitude, element.latitude]);
    });
    String js = "lineManager.init(map,$showPoint,${jsonEncode(pointList)})";
    print("js:$js");
    String result = await _webViewController!.runJavascriptReturningResult(js);
    print("返回结果:" + result);
    return "line";
  }
  static Future removeLine(
      WebViewController? _webViewController, String id) async {
    String js = "map.removeLayer('$id')";
    print("js:$js");
    return await _webViewController!.runJavascript(js);
  }
  //获取地图的显示参数
  static Future<MapShowInfo> getMapShowParams(
      List<SimpleLocation> points) async {
    if (points.length == 1) {
      return MapShowInfo(points[0], 18);
    } else {
      List<double?> outSides = _getOutSidePoint(points);
      Coordinate center = Coordinate(
          (outSides[0]! + outSides[1]!) / 2, (outSides[2]! + outSides[3]!) / 2);
      //留1/20的边界
      int zoom =
          await _getZoom(outSides[0], outSides[1], outSides[2], outSides[3]);
      return MapShowInfo(
          SimpleLocation(
              latitude: center.latitude, longitude: center.longitude),
          zoom);
    }
  }
  //
  ///获取地图的中心点
  static List<double?> _getOutSidePoint(List<SimpleLocation> points) {
    double? maxLat = points[0].latitude;
    double? minLat = points[0].latitude;
    double? maxLng = points[0].longitude;
    double? minLng = points[0].longitude;
    //获取东南西北四个点
    points.forEach((element) {
      if (maxLat! < element.latitude!) {
        maxLat = element.latitude;
      }
      if (minLat! > element.latitude!) {
        minLat = element.latitude;
      }
      if (maxLng! < element.longitude!) {
        maxLng = element.longitude;
      }
      if (minLng! > element.longitude!) {
        minLng = element.longitude;
      }
    });
    return [maxLat, minLat, maxLng, minLng];
  }
  static Future<int> _getZoom(
      double? maxLat, double? minLat, double? maxLng, double? minLng) async {
    List<double> zoom = [
      50,
      100,
      200,
      500,
      1000,
      2000,
      5000,
      10000,
      20000,
      25000,
      50000,
      100000,
      200000,
      500000,
      1000000,
      2000000
    ]; //级别18到3。
    var pointA = Coordinate(maxLat!, maxLng!); // 创建点坐标A
    var pointB = Coordinate(minLat!, minLng!); // 创建点坐标B
    //留1/20的边界
    int? distance = await getLocationDistance(pointA, pointB);
    distance = (distance / 10).toInt() + distance;
    for (var i = 0, zoomLen = zoom.length; i < zoomLen; i++) {
      if (zoom[i] - distance > 0) {
        return 18 - i + 3; //之所以会多3,是因为地图范围常常是比例尺距离的10倍以上。所以级别会增加3。
      }
    }
    return 18;
  }
  static Future<Marker> addMarker(WebViewController? _webViewController,
      SimpleLocation position, String icon,
      {String? identifier,
      int zIndex = 0,
      Offset? offset,
      double? height,
      double? width}) async {
    Marker marker = Marker(
        position: position,
        id: identifier,
        icon: "data:image/png;base64," + icon,
        zIndex: zIndex,
        width: width,
        height: height,
        centerOffset: offset);
    String js = "markerUtil.addMarker(map, ${jsonEncode(marker.toMap())})";
    print("addMarker:$js");
    /// 添加Marker
    await _webViewController?.runJavascriptReturningResult(js);
    return marker;
  }
  static Future setCenter(
      WebViewController? _webViewController, SimpleLocation location) async {
    await _webViewController!.runJavascript(
        "mapUtil.setCenter(map,[${location.longitude},${location.latitude}])");
  }
  static Future setZoom(WebViewController? _webViewController, int zoom) async {
    await _webViewController!.runJavascript("mapUtil.setCenter(map,$zoom)");
  }
  static removeMarker(WebViewController? _webViewController, Marker marker) {
    String js = "markerUtil.removeMarker(${marker.Id})";
    _webViewController!.runJavascript(js);
  }
  static Future<bool> removeMarkers(
      WebViewController? _webViewController, List<Marker> markers) async {
    List ids = [];
    markers.forEach((element) {
      ids.add(element.Id);
    });
    String js = "markerUtil.removeMarker(${ids})";
    await _webViewController!.runJavascript(js);
    return true;
  }
  static cleanAllMarkers(WebViewController? _webViewController) {
    //_webViewController!.cleanAllMarkers();
  }
  static Future<int?> getDistance(
      SimpleLocation pointA, SimpleLocation pointB) async {
    return await getLocationDistance(
        Coordinate(pointA.latitude!, pointA.longitude!),
        Coordinate(pointB.latitude!, pointB.longitude!));
  }
  static String getDistanceDesc(int distance) {
    if (distance >= 1000) {
      return "${(distance / 1000).toStringAsFixed(1)}米";
    } else {
      return "$distance米";
    }
  }
}
///路径录制管理
///
class TravelRECManager {
  WebViewController? webViewController;
  List<SimpleLocation> positionList = [];
  List<Marker> startMarkers = [];
  TravelRECManager(this.webViewController) {
    webViewController!.runJavascript("lineManager.init(map,false)");
  }
  //开始录制
  startREC(SimpleLocation currentPosition) {
    positionList.clear();
    positionList.add(currentPosition);
    //画起始点
    MapMarkerUtil.addTravelStartMarker(webViewController, currentPosition)
        .then((value) {
      startMarkers = value;
    });
  }
  //添加位置
  addLocation(SimpleLocation lication) async {
    if (positionList.isEmpty) {
      return;
    }
    //计算距离,如果距离在10米内就不添加到数组
    int? distance = await getLocationDistance(
        Coordinate(positionList[positionList.length - 1].latitude!,
            positionList[positionList.length - 1].longitude!),
        Coordinate(lication.latitude!, lication.longitude!));
    if (distance != null && distance >= 10) {
      //上传位置
      positionList.add(lication);
      await webViewController!.runJavascript(
          "lineManager.addPoint([${lication.longitude},${lication.latitude}])");
    }
  }
  //停止录制
  stopREC() async {
    //删除marker
    await MapUtil.removeMarkers(webViewController, startMarkers);
    await webViewController!.runJavascript("lineManager.remove()");
    //清除数据
    positionList.clear();
  }
}
//测距
class MeasureManager {
  WebViewController? webViewController;
  List<SimpleLocation> positionList = [];
  String? lineId;
  MeasureManager(this.webViewController) {}
  //清除所有覆盖物
  clear() async {
    positionList.clear();
    await webViewController!.runJavascript("lineManager.remove()");
  }
  Future<double> _getDistance() async {
    //计算距离
    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(SimpleLocation location) async {
    if (lineId == null) {
      webViewController!.runJavascript("lineManager.init(map,true)");
      lineId = "line";
    }
    //添加点
    positionList.add(location);
    String js =
        "lineManager.addPoint([${location.longitude},${location.latitude}])";
    print("js:$js");
    await webViewController!.runJavascript(js);
    return await _getDistance();
  }
  //回退位置
  Future<double> backPoint() async {
    if (positionList.isEmpty) {
      return 0;
    }
    positionList.removeAt(positionList.length - 1);
    await webViewController!.runJavascript("lineManager.pop()");
    return await _getDistance();
  }
}
class MapMarkerUtil {
  static Future update(WebViewController? _webViewController, String id,
      {String? icon, SimpleLocation? position, bool? visibility}) async {
    Map<String, dynamic> params = {};
    if (icon != null) {
      params["icon"] = "data:image/png;base64," + icon;
    }
    if (position != null) {
      params["position"] = position.toJson();
    }
    if (visibility != null) {
      params["visibility"] = visibility;
    }
    String js = "markerUtil.updateMarker('$id',${jsonEncode(params)})";
    print("js:$js");
    await _webViewController!.runJavascript(js);
  }
  ///添加轨迹开始点
  static Future<List<Marker>> addTravelStartMarker(
      WebViewController? _webViewController, SimpleLocation position) async {
    ByteData byteData =
        await rootBundle.load("assets/images/map/icon_travel_start.png");
    Uint8List pngBytes = byteData.buffer.asUint8List();
    String markerIcon = base64.encode(pngBytes);
    byteData =
        await rootBundle.load("assets/images/map/icon_travel_start_1.png");
    pngBytes = byteData.buffer.asUint8List();
    String marker1Icon = base64.encode(pngBytes);
    Marker marker1 = await MapUtil.addMarker(
        _webViewController, position, marker1Icon,
        offset: Offset(0, 20), width: 28, height: 28);
    Marker marker = await MapUtil.addMarker(
        _webViewController, position, markerIcon,
        zIndex: 1, width: 40, height: 45);
    return [marker, marker1];
  }
  //添加轨迹结束点
  static Future<List<Marker>> addTravelEndMarker(
      WebViewController? _webViewController, SimpleLocation position) async {
    ByteData byteData =
        await rootBundle.load("assets/images/map/icon_travel_end.png");
    Uint8List pngBytes = byteData.buffer.asUint8List();
    String markerIcon = base64.encode(pngBytes);
    byteData = await rootBundle.load("assets/images/map/icon_travel_end_1.png");
    pngBytes = byteData.buffer.asUint8List();
    String marker1Icon = base64.encode(pngBytes);
    Marker marker1 = await MapUtil.addMarker(
        _webViewController, position, marker1Icon,
        offset: Offset(0, 20), width: 28, height: 28, zIndex: 1);
    Marker marker = await MapUtil.addMarker(
        _webViewController, position, markerIcon,
        zIndex: 10, width: 40, height: 45);
    return [marker, marker1];
  }
  ///添加文字图层
  static Future addText(WebViewController? _webViewController,
      SimpleLocation position, String text,
      {Color bgColor = const Color(0xAA0E95FE),
      Color fontColor = Colors.white}) async {
    // BMFText bmfText = BMFText(
    //     text: "     " + text + "  ",
    //     position: position,
    //     bgColor: bgColor,
    //     fontColor: fontColor,
    //     fontSize: 30,
    //     typeFace: BMFTypeFace(
    //         familyName: BMFFamilyName.sMonospace,
    //         textStype: BMFTextStyle.BOLD_ITALIC),
    //     alignY: BMFVerticalAlign.ALIGN_CENTER_VERTICAL,
    //     alignX: BMFHorizontalAlign.ALIGN_LEFT);
    //
    // /// 添加text
    // await _webViewController!.addText(bmfText);
  }
}
lib/utils/map_util.dart
File was deleted
lib/utils/pageutils.dart
@@ -42,7 +42,7 @@
  static void navigateToNextPage(
      BuildContext context, Widget page, PageDataLisener? dataLisener) async {
    final result =
        await Navigator.of(context).push(MaterialPageRoute(builder: (context) {
        await Navigator.of(context).push(CupertinoPageRoute(builder: (context) {
      return page;
    }));
    dataLisener!(result);
@@ -51,7 +51,7 @@
  static void navigateToNextPagePush(Widget page) {
    navigatorKey.currentState!.push(
        MaterialPageRoute(builder: (BuildContext context) => page));
        CupertinoPageRoute (builder: (BuildContext context) => page));
  }
  static void navigateToNextPageWithFinish(
lib/utils/permission_util.dart
@@ -55,9 +55,9 @@
  static Future _removeDenyPermission(Permission permission) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String>? list = prefs.getStringList("permission_delay_list");
    list ?? [];
    list ??= [];
    for (int i = 0; i < list!.length; i++) {
    for (int i = 0; i < list.length; i++) {
      if (list[i] == permission.value.toString()) {
        list.removeAt(i);
        i--;
lib/utils/push_util.dart
@@ -5,14 +5,13 @@
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: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/event_bus_util.dart';
import 'package:locations/utils/pageutils.dart';
import 'package:locations/utils/user_util.dart';
@@ -35,12 +34,20 @@
        if (type == "sos") {
          //SOS
          NavigatorUtil.navigateToNextPagePush(SOSPage(title: ""));
        } else if (type == "requestLocation") {
        } else if (type == "locationInvite") {
          //请求定位
          LocationPage.getLocationInvite(navigatorKey.currentState!.context);
          eventBus.fire(LocationInviteEventBus());
        }
      }, onReceiveMessage: (Map<String, dynamic> message) async {
        print("flutter onReceiveMessage: ${message["message"]}");
        var data = jsonDecode(message["message"]);
        print("flutter onReceiveMessage: $data");
        var type = data["type"];
        if (type == "sos") {
          //SOS
          NavigatorUtil.navigateToNextPagePush(SOSPage(title: ""));
        } else if (type == "locationInvite") {
            eventBus.fire(LocationInviteEventBus());
        }
      }, onReceiveNotificationAuthorization:
              (Map<String, dynamic> message) async {
        print("flutter onReceiveNotificationAuthorization: $message");
@@ -66,13 +73,15 @@
    bool isLogin = await UserUtil.isLogin();
    if (isLogin) {
      UserInfo? user = await UserUtil.getUserInfo();
      await setAlias(user!.id!.toString());
      setAlias(user!.id!.toString());
    }
  }
  ///添加alias
  static Future setAlias(String alias) async {
    await _jpush.setAlias(alias);
    try {
      await _jpush.setAlias(alias);
    } catch (e) {}
  }
  //删除alias
lib/utils/search_util.dart
@@ -1,18 +1,17 @@
import 'package:shared_preferences/shared_preferences.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:flutter_baidu_mapapi_search/flutter_baidu_mapapi_search.dart';
import 'dart:convert' as convert;
import 'package:locations/model/map/poi_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SearchUtil {
  //添加搜索记录
  static addSearchRecord(BMFPoiInfo info) async {
  static addSearchRecord(PoiModel info) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String>? list = prefs.getStringList("searchRecord");
    list ??= [];
    for (int i = 0; i < list.length; i++) {
      BMFPoiInfo poiInfo =BMFPoiInfo.fromMap( convert.jsonDecode(list[i]));
      PoiModel poiInfo = PoiModel.fromJson(convert.jsonDecode(list[i]));
      if (poiInfo.address == info.address) {
        list.removeAt(i);
        break;
@@ -22,20 +21,20 @@
    if (list.length > 10) {
      list.removeRange(10, list.length);
    }
    list.add(convert.jsonEncode(info.toMap()));
    list.add(convert.jsonEncode(info.toJson()));
    prefs.setStringList("searchRecord", list);
  }
  ///获取记录
  static Future<List<BMFPoiInfo>> getRecordList() async {
  static Future<List<PoiModel>> getRecordList() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String>? list = prefs.getStringList("searchRecord");
    print(list);
    list ??= [];
    List<BMFPoiInfo> resultList = [];
    List<PoiModel> resultList = [];
    list.reversed.forEach((element) {
      BMFPoiInfo info =BMFPoiInfo.fromMap( convert.jsonDecode(element));
      PoiModel info = PoiModel.fromJson(convert.jsonDecode(element));
      resultList.add(info);
    });
    return resultList;
lib/utils/share_utils.dart
New file
@@ -0,0 +1,39 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:locations/ui/map/travel.dart';
import 'package:share_plus/share_plus.dart';
class ShareUtil {
  static shareImg(
      BuildContext context, File f, SharePlatform sharePlatform) async {
    if (Platform.isAndroid) {
      const platform = MethodChannel("com.yeshi.location/share"); //分析1
      bool result = false;
      try {
        var params = {
          "path": f.path,
          "platform": sharePlatform.runtimeType.toString()
        };
        switch (sharePlatform) {
          case SharePlatform.qq:
            params["platform"] = "qq";
            break;
          case SharePlatform.wx:
            params["platform"] = "wx";
            break;
          case SharePlatform.sina:
            params["platform"] = "sina";
            break;
        }
        result = await platform.invokeMethod("shareImg", params); //分析2
      } on PlatformException catch (e) {
        print(e.toString());
      }
    } else {
      Share.shareFiles([f.path]);
    }
  }
}
lib/utils/ui_constant.dart
@@ -4,21 +4,37 @@
//滑动效果
class ColorConstant {
  static const Color theme = Color(0xFF0E95FE);
  static const Color theme = Color(0xFF0078FF);//Color(0xFF0E95FE);
  static const Color title = Color(0xFF333333);
}
class Constant {
  static const String APP_NAME =
      "定位追踪轨迹";
  //阿里云授权登录
  static const String ALIYUN_AUTH_SECRETINFO = "v+dQBemz/CZaLI0YP/A5AxxG2YgF7IvP+RNzRkmOGln1bbd7tcMt7uvqt8dvb9ekawjGbvSZ6Y/N9WT0kpUb6lrkS11BxfhCpXtuxmOxei97xP3l5Hd8PVqPv2jPC8uVDRhl6kaAqf/6I1gwL1d7am+8w1sGkYnZ3UEgd9ljDBKjeRbbpp+KEzLiqnKWYDNqMLSRdU0BmzTSGqtkM5c1TYOZgx68NxwE2oM9VzcjQEeFP0yiQatMyNIQ5mJjbyU3zi9qiyMQaeTLHeACvqZ2XCYQBbAeqJh6DPrhIHGlfGc=";
  static const String ALIYUN_AUTH_SECRETINFO =
      "v+dQBemz/CZaLI0YP/A5AxxG2YgF7IvP+RNzRkmOGln1bbd7tcMt7uvqt8dvb9ekawjGbvSZ6Y/N9WT0kpUb6lrkS11BxfhCpXtuxmOxei97xP3l5Hd8PVqPv2jPC8uVDRhl6kaAqf/6I1gwL1d7am+8w1sGkYnZ3UEgd9ljDBKjeRbbpp+KEzLiqnKWYDNqMLSRdU0BmzTSGqtkM5c1TYOZgx68NxwE2oM9VzcjQEeFP0yiQatMyNIQ5mJjbyU3zi9qiyMQaeTLHeACvqZ2XCYQBbAeqJh6DPrhIHGlfGc=";
  //隐私政策链接
  static const String PRIVACY_URL="http://www.baidu.com";
  static const String PRIVACY_URL =
      "http://web.location.izzql.com/privacy.html";
  //用户协议链接
  static const String PROTOCOL_URL="http://www.baidu.com";
  static const String PROTOCOL_URL =
      "http://web.location.izzql.com/user_protocol.html";
  //微信
  static const String WX_APPID="wxd930ea5d5a228f5f";
  static const String WX_UNIVERSAL_LINK="https://your.univerallink.com/link/";
  static const String WX_APPID = "wxd930ea5d5a228f5f";
  static const String WX_UNIVERSAL_LINK = "https://your.univerallink.com/link/";
  static const String APP_DOWNLOAD_LINK =
      "https://a.app.qq.com/o/simple.jsp?pkgname=com.dw.zzql";
  static const String MAP_LINK =
      "http://web.location.izzql.com/map/map.html";
}
lib/utils/user_util.dart
@@ -1,12 +1,20 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx;
import 'package:locations/api/http.dart';
import 'package:locations/model/user/user_info.dart';
import 'package:locations/utils/event_bus_util.dart';
import 'package:locations/utils/location_util.dart';
import 'package:locations/utils/string_util.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'push_util.dart';
Timer? locationTimer;
class UserUtil {
  static const _loginMessageChannel =
@@ -58,6 +66,36 @@
    }
  }
  static Future updateUserInfo(BuildContext context) async {
    int? uid = await getUid();
    if (uid == null) {
      return;
    }
    Map<String, dynamic>? result = await UserApiUtil.getUserInfo(context, uid);
    var code = result!["code"];
    if (code == 0) {
      UserInfo user = UserInfo.fromJson(result["data"]);
      //保存用户信息
      UserUtil.setUserInfo(user);
      //是否启动定位
      locationTimer ??= Timer(Duration(seconds: 30), () {
        //判断是否已经登录
        getUid().then((value) {
          if (value == null) {
            return;
          }
          if (user.needAlwaysLocation!) {
            LocationUtil.startLocation(30, (state, map) {});
          }
        });
      });
    } else if (code == 80001 || code == 80002) {
      await logout();
    }
  }
  static Future<int?> getUid() async {
    UserInfo? user = await getUserInfo();
    if (user != null) {
@@ -73,6 +111,12 @@
  //退出登录
  static logout() async {
    await _logout();
    await PushUtil.removeAlias();
    eventBus.fire(LoginEventBus(false));
  }
  static _logout() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.remove("user_info");
  }
pubspec.lock
@@ -29,20 +29,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.3.1"
  chewie:
    dependency: transitive
    description:
      name: chewie
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.1.0"
  chewie_audio:
    dependency: transitive
    description:
      name: chewie_audio
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.2.0"
  clock:
    dependency: transitive
    description:
@@ -78,6 +64,13 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.0.4"
  date_format:
    dependency: "direct dev"
    description:
      name: date_format
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.4"
  device_info:
    dependency: "direct dev"
    description:
@@ -98,7 +91,7 @@
      name: dio
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "4.0.1"
    version: "4.0.4"
  event_bus:
    dependency: "direct dev"
    description:
@@ -132,41 +125,6 @@
    description: flutter
    source: sdk
    version: "0.0.0"
  flutter_baidu_mapapi_base:
    dependency: "direct dev"
    description:
      name: flutter_baidu_mapapi_base
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.0.0"
  flutter_baidu_mapapi_map:
    dependency: "direct dev"
    description:
      name: flutter_baidu_mapapi_map
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.0.0+2"
  flutter_baidu_mapapi_search:
    dependency: "direct dev"
    description:
      name: flutter_baidu_mapapi_search
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.0.0"
  flutter_baidu_mapapi_utils:
    dependency: "direct dev"
    description:
      name: flutter_baidu_mapapi_utils
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.0.0"
  flutter_bmflocation:
    dependency: "direct dev"
    description:
      name: flutter_bmflocation
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.0-nullsafety.1"
  flutter_datetime_picker:
    dependency: "direct dev"
    description:
@@ -174,20 +132,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.5.1"
  flutter_html:
    dependency: "direct dev"
    description:
      name: flutter_html
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.2.0"
  flutter_layout_grid:
    dependency: transitive
    description:
      name: flutter_layout_grid
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.0.3"
  flutter_lints:
    dependency: "direct dev"
    description:
@@ -195,13 +139,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.0.4"
  flutter_math_fork:
    dependency: transitive
    description:
      name: flutter_math_fork
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.5.0"
  flutter_page_indicator:
    dependency: transitive
    description:
@@ -209,13 +146,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.0.3"
  flutter_plugin_android_lifecycle:
    dependency: transitive
    description:
      name: flutter_plugin_android_lifecycle
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.4"
  flutter_spinkit:
    dependency: "direct dev"
    description:
@@ -223,13 +153,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "5.1.0"
  flutter_svg:
    dependency: transitive
    description:
      name: flutter_svg
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.23.0+1"
  flutter_swiper:
    dependency: "direct dev"
    description:
@@ -255,12 +178,19 @@
      name: flutter_unionad
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.1.9"
    version: "1.2.0"
  flutter_web_plugins:
    dependency: transitive
    description: flutter
    source: sdk
    version: "0.0.0"
  flutter_widget_from_html_core:
    dependency: "direct dev"
    description:
      name: flutter_widget_from_html_core
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.8.3+1"
  fluttertoast:
    dependency: "direct dev"
    description:
@@ -275,6 +205,13 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.6.1+5"
  fwfh_text_style:
    dependency: transitive
    description:
      name: fwfh_text_style
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.5.0+1"
  html:
    dependency: transitive
    description:
@@ -295,7 +232,7 @@
      name: jpush_flutter
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.1.8"
    version: "2.1.9"
  js:
    dependency: transitive
    description:
@@ -338,20 +275,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.0.1"
  nested:
    dependency: transitive
    description:
      name: nested
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.0.0"
  numerus:
    dependency: transitive
    description:
      name: numerus
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.1.1"
  package_info:
    dependency: "direct dev"
    description:
@@ -366,41 +289,41 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.8.0"
  path_drawing:
    dependency: transitive
    description:
      name: path_drawing
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.5.1+1"
  path_parsing:
    dependency: transitive
    description:
      name: path_parsing
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.2.1"
  path_provider:
    dependency: "direct dev"
    description:
      name: path_provider
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.6"
    version: "2.0.8"
  path_provider_android:
    dependency: transitive
    description:
      name: path_provider_android
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.9"
  path_provider_ios:
    dependency: transitive
    description:
      name: path_provider_ios
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.7"
  path_provider_linux:
    dependency: transitive
    description:
      name: path_provider_linux
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.1.1"
    version: "2.1.4"
  path_provider_macos:
    dependency: transitive
    description:
      name: path_provider_macos
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.2"
    version: "2.0.4"
  path_provider_platform_interface:
    dependency: transitive
    description:
@@ -421,7 +344,7 @@
      name: permission_handler
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "8.2.6"
    version: "8.3.0"
  permission_handler_platform_interface:
    dependency: transitive
    description:
@@ -429,20 +352,13 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.7.0"
  petitparser:
    dependency: transitive
    description:
      name: petitparser
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "4.4.0"
  platform:
    dependency: transitive
    description:
      name: platform
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.0.2"
    version: "3.1.0"
  plugin_platform_interface:
    dependency: transitive
    description:
@@ -457,13 +373,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "4.2.4"
  provider:
    dependency: transitive
    description:
      name: provider
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "6.0.1"
  pull_to_refresh:
    dependency: "direct dev"
    description:
@@ -485,13 +394,13 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "4.0.0"
  quiver:
    dependency: transitive
  screenshot:
    dependency: "direct dev"
    description:
      name: quiver
      name: screenshot
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.0.1+1"
    version: "1.2.3"
  share_plus:
    dependency: "direct dev"
    description:
@@ -538,6 +447,20 @@
    dependency: "direct dev"
    description:
      name: shared_preferences
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.11"
  shared_preferences_android:
    dependency: transitive
    description:
      name: shared_preferences_android
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.9"
  shared_preferences_ios:
    dependency: transitive
    description:
      name: shared_preferences_ios
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.8"
@@ -594,7 +517,7 @@
      name: sqflite
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.0+4"
    version: "2.0.1"
  sqflite_common:
    dependency: transitive
    description:
@@ -651,13 +574,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.1.6"
  tuple:
    dependency: transitive
    description:
      name: tuple
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.0"
  typed_data:
    dependency: transitive
    description:
@@ -713,7 +629,7 @@
      name: url_launcher_web
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.4"
    version: "2.0.5"
  url_launcher_windows:
    dependency: transitive
    description:
@@ -721,13 +637,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.2"
  uuid:
    dependency: transitive
    description:
      name: uuid
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "3.0.5"
  vector_math:
    dependency: transitive
    description:
@@ -735,97 +644,41 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.1.0"
  video_player:
    dependency: transitive
    description:
      name: video_player
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.2.7"
  video_player_platform_interface:
    dependency: transitive
    description:
      name: video_player_platform_interface
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "4.2.0"
  video_player_web:
    dependency: transitive
    description:
      name: video_player_web
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.0.4"
  wakelock:
    dependency: transitive
    description:
      name: wakelock
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.5.6"
  wakelock_macos:
    dependency: transitive
    description:
      name: wakelock_macos
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.4.0"
  wakelock_platform_interface:
    dependency: transitive
    description:
      name: wakelock_platform_interface
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.3.0"
  wakelock_web:
    dependency: transitive
    description:
      name: wakelock_web
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.4.0"
  wakelock_windows:
    dependency: transitive
    description:
      name: wakelock_windows
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.2.0"
  webview_flutter:
    dependency: "direct dev"
    description:
      name: webview_flutter
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.3.1"
    version: "2.8.0"
  webview_flutter_android:
    dependency: transitive
    description:
      name: webview_flutter_android
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.2.1"
    version: "2.8.0"
  webview_flutter_platform_interface:
    dependency: transitive
    description:
      name: webview_flutter_platform_interface
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "1.4.0"
    version: "1.8.0"
  webview_flutter_wkwebview:
    dependency: transitive
    description:
      name: webview_flutter_wkwebview
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.3.0"
    version: "2.7.1"
  win32:
    dependency: transitive
    description:
      name: win32
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "2.2.10"
    version: "2.3.1"
  xdg_directories:
    dependency: transitive
    description:
@@ -833,13 +686,6 @@
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "0.2.0"
  xml:
    dependency: transitive
    description:
      name: xml
      url: "https://pub.flutter-io.cn"
    source: hosted
    version: "5.3.1"
sdks:
  dart: ">=2.14.0 <3.0.0"
  flutter: ">=2.5.0"
pubspec.yaml
@@ -62,21 +62,21 @@
  fluttertoast: ^8.0.8
  #百度地图
  #  flutter_bmflocation: ^2.0.0-nullsafety.1
  flutter_baidu_mapapi_base: ^3.0.0
  flutter_baidu_mapapi_map: ^3.0.0+2
  flutter_baidu_mapapi_search: ^3.0.0
  flutter_baidu_mapapi_utils: ^3.0.0
#  flutter_baidu_mapapi_base: ^3.0.0
#  flutter_baidu_mapapi_map: ^3.0.0+2
#  flutter_baidu_mapapi_search: ^3.0.0
#  flutter_baidu_mapapi_utils: ^3.0.0
  #百度定位
  flutter_bmflocation: ^2.0.0-nullsafety.1
#  flutter_bmflocation: ^2.0.0-nullsafety.1
  #  flutter_bmflocation: ^2.0.0-nullsafety.1
  webview_flutter: ^2.3.1
  webview_flutter: ^2.8.0
  #穿山甲广告
  flutter_unionad: ^1.1.9
  #广点通广告
  flutter_tencentad: ^1.1.0
  flutter_html: ^2.2.0
  flutter_widget_from_html_core: ^0.8.3
  #时间日期选择
  flutter_datetime_picker: ^1.5.1
@@ -112,6 +112,11 @@
  launch_review: ^3.0.1
  date_format: ^2.0.4
  #截图插件
  screenshot: ^1.2.3
# For information on the generic Dart part of this file, see the