zk 1 год назад
Родитель
Сommit
7331dc7677
48 измененных файлов с 3167 добавлено и 146 удалено
  1. 45 8
      android/app/build.gradle
  2. 128 0
      android/app/proguard-rules.pro
  3. 6 1
      android/app/src/main/AndroidManifest.xml
  4. 15 0
      android/build.gradle
  5. 19 0
      assets/color/color.xml
  6. 48 0
      assets/color/common_color.xml
  7. 12 0
      assets/string/base/string.xml
  8. 14 0
      lib/base/app_base_request.dart
  9. 66 0
      lib/base/app_base_request.g.dart
  10. 168 0
      lib/base/atmob_platform_info.dart
  11. 30 0
      lib/base/base_controller.dart
  12. 54 0
      lib/base/base_page.dart
  13. 139 0
      lib/base/base_request.dart
  14. 63 0
      lib/base/base_request.g.dart
  15. 19 0
      lib/base/base_response.dart
  16. 39 0
      lib/base/base_response.g.dart
  17. 40 0
      lib/base/base_view.dart
  18. 13 0
      lib/data/api/atmob_api.dart
  19. 53 0
      lib/data/api/atmob_api.g.dart
  20. 10 0
      lib/data/const/build_config.dart
  21. 48 0
      lib/data/const/constants.dart
  22. 36 0
      lib/data/const/error_code.dart
  23. 29 0
      lib/data/repositories/account_repository.dart
  24. 14 0
      lib/device/device_info_util.dart
  25. 20 0
      lib/device/platform_android_info.dart
  26. 32 0
      lib/device/platform_ios_info.dart
  27. 43 0
      lib/di/get_it.config.dart
  28. 12 0
      lib/di/get_it.dart
  29. 29 0
      lib/di/network_module.dart
  30. 153 101
      lib/main.dart
  31. 13 0
      lib/module/main/main_controller.dart
  32. 21 0
      lib/module/main/main_page.dart
  33. 32 0
      lib/module/splash/splash_controller.dart
  34. 33 0
      lib/module/splash/splash_page.dart
  35. 12 0
      lib/resource/assets.gen.dart
  36. 159 0
      lib/resource/colors.gen.dart
  37. 28 0
      lib/resource/string.gen.dart
  38. 7 0
      lib/resource/string_source.dart
  39. 32 0
      lib/router/app_pages.dart
  40. 23 0
      lib/utils/app_info_util.dart
  41. 208 0
      lib/utils/async_util.dart
  42. 29 0
      lib/utils/atmob_log.dart
  43. 20 0
      lib/utils/de_bounce.dart
  44. 39 0
      lib/utils/http_handler.dart
  45. 52 0
      lib/utils/mmkv_util.dart
  46. 26 0
      lib/utils/toast_util.dart
  47. 949 5
      pubspec.lock
  48. 87 31
      pubspec.yaml

+ 45 - 8
android/app/build.gradle

@@ -7,7 +7,7 @@ plugins {
 
 android {
     namespace = "com.atmob.abroad_location"
-    compileSdk = flutter.compileSdkVersion
+    compileSdk = rootProject.ext.compileSdkVersion
     ndkVersion = flutter.ndkVersion
 
     compileOptions {
@@ -19,22 +19,59 @@ android {
         jvmTarget = JavaVersion.VERSION_1_8
     }
 
+    configurations.configureEach {
+        resolutionStrategy {
+            // don't cache changing modules at all
+            cacheChangingModulesFor 10, 'seconds'
+        }
+    }
+
     defaultConfig {
-        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
-        applicationId = "com.atmob.abroad_location"
-        // You can update the following values to match your application needs.
-        // For more information, see: https://flutter.dev/to/review-gradle-config.
-        minSdk = flutter.minSdkVersion
-        targetSdk = flutter.targetSdkVersion
+        applicationId = rootProject.ext.applicationId
+        minSdk = rootProject.ext.minSdkVersion
+        targetSdk = rootProject.ext.targetSdkVersion
         versionCode = flutter.versionCode
         versionName = flutter.versionName
+
+        ndk {
+            //noinspection ChromeOsAbiSupport
+            abiFilters "arm64-v8a"
+        }
     }
 
+    signingConfigs {
+        location {
+            storeFile file("keystore/location.jks")
+            storePassword "location888"
+            keyAlias "location"
+            keyPassword "location888"
+        }
+    }
+
+
     buildTypes {
+        debug {
+            // Note: The debug build type must have the same signing config as the main build type.
+            signingConfig signingConfigs.location
+        }
         release {
             // TODO: Add your own signing config for the release build.
             // Signing with the debug keys for now, so `flutter run --release` works.
-            signingConfig = signingConfigs.debug
+            minifyEnabled true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            signingConfig signingConfigs.location
+        }
+    }
+
+    applicationVariants.configureEach { variant ->
+        def date = new Date().format("YYMMddHHmmss")
+        variant.outputs.configureEach {
+            def fileName = "${defaultConfig.applicationId}" +
+                    "-v${defaultConfig.versionName}" +
+                    "-${variant.buildType.name}" +
+                    "-${date}" +
+                    ".apk"
+            outputFileName = fileName
         }
     }
 }

+ 128 - 0
android/app/proguard-rules.pro

@@ -0,0 +1,128 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+-dontshrink
+
+## Log
+-assumenosideeffects class android.util.Log {
+    public static *** d(...);
+    public static *** v(...);
+    public static *** w(...);
+    public static *** i(...);
+    public static *** wtf(...);
+}
+-assumenosideeffects class com.atmob.common.logging.AtmobLog {
+    public static *** d(...);
+    public static *** v(...);
+    public static *** w(...);
+    public static *** i(...);
+    public static *** wtf(...);
+}
+
+## native
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+## res
+-keepclassmembers class **.R$* {
+    public static <fields>;
+}
+
+## ViewBinding & DataBinding
+-keep class * implements androidx.viewbinding.ViewBinding {
+    public inflate(android.view.LayoutInflater);
+    public inflate(android.view.LayoutInflater, android.view.ViewGroup, boolean);
+}
+
+# common config end
+
+# RxJava start
+-dontwarn java.util.concurrent.Flow*
+# RxJava end
+
+# Glide start
+-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep class * extends com.bumptech.glide.module.AppGlideModule {
+ <init>(...);
+}
+-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
+  **[] $VALUES;
+  public *;
+}
+-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
+  *** rewind();
+}
+# Glide end
+
+# Turbo start
+-keep class com.plutus.common.turbo.beans.** {*;}
+# Turbo end
+
+# bugly start
+-dontwarn com.tencent.bugly.**
+-keep public class com.tencent.bugly.**{*;}
+# bugly end
+
+# Umeng start
+-keep class com.umeng.** {*;}
+-keep class org.repackage.** {*;}
+-keepclassmembers class * {
+   public <init> (org.json.JSONObject);
+}
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+# Umeng end
+
+#oaid miitmdid start
+-keep class com.bun.miitmdid.core.** {*;}
+-keep class com.bun.lib.**{*;}
+-keep class XI.CA.XI.**{*;}
+-keep class XI.K0.XI.**{*;}
+-keep class XI.XI.K0.**{*;}
+-keep class XI.vs.K0.**{*;}
+-keep class XI.xo.XI.XI.**{*;}
+-keep class com.asus.msa.SupplementaryDID.**{*;}
+-keep class com.asus.msa.sdid.**{*;}
+-keep class com.bun.lib.**{*;}
+-keep class com.bun.miitmdid.**{*;}
+-keep class com.huawei.hms.ads.identifier.**{*;}
+-keep class com.samsung.android.deviceidservice.**{*;}
+-keep class org.json.**{*;}
+-keep public class com.netease.nis.sdkwrapper.Utils {
+public <methods>;
+}
+
+#
+-keep class androidx.core.content.FileProvider { *; }
+-keep class android.support.v4.content.FileProvider { *; }
+
+#oaid miitmdid end
+
+#flutter start
+
+-keep class io.flutter.** { *; }
+
+#flutter end
+
+

+ 6 - 1
android/app/src/main/AndroidManifest.xml

@@ -1,5 +1,10 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.atmob.abroad_location">
+
+
+    <uses-permission android:name="android.permission.INTERNET" />
+
     <application
+        android:allowBackup="false"
         android:label="abroad_location"
         android:name="${applicationName}"
         android:icon="@mipmap/ic_launcher">

+ 15 - 0
android/build.gradle

@@ -1,7 +1,22 @@
 allprojects {
+    ext {
+        compileSdkVersion = 34
+        applicationId = "com.atmob.abroad_location"
+        minSdkVersion = 23
+        targetSdkVersion = 33
+    }
     repositories {
         google()
         mavenCentral()
+
+        maven {
+            credentials {
+                username "$atmob_maven_username"
+                password "$atmob_maven_password"
+            }
+            allowInsecureProtocol = true
+            url "$atmob_maven_url/repository/android-group/"
+        }
     }
 }
 

+ 19 - 0
assets/color/color.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="transparent">#00FFFFFF</color>
+    <!--主题色文字色彩-->
+    <color name="colorPrimary">#7B7DFF</color>
+    <!--主题色点缀色-->
+    <color name="colorAccentPrimary">#6399FF</color>
+    <!--主题色背景色-->
+    <color name="bgColorPrimary">#F5F6F8</color>
+    <!--主题色背景色-->
+    <color name="colorPrimaryLight">#F6FAFF</color>
+
+    <!--主要文字色彩-->
+    <color name="primaryTextColor">#E6000000</color>
+    <!--次要文字色彩-->
+    <color name="secondaryTextColor">#99000000</color>
+
+
+</resources>

+ 48 - 0
assets/color/common_color.xml

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- 白色 -->
+    <color name="white">#FFFFFFFF</color>
+    <color name="white95">#F2FFFFFF</color>
+    <color name="white90">#E6FFFFFF</color>
+    <color name="white87">#DEFFFFFF</color>
+    <color name="white85">#D9FFFFFF</color>
+    <color name="white80">#CCFFFFFF</color>
+    <color name="white75">#BFFFFFFF</color>
+    <color name="white70">#B3FFFFFF</color>
+    <color name="white65">#A6FFFFFF</color>
+    <color name="white60">#99FFFFFF</color>
+    <color name="white55">#8CFFFFFF</color>
+    <color name="white50">#80FFFFFF</color>
+    <color name="white45">#73FFFFFF</color>
+    <color name="white40">#66FFFFFF</color>
+    <color name="white35">#59FFFFFF</color>
+    <color name="white30">#4DFFFFFF</color>
+    <color name="white25">#40FFFFFF</color>
+    <color name="white20">#33FFFFFF</color>
+    <color name="white15">#26FFFFFF</color>
+    <color name="white10">#1AFFFFFF</color>
+    <color name="white5">#0DFFFFFF</color>
+
+    <!-- 黑色 -->
+    <color name="black">#FF000000</color>
+    <color name="black95">#F2000000</color>
+    <color name="black90">#E6000000</color>
+    <color name="black85">#D9000000</color>
+    <color name="black80">#CC000000</color>
+    <color name="black75">#B000000F</color>
+    <color name="black70">#B3000000</color>
+    <color name="black65">#A6000000</color>
+    <color name="black60">#99000000</color>
+    <color name="black55">#8C000000</color>
+    <color name="black50">#80000000</color>
+    <color name="black45">#73000000</color>
+    <color name="black40">#66000000</color>
+    <color name="black35">#59000000</color>
+    <color name="black30">#4D000000</color>
+    <color name="black25">#40000000</color>
+    <color name="black20">#33000000</color>
+    <color name="black15">#26000000</color>
+    <color name="black10">#1A000000</color>
+    <color name="black5">#0D000000</color>
+
+</resources>

+ 12 - 0
assets/string/base/string.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">Location</string>
+
+    <string name="account_no_login">Not logged in.</string>
+    <string name="loading_more">Load more</string>
+    <string name="load_pull_up">Pull up to load</string>
+    <string name="loading_txt">Loading…</string>
+    <string name="load_no_data">No more data</string>
+    <string name="load_failed">Failed to load</string>
+    <string name="network_error">Network error</string>
+</resources>

+ 14 - 0
lib/base/app_base_request.dart

@@ -0,0 +1,14 @@
+import 'package:json_annotation/json_annotation.dart';
+import '../data/repositories/account_repository.dart';
+import 'base_request.dart';
+
+part 'app_base_request.g.dart';
+
+@JsonSerializable()
+class AppBaseRequest extends BaseRequest {
+  @JsonKey(name: 'authToken')
+  String? authToken = AccountRepository.token;
+
+  @override
+  Map<String, dynamic> toJson() => _$AppBaseRequestToJson(this);
+}

+ 66 - 0
lib/base/app_base_request.g.dart

@@ -0,0 +1,66 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'app_base_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+AppBaseRequest _$AppBaseRequestFromJson(Map<String, dynamic> json) =>
+    AppBaseRequest()
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$AppBaseRequestToJson(AppBaseRequest instance) =>
+    <String, dynamic>{
+      'appPlatform': instance.appPlatform,
+      'os': instance.os,
+      'osVersion': instance.osVersion,
+      'packageName': instance.packageName,
+      'appVersionName': instance.appVersionName,
+      'appVersionCode': instance.appVersionCode,
+      'channelName': instance.channelName,
+      'appId': instance.appId,
+      'tgPlatform': instance.tgPlatform,
+      'oaid': instance.oaid,
+      'aaid': instance.aaid,
+      'androidId': instance.androidId,
+      'imei': instance.imei,
+      'simImei0': instance.simImei0,
+      'simImei1': instance.simImei1,
+      'mac': instance.mac,
+      'idfa': instance.idfa,
+      'idfv': instance.idfv,
+      'machineId': instance.machineId,
+      'brand': instance.brand,
+      'model': instance.model,
+      'wifiName': instance.wifiName,
+      'region': instance.region,
+      'locLng': instance.locLng,
+      'locLat': instance.locLat,
+      'authToken': instance.authToken,
+    };

+ 168 - 0
lib/base/atmob_platform_info.dart

@@ -0,0 +1,168 @@
+class AtmobPlatformInfo {
+  AtmobPlatformInfo();
+
+  /// 设备信息
+  /// Android特有
+  String? _oaid;
+  String? _aaid;
+  String? _androidId;
+  String? _imei;
+  String? _simImei0;
+  String? _simImei1;
+  String? _mac;
+
+  //渠道信息
+  String? _channelName;
+  int? _appId;
+  int? _tgPlatform;
+
+  /// iOS特有
+  String? _idfa;
+  String? _idfv;
+
+  /// 公共设备信息
+  String? _machineId; //给web使用
+  String? _brand;
+  String? _model;
+  String? _wifiName;
+
+  /// 地理位置信息
+  String? _region;
+  double? _locLng;
+  double? _locLat;
+
+  AtmobPlatformInfo setChannelName(String? channelName) {
+    _channelName = channelName;
+    return this;
+  }
+
+  AtmobPlatformInfo setTgPlatform(int? tgPlatform) {
+    _tgPlatform = tgPlatform;
+    return this;
+  }
+
+  AtmobPlatformInfo setAppId(int? appId) {
+    _appId = appId;
+    return this;
+  }
+
+  AtmobPlatformInfo setOaid(String? oaid) {
+    _oaid = oaid;
+    return this;
+  }
+
+  AtmobPlatformInfo setAaid(String? aaid) {
+    _aaid = aaid;
+    return this;
+  }
+
+  AtmobPlatformInfo setAndroidId(String? androidId) {
+    _androidId = androidId;
+    return this;
+  }
+
+  AtmobPlatformInfo setImei(String? imei) {
+    _imei = imei;
+    return this;
+  }
+
+  AtmobPlatformInfo setSimImei0(String? simImei0) {
+    _simImei0 = simImei0;
+    return this;
+  }
+
+  AtmobPlatformInfo setSimImei1(String? simImei1) {
+    _simImei1 = simImei1;
+    return this;
+  }
+
+  AtmobPlatformInfo setMac(String? mac) {
+    _mac = mac;
+    return this;
+  }
+
+  AtmobPlatformInfo setIdfa(String? idfa) {
+    _idfa = idfa;
+    return this;
+  }
+
+  AtmobPlatformInfo setIdfv(String? idfv) {
+    _idfv = idfv;
+    return this;
+  }
+
+  AtmobPlatformInfo setMachineId(String? machineId) {
+    _machineId = machineId;
+    return this;
+  }
+
+  AtmobPlatformInfo setBrand(String? brand) {
+    _brand = brand;
+    return this;
+  }
+
+  AtmobPlatformInfo setModel(String? model) {
+    _model = model;
+    return this;
+  }
+
+  AtmobPlatformInfo setWifiName(String? wifiName) {
+    _wifiName = wifiName;
+    return this;
+  }
+
+  AtmobPlatformInfo setRegion(String? region) {
+    _region = region;
+    return this;
+  }
+
+  AtmobPlatformInfo setLocLng(double? locLng) {
+    _locLng = locLng;
+    return this;
+  }
+
+  AtmobPlatformInfo setLocLat(double? locLat) {
+    _locLat = locLat;
+    return this;
+  }
+
+  double? get locLat => _locLat;
+
+  double? get locLng => _locLng;
+
+  String? get region => _region;
+
+  String? get wifiName => _wifiName;
+
+  String? get model => _model;
+
+  String? get brand => _brand;
+
+  String? get machineId => _machineId;
+
+  String? get idfv => _idfv;
+
+  String? get idfa => _idfa;
+
+  String? get mac => _mac;
+
+  String? get simImei1 => _simImei1;
+
+  String? get simImei0 => _simImei0;
+
+  String? get imei => _imei;
+
+  String? get androidId => _androidId;
+
+  String? get aaid => _aaid;
+
+  String? get oaid => _oaid;
+
+  String? get channelName => _channelName;
+
+  int? get tgPlatform => _tgPlatform;
+
+  int? get appId => _appId;
+}
+
+final AtmobPlatformInfo atmobPlatformInfo = AtmobPlatformInfo();

+ 30 - 0
lib/base/base_controller.dart

@@ -0,0 +1,30 @@
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+
+class BaseController extends GetxController {
+  Map? parameters;
+
+  @override
+  void onInit() {
+    super.onInit();
+    _initParameters();
+  }
+
+  void _initParameters() {
+    var getParameters = Get.parameters;
+    var getArguments = Get.arguments;
+    parameters ??= <dynamic, dynamic>{};
+    parameters?.addAll(getParameters);
+    if (getArguments != null && getArguments is Map) {
+      parameters?.addAll(getArguments);
+    }
+  }
+
+  /// 隐藏键盘
+  void hideKeyboard(BuildContext context) {
+    FocusScopeNode currentFocus = FocusScope.of(context);
+    if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
+      FocusManager.instance.primaryFocus!.unfocus();
+    }
+  }
+}

+ 54 - 0
lib/base/base_page.dart

@@ -0,0 +1,54 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+
+import 'base_view.dart';
+
+abstract class BasePage<T extends GetxController> extends BaseView<T> {
+  const BasePage({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return AnnotatedRegion<SystemUiOverlayStyle>(
+      value: SystemUiOverlayStyle.light.copyWith(
+        statusBarColor: Colors.transparent,
+        statusBarBrightness: _getStatusBarDarkFont(),
+        statusBarIconBrightness: _getStatusIconBarDarkFont(),
+        systemNavigationBarColor: navigationBarColor(),
+        systemNavigationBarIconBrightness: _getNavigationBarDarkFont(),
+      ),
+      child: buildBoot(
+        immersive() ? buildBody(context) : SafeArea(child: buildBody(context)),
+      ),
+    );
+  }
+
+  Brightness _getStatusBarDarkFont() {
+    return statusBarDarkFont() ? Brightness.light : Brightness.dark;
+  }
+
+  Brightness _getStatusIconBarDarkFont() {
+    return statusBarDarkFont() ? Brightness.dark : Brightness.light;
+  }
+
+  Brightness _getNavigationBarDarkFont() {
+    return statusBarDarkFont() ? Brightness.dark : Brightness.light;
+  }
+
+  bool statusBarDarkFont() {
+    return true;
+  }
+
+  bool navigationBarDarkFont() {
+    return true;
+  }
+
+  bool immersive() {
+    return false;
+  }
+
+  Color navigationBarColor() {
+    return Colors.transparent;
+  }
+}

+ 139 - 0
lib/base/base_request.dart

@@ -0,0 +1,139 @@
+import 'dart:io';
+import 'package:flutter/cupertino.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'atmob_platform_info.dart';
+import '../utils/app_info_util.dart';
+
+part 'base_request.g.dart';
+
+@JsonSerializable()
+class BaseRequest {
+  /// 平台类型: 1-Android 2-iOS 3-移动H5 4-PC_WEB 5-微信小程序 6-微信小游戏
+  /// 7-微信公众号 8-抖音小程序 9-抖音小游戏 10-鸿蒙APP
+  @JsonKey(name: "appPlatform")
+  late int appPlatform = 0;
+
+  /// 操作系统:android、ios、mac、windows、linux、harmony
+  @JsonKey(name: "os")
+  late String os = "unknown";
+  @JsonKey(name: "osVersion")
+  late String osVersion;
+
+  /// 包信息
+  @JsonKey(name: "packageName")
+  String? packageName;
+  @JsonKey(name: "appVersionName")
+  String? appVersionName;
+  @JsonKey(name: "appVersionCode")
+  int? appVersionCode;
+
+  /// 渠道信息, iOS没有渠道信息, Android通过渠道包工具获取
+  @JsonKey(name: "channelName")
+  String? channelName;
+  @JsonKey(name: "appId")
+  int? appId;
+  @JsonKey(name: "tgPlatform")
+  int? tgPlatform;
+
+  /// 设备信息
+  /// Android特有
+  @JsonKey(name: "oaid")
+  String? oaid;
+  @JsonKey(name: "aaid")
+  String? aaid;
+  @JsonKey(name: "androidId")
+  String? androidId;
+  @JsonKey(name: "imei")
+  String? imei;
+  @JsonKey(name: "simImei0")
+  String? simImei0;
+  @JsonKey(name: "simImei1")
+  String? simImei1;
+  @JsonKey(name: "mac")
+  String? mac;
+
+  /// iOS特有
+  @JsonKey(name: "idfa")
+  String? idfa;
+  @JsonKey(name: "idfv")
+  String? idfv;
+
+  /// 公共设备信息
+  @JsonKey(name: "machineId")
+  String? machineId; //给web使用
+  @JsonKey(name: "brand")
+  String? brand;
+  @JsonKey(name: "model")
+  String? model;
+  @JsonKey(name: "wifiName")
+  String? wifiName;
+
+  /// 地理位置信息
+  @JsonKey(name: "region")
+  String? region;
+  @JsonKey(name: "locLng")
+  double? locLng;
+  @JsonKey(name: "locLat")
+  double? locLat;
+
+  BaseRequest() {
+    initPlatformOS();
+    initPackageInfo();
+    initChannelInfo();
+    initDeviceInfo();
+  }
+
+  Map<String, dynamic> toJson() => _$BaseRequestToJson(this);
+
+  void initPlatformOS() {
+    if (Platform.isAndroid) {
+      appPlatform = 1;
+      os = "android";
+    } else if (Platform.isIOS) {
+      appPlatform = 2;
+      os = "ios";
+    } else if (Platform.isMacOS) {
+      os = "mac";
+    } else if (Platform.isWindows) {
+      os = "windows";
+    } else if (Platform.isLinux) {
+      os = "linux";
+    } else if (Platform.isFuchsia) {
+      os = "fuchsia";
+    }
+    osVersion = Platform.operatingSystemVersion;
+
+    debugPrint("os: $os, osVersion: $osVersion");
+  }
+
+  void initPackageInfo() {
+    packageName = appInfoUtil.packageName;
+    appVersionName = appInfoUtil.appVersionName;
+    appVersionCode = appInfoUtil.appVersionCode;
+  }
+
+  void initChannelInfo() {
+    channelName = atmobPlatformInfo.channelName;
+    appId = atmobPlatformInfo.appId;
+    tgPlatform = atmobPlatformInfo.tgPlatform;
+  }
+
+  void initDeviceInfo() {
+    oaid = atmobPlatformInfo.oaid;
+    aaid = atmobPlatformInfo.aaid;
+    androidId = atmobPlatformInfo.androidId;
+    imei = atmobPlatformInfo.imei;
+    simImei0 = atmobPlatformInfo.simImei0;
+    simImei1 = atmobPlatformInfo.simImei1;
+    mac = atmobPlatformInfo.mac;
+    idfa = atmobPlatformInfo.idfa;
+    idfv = atmobPlatformInfo.idfv;
+    machineId = atmobPlatformInfo.machineId;
+    brand = atmobPlatformInfo.brand;
+    model = atmobPlatformInfo.model;
+    wifiName = atmobPlatformInfo.wifiName;
+    region = atmobPlatformInfo.region;
+    locLng = atmobPlatformInfo.locLng;
+    locLat = atmobPlatformInfo.locLat;
+  }
+}

+ 63 - 0
lib/base/base_request.g.dart

@@ -0,0 +1,63 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'base_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+BaseRequest _$BaseRequestFromJson(Map<String, dynamic> json) => BaseRequest()
+  ..appPlatform = (json['appPlatform'] as num).toInt()
+  ..os = json['os'] as String
+  ..osVersion = json['osVersion'] as String
+  ..packageName = json['packageName'] as String?
+  ..appVersionName = json['appVersionName'] as String?
+  ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+  ..channelName = json['channelName'] as String?
+  ..appId = (json['appId'] as num?)?.toInt()
+  ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+  ..oaid = json['oaid'] as String?
+  ..aaid = json['aaid'] as String?
+  ..androidId = json['androidId'] as String?
+  ..imei = json['imei'] as String?
+  ..simImei0 = json['simImei0'] as String?
+  ..simImei1 = json['simImei1'] as String?
+  ..mac = json['mac'] as String?
+  ..idfa = json['idfa'] as String?
+  ..idfv = json['idfv'] as String?
+  ..machineId = json['machineId'] as String?
+  ..brand = json['brand'] as String?
+  ..model = json['model'] as String?
+  ..wifiName = json['wifiName'] as String?
+  ..region = json['region'] as String?
+  ..locLng = (json['locLng'] as num?)?.toDouble()
+  ..locLat = (json['locLat'] as num?)?.toDouble();
+
+Map<String, dynamic> _$BaseRequestToJson(BaseRequest instance) =>
+    <String, dynamic>{
+      'appPlatform': instance.appPlatform,
+      'os': instance.os,
+      'osVersion': instance.osVersion,
+      'packageName': instance.packageName,
+      'appVersionName': instance.appVersionName,
+      'appVersionCode': instance.appVersionCode,
+      'channelName': instance.channelName,
+      'appId': instance.appId,
+      'tgPlatform': instance.tgPlatform,
+      'oaid': instance.oaid,
+      'aaid': instance.aaid,
+      'androidId': instance.androidId,
+      'imei': instance.imei,
+      'simImei0': instance.simImei0,
+      'simImei1': instance.simImei1,
+      'mac': instance.mac,
+      'idfa': instance.idfa,
+      'idfv': instance.idfv,
+      'machineId': instance.machineId,
+      'brand': instance.brand,
+      'model': instance.model,
+      'wifiName': instance.wifiName,
+      'region': instance.region,
+      'locLng': instance.locLng,
+      'locLat': instance.locLat,
+    };

+ 19 - 0
lib/base/base_response.dart

@@ -0,0 +1,19 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'base_response.g.dart';
+
+@JsonSerializable(genericArgumentFactories: true)
+class BaseResponse<T> {
+  @JsonKey(name: 'code')
+  int? code;
+
+  @JsonKey(name: 'msg')
+  String? message;
+  T? data;
+
+  BaseResponse(this.data, this.code, this.message);
+
+  factory BaseResponse.fromJson(
+          Map<String, dynamic> json, T Function(Object? json) fromJsonT) =>
+      _$BaseResponseFromJson(json, fromJsonT);
+}

+ 39 - 0
lib/base/base_response.g.dart

@@ -0,0 +1,39 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'base_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+BaseResponse<T> _$BaseResponseFromJson<T>(
+  Map<String, dynamic> json,
+  T Function(Object? json) fromJsonT,
+) =>
+    BaseResponse<T>(
+      _$nullableGenericFromJson(json['data'], fromJsonT),
+      (json['code'] as num?)?.toInt(),
+      json['msg'] as String?,
+    );
+
+Map<String, dynamic> _$BaseResponseToJson<T>(
+  BaseResponse<T> instance,
+  Object? Function(T value) toJsonT,
+) =>
+    <String, dynamic>{
+      'code': instance.code,
+      'msg': instance.message,
+      'data': _$nullableGenericToJson(instance.data, toJsonT),
+    };
+
+T? _$nullableGenericFromJson<T>(
+  Object? input,
+  T Function(Object? json) fromJson,
+) =>
+    input == null ? null : fromJson(input);
+
+Object? _$nullableGenericToJson<T>(
+  T? input,
+  Object? Function(T value) toJson,
+) =>
+    input == null ? null : toJson(input);

+ 40 - 0
lib/base/base_view.dart

@@ -0,0 +1,40 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:get/get.dart';
+import 'package:get/get_state_manager/src/simple/get_view.dart';
+
+abstract class BaseView<T extends GetxController> extends GetView<T> {
+  const BaseView({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return buildBoot(buildBody(context));
+  }
+
+  Widget buildBoot(Widget child) {
+    return GestureDetector(
+      onTap: () {
+        backgroundOnTapEvent();
+      },
+      child: Container(
+        height: viewHeight(),
+        color: backgroundColor(),
+        child: child,
+      ),
+    );
+  }
+
+  Widget buildBody(BuildContext context);
+
+  // 点击空白处
+  void backgroundOnTapEvent() {}
+
+  Color backgroundColor() {
+    return Colors.white;
+  }
+
+  double? viewHeight() {
+    return null;
+  }
+}

+ 13 - 0
lib/data/api/atmob_api.dart

@@ -0,0 +1,13 @@
+import 'package:dio/dio.dart';
+import 'package:retrofit/error_logger.dart';
+import 'package:retrofit/http.dart';
+
+part 'atmob_api.g.dart';
+
+@RestApi()
+abstract class AtmobApi {
+  factory AtmobApi(Dio dio, {String baseUrl, ParseErrorLogger? errorLogger}) =
+      _AtmobApi;
+
+
+}

+ 53 - 0
lib/data/api/atmob_api.g.dart

@@ -0,0 +1,53 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'atmob_api.dart';
+
+// **************************************************************************
+// RetrofitGenerator
+// **************************************************************************
+
+// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations
+
+class _AtmobApi implements AtmobApi {
+  _AtmobApi(
+    this._dio, {
+    this.baseUrl,
+    this.errorLogger,
+  });
+
+  final Dio _dio;
+
+  String? baseUrl;
+
+  final ParseErrorLogger? errorLogger;
+
+  RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
+    if (T != dynamic &&
+        !(requestOptions.responseType == ResponseType.bytes ||
+            requestOptions.responseType == ResponseType.stream)) {
+      if (T == String) {
+        requestOptions.responseType = ResponseType.plain;
+      } else {
+        requestOptions.responseType = ResponseType.json;
+      }
+    }
+    return requestOptions;
+  }
+
+  String _combineBaseUrls(
+    String dioBaseUrl,
+    String? baseUrl,
+  ) {
+    if (baseUrl == null || baseUrl.trim().isEmpty) {
+      return dioBaseUrl;
+    }
+
+    final url = Uri.parse(baseUrl);
+
+    if (url.isAbsolute) {
+      return url.toString();
+    }
+
+    return Uri.parse(dioBaseUrl).resolveUri(url).toString();
+  }
+}

+ 10 - 0
lib/data/const/build_config.dart

@@ -0,0 +1,10 @@
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+
+final class BuildConfig {
+  BuildConfig._();
+
+  static bool get isDebug => kDebugMode;
+}
+

+ 48 - 0
lib/data/const/constants.dart

@@ -0,0 +1,48 @@
+class Constants {
+  Constants._();
+
+  static const String env = envProd;
+
+  static const String envDev = 'dev';
+
+  static const String envTest = 'test';
+
+  static const String envProd = 'prod';
+
+  static const String _devBaseUrl = "http://192.168.10.68:56389";
+
+  static const String _testBaseUrl = "http://loc-api.v8dashen.com";
+
+  static const String _prodBaseUrl = "http://loc-api.v8dashen.com";
+
+  static const String locationClientUrl =
+      "ws://loc-api.v8dashen.com/websocket/";
+
+  static String baseUrl = getBaseUrl();
+
+  static bool isProdEnv() {
+    return Constants.env == Constants.envProd;
+  }
+
+  static const String appDefaultChannel = "Android";
+  static const int appDefaultAppId = 0;
+  static const int appDefaultTgPlatformId = 0;
+  static const String appChanelName = "app_channel_name";
+  static const String appChannelId = "app_channel_id";
+  static const String appTgPlatformId = "app_tg_platform_id";
+
+}
+
+String getBaseUrl() {
+  switch (Constants.env) {
+    case Constants.envDev:
+      return Constants._devBaseUrl;
+    case Constants.envTest:
+      return Constants._testBaseUrl;
+    case Constants.envProd:
+      return Constants._prodBaseUrl;
+    default:
+      return Constants._devBaseUrl;
+  }
+}
+

+ 36 - 0
lib/data/const/error_code.dart

@@ -0,0 +1,36 @@
+
+import '../../resource/string.gen.dart';
+
+class ErrorCode {
+  /// 登录相关错误码
+  static const int verificationCodeError = 1005; //验证码错误
+  static const int noLoginError = 1006; //未登录
+  static const int noMember = 1007; //没有会员
+
+  /// 好友关系相关错误码
+  static const int friendNotRegistered = 1100; //好友未注册本应用
+  static const int friendRequestSent = 1101; //好友申请已发出,请等待对方通过
+  static const int alreadyInFriendList = 1102; //该好友已在好友列表
+  static const int cannotAddSelf = 1103; //不能添加自己为好友
+
+  /// 紧急联系人相关错误码
+  static const int maxContactsReached = 1200; // 最多添加5人,请移除后再添加
+  static const int contactAlreadyAdded = 1201; //对方已是您的紧急联系人
+  static const int smsSendFailed = 1202; //短信发送失败,请核实手机号码
+
+  /// 会员服务相关错误码
+  static const int getMemberFree = 1300; //每位用户只能领取一次试用
+  static const int isMember = 1301; //您已经是会员了
+  static const int payOrderError = 1004;
+}
+
+/// 错误码扩展方法
+extension ErrorDescription on int {
+  String? get description {
+    switch (this) {
+      case ErrorCode.noLoginError:
+        return StringName.accountNoLogin;
+    }
+    return null;
+  }
+}

+ 29 - 0
lib/data/repositories/account_repository.dart

@@ -0,0 +1,29 @@
+
+import 'package:injectable/injectable.dart';
+
+import '../../di/get_it.dart';
+import '../../utils/mmkv_util.dart';
+
+@lazySingleton
+class AccountRepository {
+  final String tag = "AccountRepository";
+  static final String keyAccountLoginToken = 'key_account_login_token';
+
+
+  static String? token = KVUtil.getString(keyAccountLoginToken, null);
+
+
+  static AccountRepository getInstance() {
+    return getIt.get<AccountRepository>();
+  }
+
+  void onLoginSuccess(String phoneNum, String authToken) {
+    AccountRepository.token = authToken;
+
+  }
+
+  void logout() {
+    AccountRepository.token = null;
+
+  }
+}

+ 14 - 0
lib/device/device_info_util.dart

@@ -0,0 +1,14 @@
+import 'package:abroad_location/device/platform_ios_info.dart';
+
+import 'platform_android_info.dart';
+
+class DeviceInfoUtil {
+  DeviceInfoUtil._();
+
+  init() async {
+    await PlatformAndroidInfo.init();
+    await PlatformIosInfo.init();
+  }
+}
+
+final deviceInfoUtil = DeviceInfoUtil._();

+ 20 - 0
lib/device/platform_android_info.dart

@@ -0,0 +1,20 @@
+import 'dart:io';
+
+import 'package:device_info_plus/device_info_plus.dart';
+import 'package:android_id/android_id.dart';
+import '../base/atmob_platform_info.dart';
+
+class PlatformAndroidInfo {
+  static Future<void> init() async {
+    if (Platform.isAndroid) {
+      DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
+      AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
+      String? deviceId = await const AndroidId().getId();
+      atmobPlatformInfo
+          .setAndroidId(deviceId)
+          .setBrand(androidInfo.brand)
+          .setModel(androidInfo.model);
+
+    }
+  }
+}

+ 32 - 0
lib/device/platform_ios_info.dart

@@ -0,0 +1,32 @@
+import 'dart:io';
+
+import 'package:app_tracking_transparency/app_tracking_transparency.dart';
+import 'package:device_info_plus/device_info_plus.dart';
+
+import '../base/atmob_platform_info.dart';
+
+class PlatformIosInfo {
+  static Future<void> init() async {
+    if (Platform.isIOS) {
+      DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
+      IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
+      atmobPlatformInfo
+          .setModel(iosInfo.model)
+          .setIdfv(iosInfo.identifierForVendor);
+
+      final TrackingStatus status =
+          await AppTrackingTransparency.trackingAuthorizationStatus;
+      if (status == TrackingStatus.notDetermined) {
+        final TrackingStatus newStatus =
+            await AppTrackingTransparency.requestTrackingAuthorization();
+        if (newStatus == TrackingStatus.authorized) {
+          atmobPlatformInfo.setIdfa(
+              await AppTrackingTransparency.getAdvertisingIdentifier());
+        }
+      } else if (status == TrackingStatus.authorized) {
+        atmobPlatformInfo
+            .setIdfa(await AppTrackingTransparency.getAdvertisingIdentifier());
+      }
+    }
+  }
+}

+ 43 - 0
lib/di/get_it.config.dart

@@ -0,0 +1,43 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// **************************************************************************
+// InjectableConfigGenerator
+// **************************************************************************
+
+// ignore_for_file: type=lint
+// coverage:ignore-file
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:dio/dio.dart' as _i361;
+import 'package:get_it/get_it.dart' as _i174;
+import 'package:injectable/injectable.dart' as _i526;
+
+import '../data/api/atmob_api.dart' as _i243;
+import '../data/repositories/account_repository.dart' as _i20;
+import '../module/main/main_controller.dart' as _i731;
+import '../module/splash/splash_controller.dart' as _i973;
+import 'network_module.dart' as _i567;
+
+extension GetItInjectableX on _i174.GetIt {
+// initializes the registration of main-scope dependencies inside of GetIt
+  _i174.GetIt init({
+    String? environment,
+    _i526.EnvironmentFilter? environmentFilter,
+  }) {
+    final gh = _i526.GetItHelper(
+      this,
+      environment,
+      environmentFilter,
+    );
+    final networkModule = _$NetworkModule();
+    gh.factory<_i973.SplashController>(() => _i973.SplashController());
+    gh.factory<_i731.MainController>(() => _i731.MainController());
+    gh.singleton<_i361.Dio>(() => networkModule.createDefaultDio());
+    gh.lazySingleton<_i20.AccountRepository>(() => _i20.AccountRepository());
+    gh.singleton<_i243.AtmobApi>(
+        () => networkModule.provideAtmobApi(gh<_i361.Dio>()));
+    return this;
+  }
+}
+
+class _$NetworkModule extends _i567.NetworkModule {}

+ 12 - 0
lib/di/get_it.dart

@@ -0,0 +1,12 @@
+import 'package:abroad_location/di/get_it.config.dart';
+import 'package:injectable/injectable.dart';
+import 'package:get_it/get_it.dart';
+
+final getIt = GetIt.instance;
+
+@InjectableInit(
+  initializerName: 'init',
+  preferRelativeImports: true,
+  asExtension: true,
+)
+void configureDependencies() => getIt.init();

+ 29 - 0
lib/di/network_module.dart

@@ -0,0 +1,29 @@
+import 'package:dio/dio.dart';
+import 'package:injectable/injectable.dart';
+import 'package:pretty_dio_logger/pretty_dio_logger.dart';
+import '../data/api/atmob_api.dart';
+import '../data/const/build_config.dart';
+import '../data/const/constants.dart';
+
+@module
+abstract class NetworkModule {
+  @singleton
+  Dio createDefaultDio() {
+    Dio dio = Dio(BaseOptions());
+    dio.interceptors.add(PrettyDioLogger(
+      requestHeader: true,
+      requestBody: true,
+      responseBody: true,
+      responseHeader: false,
+      error: true,
+      compact: true,
+      enabled: BuildConfig.isDebug,
+    ));
+    return dio;
+  }
+
+  @singleton
+  AtmobApi provideAtmobApi(Dio dio) {
+    return AtmobApi(dio, baseUrl: Constants.baseUrl);
+  }
+}

+ 153 - 101
lib/main.dart

@@ -1,125 +1,177 @@
+import 'dart:io';
+
+import 'package:abroad_location/resource/colors.gen.dart';
+import 'package:abroad_location/resource/string.gen.dart';
+import 'package:abroad_location/resource/string_source.dart';
+import 'package:abroad_location/router/app_pages.dart';
+import 'package:abroad_location/utils/atmob_log.dart';
+import 'package:abroad_location/utils/mmkv_util.dart';
+import 'package:abroad_location/utils/toast_util.dart';
+import 'package:atmob_logging/atmob_logging.dart' as ALog;
 import 'package:flutter/material.dart';
+import 'package:flutter_bugly/flutter_bugly.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get_instance/src/bindings_interface.dart';
+import 'package:get/get_navigation/src/root/get_cupertino_app.dart';
+import 'package:get/get_navigation/src/root/get_material_app.dart';
+import 'package:get/get_navigation/src/root/internacionalization.dart';
+import 'package:get/get_navigation/src/routes/get_route.dart';
+import 'data/const/build_config.dart';
+import 'data/const/constants.dart';
+import 'di/get_it.dart';
+import 'package:pull_to_refresh/pull_to_refresh.dart';
 
 void main() {
-  runApp(const MyApp());
+  FlutterBugly.postCatchedException(() async {
+    WidgetsFlutterBinding.ensureInitialized();
+
+    //必要初始化
+    await initRequired();
+
+    //非隐私相关
+    initCommon();
+
+    runApp(const MyApp());
+
+    //檢查地址
+    checkEnv();
+  });
 }
 
+
+Future<void> initRequired() async {
+  //Log
+  AtmobLog.setLogLevel(
+      BuildConfig.isDebug ? ALog.LogLevel.verbose : ALog.LogLevel.none);
+
+  //存储
+  await KVUtil.init();
+
+  //getit
+  configureDependencies();
+}
+
+void initCommon() {
+  //全局配置smartDialog
+  smartConfig();
+}
+
+
+
+void smartConfig() {
+  SmartDialog.config.custom =
+      SmartConfigCustom(animationType: SmartAnimationType.fade);
+}
+
+void checkEnv() {
+  if (!Constants.isProdEnv() && !BuildConfig.isDebug) {
+    ToastUtil.show('不是正式环境!!!', addPostFrame: true);
+  }
+}
+
+
+
 class MyApp extends StatelessWidget {
   const MyApp({super.key});
 
-  // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
-    return MaterialApp(
-      title: 'Flutter Demo',
+    return ScreenUtilInit(
+      designSize: const Size(360, 640),
+      builder: (_, child) {
+        return buildApp();
+      },
+    );
+  }
+
+  Widget buildApp() {
+    return RefreshConfiguration(
+      headerBuilder: () => Platform.isAndroid
+          ? const MaterialClassicHeader(color: ColorName.colorPrimary)
+          : const ClassicHeader(),
+      footerBuilder: () => ClassicFooter(
+        canLoadingText: StringName.loadingMore,
+        idleText: StringName.loadPullUp,
+        loadingText: StringName.loadingTxt,
+        noDataText: StringName.loadNoData,
+        failedText: StringName.loadFailed,
+      ),
+      child: Platform.isAndroid ? buildMaterialApp() : buildIosApp(),
+    );
+  }
+
+  Widget buildIosApp() {
+    return GetCupertinoApp(
+      onGenerateTitle: AppCommonConfig.appName,
+      getPages: AppCommonConfig.getPages,
+      initialRoute: AppCommonConfig.initialRoute,
+      initialBinding: AppCommonConfig.initialBinding,
+      navigatorObservers: AppCommonConfig.navigatorObservers,
+      builder: AppCommonConfig.builder,
+      translations: AppCommonConfig.translations,
+      localizationsDelegates: AppCommonConfig.localizations.delegates,
+      supportedLocales: AppCommonConfig.localizations.supportedLocales,
+      locale: AppCommonConfig.localizations.locale,
+      fallbackLocale: AppCommonConfig.localizations.fallbackLocale,
+    );
+  }
+
+  Widget buildMaterialApp() {
+    return GetMaterialApp(
+      onGenerateTitle: AppCommonConfig.appName,
+      getPages: AppCommonConfig.getPages,
+      initialRoute: AppCommonConfig.initialRoute,
+      initialBinding: AppCommonConfig.initialBinding,
       theme: ThemeData(
-        // This is the theme of your application.
-        //
-        // TRY THIS: Try running your application with "flutter run". You'll see
-        // the application has a purple toolbar. Then, without quitting the app,
-        // try changing the seedColor in the colorScheme below to Colors.green
-        // and then invoke "hot reload" (save your changes or press the "hot
-        // reload" button in a Flutter-supported IDE, or press "r" if you used
-        // the command line to start the app).
-        //
-        // Notice that the counter didn't reset back to zero; the application
-        // state is not lost during the reload. To reset the state, use hot
-        // restart instead.
-        //
-        // This works for code too, not just values: Most code changes can be
-        // tested with just a hot reload.
-        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
         useMaterial3: true,
+        textSelectionTheme: const TextSelectionThemeData(
+          cursorColor: ColorName.colorPrimary, // 设置默认光标颜色
+          selectionHandleColor: ColorName.colorPrimary, // 设置光标下面水滴的颜色
+        ),
       ),
-      home: const MyHomePage(title: 'Flutter Demo Home Page'),
+      navigatorObservers: AppCommonConfig.navigatorObservers,
+      builder: AppCommonConfig.builder,
+      translations: AppCommonConfig.translations,
+      localizationsDelegates: AppCommonConfig.localizations.delegates,
+      supportedLocales: AppCommonConfig.localizations.supportedLocales,
+      locale: AppCommonConfig.localizations.locale,
+      fallbackLocale: AppCommonConfig.localizations.fallbackLocale,
     );
   }
 }
 
-class MyHomePage extends StatefulWidget {
-  const MyHomePage({super.key, required this.title});
+class AppCommonConfig {
+  static GenerateAppTitle? appName = (_) => StringName.appName;
 
-  // This widget is the home page of your application. It is stateful, meaning
-  // that it has a State object (defined below) that contains fields that affect
-  // how it looks.
+  // 路由配置
+  static List<GetPage>? getPages = AppPage.pages;
+  static const initialRoute = RoutePath.splash;
 
-  // This class is the configuration for the state. It holds the values (in this
-  // case the title) provided by the parent (in this case the App widget) and
-  // used by the build method of the State. Fields in a Widget subclass are
-  // always marked "final".
+  // 初始化绑定
+  static Bindings initialBinding = AppBinding();
 
-  final String title;
+  // 导航观察者
+  static List<NavigatorObserver> navigatorObservers = [
+    FlutterSmartDialog.observer
+  ];
 
-  @override
-  State<MyHomePage> createState() => _MyHomePageState();
-}
+  // 弹窗初始化
+  static final builder = FlutterSmartDialog.init();
 
-class _MyHomePageState extends State<MyHomePage> {
-  int _counter = 0;
-
-  void _incrementCounter() {
-    setState(() {
-      // This call to setState tells the Flutter framework that something has
-      // changed in this State, which causes it to rerun the build method below
-      // so that the display can reflect the updated values. If we changed
-      // _counter without calling setState(), then the build method would not be
-      // called again, and so nothing would appear to happen.
-      _counter++;
-    });
-  }
+  // 本地化配置
+  static const localizations = (
+  delegates: [
+    GlobalMaterialLocalizations.delegate,
+    GlobalWidgetsLocalizations.delegate,
+    GlobalCupertinoLocalizations.delegate,
+  ],
+  supportedLocales: [Locale('en', 'US')],
+  locale: Locale('en', 'US'),
+  fallbackLocale: Locale('en', 'US'),
+  );
 
-  @override
-  Widget build(BuildContext context) {
-    // This method is rerun every time setState is called, for instance as done
-    // by the _incrementCounter method above.
-    //
-    // The Flutter framework has been optimized to make rerunning build methods
-    // fast, so that you can just rebuild anything that needs updating rather
-    // than having to individually change instances of widgets.
-    return Scaffold(
-      appBar: AppBar(
-        // TRY THIS: Try changing the color here to a specific color (to
-        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
-        // change color while the other colors stay the same.
-        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
-        // Here we take the value from the MyHomePage object that was created by
-        // the App.build method, and use it to set our appbar title.
-        title: Text(widget.title),
-      ),
-      body: Center(
-        // Center is a layout widget. It takes a single child and positions it
-        // in the middle of the parent.
-        child: Column(
-          // Column is also a layout widget. It takes a list of children and
-          // arranges them vertically. By default, it sizes itself to fit its
-          // children horizontally, and tries to be as tall as its parent.
-          //
-          // Column has various properties to control how it sizes itself and
-          // how it positions its children. Here we use mainAxisAlignment to
-          // center the children vertically; the main axis here is the vertical
-          // axis because Columns are vertical (the cross axis would be
-          // horizontal).
-          //
-          // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
-          // action in the IDE, or press "p" in the console), to see the
-          // wireframe for each widget.
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: <Widget>[
-            const Text(
-              'You have pushed the button this many times:',
-            ),
-            Text(
-              '$_counter',
-              style: Theme.of(context).textTheme.headlineMedium,
-            ),
-          ],
-        ),
-      ),
-      floatingActionButton: FloatingActionButton(
-        onPressed: _incrementCounter,
-        tooltip: 'Increment',
-        child: const Icon(Icons.add),
-      ), // This trailing comma makes auto-formatting nicer for build methods.
-    );
-  }
+  // 多语言配置
+  static Translations translations = StringResource();
 }

+ 13 - 0
lib/module/main/main_controller.dart

@@ -0,0 +1,13 @@
+
+
+
+import 'package:abroad_location/base/base_controller.dart';
+import 'package:injectable/injectable.dart';
+
+
+@injectable
+class MainController extends BaseController{
+
+
+
+}

+ 21 - 0
lib/module/main/main_page.dart

@@ -0,0 +1,21 @@
+import 'package:abroad_location/base/base_page.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
+
+import '../../router/app_pages.dart';
+import 'main_controller.dart';
+
+class MainPage extends BasePage<MainController> {
+  const MainPage({super.key});
+
+  static start() {
+    Get.offAllNamed(RoutePath.mainTab);
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Text('MainPage');
+  }
+}

+ 32 - 0
lib/module/splash/splash_controller.dart

@@ -0,0 +1,32 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:flutter/services.dart';
+import 'package:get/get_utils/src/platform/platform.dart';
+import 'package:injectable/injectable.dart';
+import '../../base/base_controller.dart';
+import '../main/main_page.dart';
+
+@injectable
+class SplashController extends BaseController {
+  final splashDelayedTime = 500;
+
+  @override
+  void onReady() {
+    _goMain(Duration(milliseconds: splashDelayedTime));
+  }
+
+  void _goMain(Duration delayTime) {
+    Future.delayed(delayTime, () {
+      MainPage.start();
+    });
+  }
+
+  void exitApp() {
+    if (GetPlatform.isAndroid) {
+      SystemNavigator.pop();
+    } else {
+      exit(0);
+    }
+  }
+}

+ 33 - 0
lib/module/splash/splash_page.dart

@@ -0,0 +1,33 @@
+import 'package:abroad_location/module/splash/splash_controller.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
+
+import '../../base/base_page.dart';
+
+class SplashPage extends BasePage<SplashController> {
+  SplashPage({super.key});
+
+  @override
+  final SplashController controller = Get.put(SplashController());
+
+  @override
+  bool immersive() {
+    return true;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Stack(
+      alignment: Alignment.center,
+      children: [
+        Text(
+          'SplashPage',
+          style: TextStyle(fontSize: 20.sp, fontWeight: FontWeight.bold),
+        )
+      ],
+    );
+  }
+}

+ 12 - 0
lib/resource/assets.gen.dart

@@ -0,0 +1,12 @@
+/// GENERATED CODE - DO NOT MODIFY BY HAND
+/// *****************************************************
+///  FlutterGen
+/// *****************************************************
+
+// coverage:ignore-file
+// ignore_for_file: type=lint
+// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
+
+class Assets {
+  const Assets._();
+}

+ 159 - 0
lib/resource/colors.gen.dart

@@ -0,0 +1,159 @@
+/// GENERATED CODE - DO NOT MODIFY BY HAND
+/// *****************************************************
+///  FlutterGen
+/// *****************************************************
+
+// coverage:ignore-file
+// ignore_for_file: type=lint
+// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
+
+import 'package:flutter/painting.dart';
+import 'package:flutter/material.dart';
+
+class ColorName {
+  ColorName._();
+
+  /// Color: #F5F6F8
+  static const Color bgColorPrimary = Color(0xFFF5F6F8);
+
+  /// Color: #FF000000
+  static const Color black = Color(0xFF000000);
+
+  /// Color: #1A000000
+  static const Color black10 = Color(0x1A000000);
+
+  /// Color: #26000000
+  static const Color black15 = Color(0x26000000);
+
+  /// Color: #33000000
+  static const Color black20 = Color(0x33000000);
+
+  /// Color: #40000000
+  static const Color black25 = Color(0x40000000);
+
+  /// Color: #4D000000
+  static const Color black30 = Color(0x4D000000);
+
+  /// Color: #59000000
+  static const Color black35 = Color(0x59000000);
+
+  /// Color: #66000000
+  static const Color black40 = Color(0x66000000);
+
+  /// Color: #73000000
+  static const Color black45 = Color(0x73000000);
+
+  /// Color: #0D000000
+  static const Color black5 = Color(0x0D000000);
+
+  /// Color: #80000000
+  static const Color black50 = Color(0x80000000);
+
+  /// Color: #8C000000
+  static const Color black55 = Color(0x8C000000);
+
+  /// Color: #99000000
+  static const Color black60 = Color(0x99000000);
+
+  /// Color: #A6000000
+  static const Color black65 = Color(0xA6000000);
+
+  /// Color: #B3000000
+  static const Color black70 = Color(0xB3000000);
+
+  /// Color: #B000000F
+  static const Color black75 = Color(0xB000000F);
+
+  /// Color: #CC000000
+  static const Color black80 = Color(0xCC000000);
+
+  /// Color: #D9000000
+  static const Color black85 = Color(0xD9000000);
+
+  /// Color: #E6000000
+  static const Color black90 = Color(0xE6000000);
+
+  /// Color: #F2000000
+  static const Color black95 = Color(0xF2000000);
+
+  /// Color: #6399FF
+  static const Color colorAccentPrimary = Color(0xFF6399FF);
+
+  /// Color: #7B7DFF
+  static const Color colorPrimary = Color(0xFF7B7DFF);
+
+  /// Color: #F6FAFF
+  static const Color colorPrimaryLight = Color(0xFFF6FAFF);
+
+  /// Color: #E6000000
+  static const Color primaryTextColor = Color(0xE6000000);
+
+  /// Color: #99000000
+  static const Color secondaryTextColor = Color(0x99000000);
+
+  /// Color: #00FFFFFF
+  static const Color transparent = Color(0x00FFFFFF);
+
+  /// Color: #FFFFFFFF
+  static const Color white = Color(0xFFFFFFFF);
+
+  /// Color: #1AFFFFFF
+  static const Color white10 = Color(0x1AFFFFFF);
+
+  /// Color: #26FFFFFF
+  static const Color white15 = Color(0x26FFFFFF);
+
+  /// Color: #33FFFFFF
+  static const Color white20 = Color(0x33FFFFFF);
+
+  /// Color: #40FFFFFF
+  static const Color white25 = Color(0x40FFFFFF);
+
+  /// Color: #4DFFFFFF
+  static const Color white30 = Color(0x4DFFFFFF);
+
+  /// Color: #59FFFFFF
+  static const Color white35 = Color(0x59FFFFFF);
+
+  /// Color: #66FFFFFF
+  static const Color white40 = Color(0x66FFFFFF);
+
+  /// Color: #73FFFFFF
+  static const Color white45 = Color(0x73FFFFFF);
+
+  /// Color: #0DFFFFFF
+  static const Color white5 = Color(0x0DFFFFFF);
+
+  /// Color: #80FFFFFF
+  static const Color white50 = Color(0x80FFFFFF);
+
+  /// Color: #8CFFFFFF
+  static const Color white55 = Color(0x8CFFFFFF);
+
+  /// Color: #99FFFFFF
+  static const Color white60 = Color(0x99FFFFFF);
+
+  /// Color: #A6FFFFFF
+  static const Color white65 = Color(0xA6FFFFFF);
+
+  /// Color: #B3FFFFFF
+  static const Color white70 = Color(0xB3FFFFFF);
+
+  /// Color: #BFFFFFFF
+  static const Color white75 = Color(0xBFFFFFFF);
+
+  /// Color: #CCFFFFFF
+  static const Color white80 = Color(0xCCFFFFFF);
+
+  /// Color: #D9FFFFFF
+  static const Color white85 = Color(0xD9FFFFFF);
+
+  /// Color: #DEFFFFFF
+  static const Color white87 = Color(0xDEFFFFFF);
+
+  /// Color: #E6FFFFFF
+  static const Color white90 = Color(0xE6FFFFFF);
+
+  /// Color: #F2FFFFFF
+  static const Color white95 = Color(0xF2FFFFFF);
+}

+ 28 - 0
lib/resource/string.gen.dart

@@ -0,0 +1,28 @@
+import 'package:get/get.dart';
+
+class StringName {
+  StringName._();
+  static final String appName = 'app_name'.tr; // Location
+  static final String accountNoLogin = 'account_no_login'.tr; // Not logged in.
+  static final String loadingMore = 'loading_more'.tr; // Load more
+  static final String loadPullUp = 'load_pull_up'.tr; // Pull up to load
+  static final String loadingTxt = 'loading_txt'.tr; // Loading…
+  static final String loadNoData = 'load_no_data'.tr; // No more data
+  static final String loadFailed = 'load_failed'.tr; // Failed to load
+  static final String networkError = 'network_error'.tr; // Network error
+}
+class StringMultiSource {
+  StringMultiSource._();
+  static const Map<String, Map<String, String>> values = {
+    'en_US': {
+      'app_name': 'Location',
+      'account_no_login': 'Not logged in.',
+      'loading_more': 'Load more',
+      'load_pull_up': 'Pull up to load',
+      'loading_txt': 'Loading…',
+      'load_no_data': 'No more data',
+      'load_failed': 'Failed to load',
+      'network_error': 'Network error',
+    },
+  };
+}

+ 7 - 0
lib/resource/string_source.dart

@@ -0,0 +1,7 @@
+import 'package:abroad_location/resource/string.gen.dart';
+import 'package:get/get.dart';
+
+class StringResource extends Translations {
+  @override
+  Map<String, Map<String, String>> get keys => StringMultiSource.values;
+}

+ 32 - 0
lib/router/app_pages.dart

@@ -0,0 +1,32 @@
+import 'package:get/get.dart';
+
+import '../di/get_it.dart';
+import '../module/main/main_controller.dart';
+import '../module/main/main_page.dart';
+import '../module/splash/splash_page.dart';
+
+abstract class AppPage {
+  static final pages = <GetPage>[...generalPages];
+}
+
+abstract class RoutePath {
+  static const splash = '/';
+
+  static const mainTab = '/mainTab';
+}
+
+class AppBinding extends Bindings {
+  @override
+  void dependencies() {
+    lazyPut(() => getIt.get<MainController>());
+  }
+
+  void lazyPut<S>(InstanceBuilderCallback<S> builder) {
+    Get.lazyPut(builder, fenix: true);
+  }
+}
+
+final generalPages = [
+  GetPage(name: RoutePath.splash, page: () => SplashPage()),
+  GetPage(name: RoutePath.mainTab, page: () => MainPage()),
+];

+ 23 - 0
lib/utils/app_info_util.dart

@@ -0,0 +1,23 @@
+import 'dart:io';
+
+import 'package:package_info_plus/package_info_plus.dart';
+
+class AppInfoUtil {
+  PackageInfo? _packageInfo;
+
+  AppInfoUtil._();
+
+  init() async {
+    _packageInfo = await PackageInfo.fromPlatform();
+  }
+
+  String? get appName => _packageInfo?.appName;
+
+  String? get packageName => _packageInfo?.packageName;
+
+  String? get appVersionName => _packageInfo?.version;
+
+  int? get appVersionCode => int.tryParse(_packageInfo?.buildNumber ?? '');
+}
+
+final appInfoUtil = AppInfoUtil._();

+ 208 - 0
lib/utils/async_util.dart

@@ -0,0 +1,208 @@
+import 'dart:async';
+
+typedef FutureCallback<T> = Future<T> Function();
+
+typedef IntervalCallback<T> = Future<T> Function(int times);
+
+typedef CancelCallback<T> = void Function();
+
+typedef FutureCompleter<T> = void Function(Completer<T> completer);
+
+typedef Predicate<T> = bool Function(T? value);
+
+class AsyncUtil {
+  AsyncUtil._();
+
+  static CancelableFuture<T> retryWithExponentialBackoff<T>(
+      FutureCallback<T> callback, int maxRetry,
+      {Duration initialInterval = const Duration(seconds: 1),
+      Predicate<dynamic>? predicate}) {
+    int retryCount = 0;
+    Timer? timer;
+
+    void attempt(Completer<T> completer) {
+      callback().then((value) {
+        if (!completer.isCompleted) {
+          completer.complete(value);
+        }
+      }).catchError((error) {
+        if (retryCount < maxRetry && (predicate == null || predicate(error))) {
+          retryCount++;
+          Duration nextInterval = initialInterval * (1 << (retryCount - 1));
+          timer = Timer(nextInterval, () => attempt(completer));
+        } else {
+          if (!completer.isCompleted) {
+            completer.completeError(error);
+          }
+        }
+      });
+    }
+
+    return CancelableFuture<T>((completer) {
+      attempt(completer);
+    }, () {
+      timer?.cancel();
+    });
+  }
+
+  static CancelableFuture<T> retry<T>(
+      FutureCallback<T> callback, Duration interval,
+      {Duration? timeout, int? maxRetry, Predicate<dynamic>? predicate}) {
+    int retryCount = 0;
+    Timer? timer;
+    Timer? timeoutTimer;
+
+    void attempt(Completer<T> completer) {
+      callback().then((value) {
+        if (!completer.isCompleted) {
+          completer.complete(value);
+        }
+      }).catchError((error) {
+        if ((maxRetry == null || maxRetry <= 0 || retryCount < maxRetry) &&
+            (predicate == null || predicate(error))) {
+          retryCount++;
+          timer = Timer(interval, () => attempt(completer));
+        } else {
+          if (!completer.isCompleted) {
+            completer.completeError(error);
+          }
+        }
+      });
+    }
+
+    return CancelableFuture<T>((completer) {
+      if (timeout != null) {
+        timeoutTimer = Timer(timeout, () {
+          if (!completer.isCompleted) {
+            completer.completeError(TimeoutException('Operation timed out'));
+          }
+        });
+      }
+      attempt(completer);
+    }, () {
+      timer?.cancel();
+      timeoutTimer?.cancel();
+    });
+  }
+
+  static CancelableFuture<T> delay<T>(
+      FutureCallback<T> callback, Duration interval) {
+    Timer? timer;
+
+    return CancelableFuture<T>((completer) {
+      timer = Timer(interval, () {
+        callback().then((value) {
+          if (!completer.isCompleted) {
+            completer.complete(value);
+          }
+        }).catchError((error) {
+          if (!completer.isCompleted) {
+            completer.completeError(error);
+          }
+        });
+      });
+    }, () {
+      timer?.cancel();
+    });
+  }
+
+  static StreamController<T> interval<T>(
+      IntervalCallback<T> callback, Duration interval, int times,
+      {Duration? delay}) {
+    Timer? timer;
+    StreamController<T> controller = StreamController<T>(onCancel: () {
+      timer?.cancel();
+    });
+    int count = 0;
+    void tick() {
+      callback(count).then((value) {
+        controller.add(value);
+        count++;
+        if (times == -1 || count < times) {
+          timer = Timer(interval, tick);
+        } else {
+          controller.close();
+        }
+      }).catchError((error) {
+        if (!controller.isClosed) {
+          controller.addError(error);
+          controller.close();
+        }
+      });
+    }
+
+    if (delay != null && delay > Duration.zero) {
+      timer = Timer(delay, tick);
+    } else {
+      tick();
+    }
+    return controller;
+  }
+}
+
+abstract interface class Cancelable {
+  void cancel();
+}
+
+class CancelableFuture<T> implements Future<T>, Cancelable {
+  final Completer<T> _completer = Completer<T>();
+
+  final CancelCallback? _cancelable;
+
+  CancelableFuture(FutureCompleter<T> completer, this._cancelable) {
+    completer.call(_completer);
+  }
+
+  @override
+  void cancel() {
+    _cancelable?.call();
+    if (!_completer.isCompleted) {
+      _completer.completeError(CancelledError());
+    }
+  }
+
+  @override
+  Stream<T> asStream() => _completer.future.asStream();
+
+  @override
+  Future<T> catchError(Function onError, {bool Function(Object error)? test}) =>
+      _completer.future.catchError(onError, test: test);
+
+  @override
+  Future<R> then<R>(FutureOr<R> Function(T value) onValue,
+          {Function? onError}) =>
+      _completer.future.then(onValue, onError: onError);
+
+  @override
+  Future<T> timeout(Duration timeLimit, {FutureOr<T> Function()? onTimeout}) =>
+      _completer.future.timeout(timeLimit, onTimeout: onTimeout);
+
+  @override
+  Future<T> whenComplete(FutureOr<void> Function() action) =>
+      _completer.future.whenComplete(action);
+}
+
+class CancelledError extends Error {
+  @override
+  String toString() {
+    return 'Operation was cancelled';
+  }
+}
+
+extension CancelableFutureExtension<T> on Future<T> {
+  CancelableFuture<T> asCancelable(
+      FutureCompleter completer, CancelCallback? cancelable) {
+    CancelableFuture<T> cancelableFuture =
+        CancelableFuture(completer, cancelable);
+    then((value) {
+      if (!cancelableFuture._completer.isCompleted) {
+        cancelableFuture._completer.complete(value);
+      }
+    }).catchError((error) {
+      if (!cancelableFuture._completer.isCompleted) {
+        cancelableFuture._completer.completeError(error);
+      }
+    });
+    return cancelableFuture;
+  }
+}

+ 29 - 0
lib/utils/atmob_log.dart

@@ -0,0 +1,29 @@
+import 'package:atmob_logging/atmob_logging.dart';
+
+class AtmobLog {
+  AtmobLog._();
+
+  static void w(String tag, String message) {
+    ALog.w(tag, message);
+  }
+
+  static void e(String tag, String message) {
+    ALog.e(tag, message);
+  }
+
+  static void i(String tag, String message) {
+    ALog.i(tag, message);
+  }
+
+  static void d(String tag, String message) {
+    ALog.d(tag, message);
+  }
+
+  static void v(String tag, String message) {
+    ALog.v(tag, message);
+  }
+
+  static void setLogLevel(LogLevel level) {
+    ALog.setLogLevel(level);
+  }
+}

+ 20 - 0
lib/utils/de_bounce.dart

@@ -0,0 +1,20 @@
+class Debounce {
+  // 设定的时间间隔,单位为毫秒
+  final int debounceTime;
+
+  // 记录上次点击的时间
+  DateTime? _lastClickTime;
+
+  Debounce({this.debounceTime = 300});
+
+  // 点击事件处理方法
+  void onClick(Function action) {
+    DateTime now = DateTime.now();
+    if (_lastClickTime == null ||
+        now.difference(_lastClickTime!) >
+            Duration(milliseconds: debounceTime)) {
+      _lastClickTime = now;
+      action();
+    }
+  }
+}

+ 39 - 0
lib/utils/http_handler.dart

@@ -0,0 +1,39 @@
+import 'dart:async';
+
+import '../base/base_response.dart';
+import '../data/const/error_code.dart';
+import '../data/repositories/account_repository.dart';
+
+class HttpHandler {
+  HttpHandler._();
+
+  static FutureOr<T> Function(BaseResponse<T> value) handle<T>(
+      bool allowEmptyData) {
+    return (BaseResponse<T> response) {
+      if (response.code == 0) {
+        if (response.data != null || allowEmptyData) {
+          return response.data == null ? Future.value() : response.data!;
+        } else {
+          throw Exception('data is null');
+        }
+      } else {
+        if (response.code == ErrorCode.noLoginError) {
+          AccountRepository.getInstance().logout();
+        }
+        throw ServerErrorException(response.code, response.message);
+      }
+    };
+  }
+}
+
+class ServerErrorException implements Exception {
+  final int? code;
+  final String? message;
+
+  ServerErrorException(this.code, this.message);
+
+  @override
+  String toString() {
+    return 'ServerErrorException: code: $code, message: $message';
+  }
+}

+ 52 - 0
lib/utils/mmkv_util.dart

@@ -0,0 +1,52 @@
+import 'package:mmkv/mmkv.dart';
+
+class KVUtil {
+  KVUtil._();
+
+  static MMKV? _mmkv;
+
+  static init() async {
+    if (_mmkv != null) {
+      return;
+    }
+    await MMKV.initialize();
+    _mmkv = MMKV.defaultMMKV();
+  }
+
+  static void putString(String key, String? value) {
+    _getMMKV().encodeString(key, value);
+  }
+
+  static String? getString(String key, String? defaultValue) {
+    return _getMMKV().decodeString(key) ?? defaultValue;
+  }
+
+  static void putInt(String key, int value) {
+    _getMMKV().encodeInt(key, value);
+  }
+
+  static int getInt(String key, int defaultValue) {
+    return _getMMKV().decodeInt(key, defaultValue: defaultValue);
+  }
+
+  static void putBool(String key, bool value) {
+    _getMMKV().encodeBool(key, value);
+  }
+
+  static bool getBool(String key, bool defaultValue) {
+    return _getMMKV().decodeBool(key, defaultValue: defaultValue);
+  }
+
+  static void putDouble(String key, double value) {
+    _getMMKV().encodeDouble(key, value);
+  }
+
+  static double getDouble(String key, double defaultValue) {
+    return _getMMKV().decodeDouble(key, defaultValue: defaultValue);
+  }
+
+  static MMKV _getMMKV() {
+    _mmkv ??= MMKV.defaultMMKV();
+    return _mmkv!;
+  }
+}

+ 26 - 0
lib/utils/toast_util.dart

@@ -0,0 +1,26 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+
+class ToastUtil {
+  ToastUtil._();
+
+  static int lengthShort = 2000;
+  static int lengthLong = 3500;
+
+  static void show(String? msg,
+      {Duration? displayTime,
+      SmartToastType? displayType = SmartToastType.normal,
+      bool? addPostFrame}) {
+    if (msg != null) {
+      if (addPostFrame == true) {
+        WidgetsBinding.instance.addPostFrameCallback((_) {
+          SmartDialog.showToast(msg,
+              displayType: displayType, displayTime: displayTime);
+        });
+      } else {
+        SmartDialog.showToast(msg,
+            displayType: displayType, displayTime: displayTime);
+      }
+    }
+  }
+}

Разница между файлами не показана из-за своего большого размера
+ 949 - 5
pubspec.lock


+ 87 - 31
pubspec.yaml

@@ -31,6 +31,61 @@ dependencies:
   flutter:
     sdk: flutter
 
+  # 状态管理
+  get: 4.7.2
+
+  # 网络
+  dio: 5.7.0
+  pretty_dio_logger: 1.4.0
+  retrofit: 4.4.1
+  json_annotation: 4.9.0
+
+  #获取设备信息
+  device_info_plus: 10.1.2
+  android_id: 0.4.0
+
+  # 包信息
+  package_info_plus: 8.1.1
+
+  # 隐私追踪
+  app_tracking_transparency: ^2.0.5
+
+  # 屏幕适配
+  flutter_screenutil: 5.9.3
+
+  # 弹窗
+  flutter_smart_dialog: 4.9.8
+
+  #数据存储
+  mmkv: 2.0.2
+
+  #权限申请
+  permission_handler: 11.3.1
+
+  #本地化
+  flutter_localizations:
+    sdk: flutter
+
+  #依赖注入
+  injectable: 2.5.0
+
+  #bugly
+  flutter_bugly: 1.1.0
+
+  #webview
+  flutter_inappwebview: ^6.1.5
+
+  #上、下拉刷新
+  pull_to_refresh: 2.0.0
+
+  #日志打印
+  atmob_logging:
+    version: ^0.0.5
+    hosted:
+      name: atmob_logging
+      url: http://pub.v8dashen.com/
+
+
 
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
@@ -40,12 +95,41 @@ dev_dependencies:
   flutter_test:
     sdk: flutter
 
+  retrofit_generator: '>=8.0.0 <10.0.0'
+
+  build_runner: 2.4.13
+
+  json_serializable: 6.9.0
+
+  flutter_gen_runner: 5.9.0
+
+  flutter_lints: ^5.0.0
+
+  injectable_generator: 2.6.2
+
+  string_get_runner:
+    git:
+      url: http://git.atmob.com:28999/Atmob-Flutter/string_get_runner.git
+      ref: v0.0.4
+
+#----------gen配置---------------
+flutter_gen:
+  output: lib/resource/
+  fonts:
+  colors:
+    inputs:
+      - assets/color/common_color.xml
+      - assets/color/color.xml
+#----------------------------------------
+#字符串生成配置
+flutter_string_get:
+  language: 'en_US'
+
   # The "flutter_lints" package below contains a set of recommended lints to
   # encourage good coding practices. The lint set provided by the package is
   # activated in the `analysis_options.yaml` file located at the root of your
   # package. See that file for information about deactivating specific lint
   # rules and activating additional ones.
-  flutter_lints: ^4.0.0
 
 # For information on the generic Dart part of this file, see the
 # following page: https://dart.dev/tools/pub/pubspec
@@ -58,33 +142,5 @@ flutter:
   # the material Icons class.
   uses-material-design: true
 
-  # To add assets to your application, add an assets section, like this:
-  # assets:
-  #   - images/a_dot_burr.jpeg
-  #   - images/a_dot_ham.jpeg
-
-  # An image asset can refer to one or more resolution-specific "variants", see
-  # https://flutter.dev/to/resolution-aware-images
-
-  # For details regarding adding assets from package dependencies, see
-  # https://flutter.dev/to/asset-from-package
-
-  # To add custom fonts to your application, add a fonts section here,
-  # in this "flutter" section. Each entry in this list should have a
-  # "family" key with the font family name, and a "fonts" key with a
-  # list giving the asset and other descriptors for the font. For
-  # example:
-  # fonts:
-  #   - family: Schyler
-  #     fonts:
-  #       - asset: fonts/Schyler-Regular.ttf
-  #       - asset: fonts/Schyler-Italic.ttf
-  #         style: italic
-  #   - family: Trajan Pro
-  #     fonts:
-  #       - asset: fonts/TrajanPro.ttf
-  #       - asset: fonts/TrajanPro_Bold.ttf
-  #         weight: 700
-  #
-  # For details regarding fonts from package dependencies,
-  # see https://flutter.dev/to/font-from-package
+  assets:
+    - assets/images/