提交 eac88af6 authored 作者: 张国庆's avatar 张国庆

网络请求

上级 3bcfa079
## 0.0.1
## 1.0.0
* TODO: Describe initial release.
* 网络请求框架
\ No newline at end of file
......@@ -4,15 +4,97 @@ A new Flutter plugin project.
## Getting Started
This project is a starting point for a Flutter
[plug-in package](https://flutter.dev/developing-packages/),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
For help getting started with Flutter development, view the
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported.
To add platforms, run `flutter create -t plugin --platforms <platforms> .` in this directory.
You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms.
## Usage
### 1. 添加依赖
```
dependencies:
clx_flutter_util:
git:
ref: '1.0.0'
url: 'https://t.clxkj.cn/openSourceLibrary/clx_flutter_net.git'
```
### 2. 初始化
```
import 'package:clx_flutter_net/clx_flutter_net.dart';
void main() {
DioUtils.instance.initConfig(); // 初始化网络请求框架
}
```
### 3. 配置项目种dio(请求域名、请求头、重新登录等)
```
import 'package:clx_flutter_net/clx_flutter_net.dart';
class DioUtils extends BaseDio {
static final DioUtils _singleton = DioUtils._internal();
static DioUtils get instance => _singleton; // 单例
DioUtils._internal();
@override
Interceptor getHeadersInterceptor() {
// 设置请求头
return HeaderInterceptor();
}
@override
void onLoginExpire() {
// token 失效 重新登录等操作
ToastUtil.showToast("登录失效,请重新登录");
logoutRemove();
Get.offAllNamed(AppRoutes.loginPage);
}
@override
String setBaseUrl() {
return ApiConfig.apiHost; // 设置请求域名
}
@override
bool isProdEnv() => ApiConfig.apiHost == ApiConfig.prod; // 生产环境不设置代理 测试抓包代理配置
}
```
### 4. 发起请求
```
import 'package:clx_flutter_net/clx_flutter_net.dart';
static Future<void> get(
url, {
required queryParameters,
NetSuccessCallback? successCallback,
NetSuccessListCallback? successListCallback,
NetErrorCallback? errorCallback,
Options? options,
}) async {
DioUtils.instance.request(
requestUrl: url,
queryParameters: queryParameters,
method: Method.get,
successCallback: (data) {
successListCallback?.call(data) ?? successCallback?.call(data);
},
errorCallback: (code, msg) {
EasyLoading.dismiss();
errorCallback != null
? errorCallback.call(code, msg)
: ToastUtil.showToast(msg);
},
options: options);
}
```
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "d211f42860350d914a5ad8102f9ec32764dc6d06"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
- platform: android
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
android {
namespace "com.example.example"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 23
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
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
}
}
}
flutter {
source '../..'
}
dependencies {}
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
package com.example.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
}
}
include ":app"
apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
// This is a basic Flutter integration test.
//
// Since integration tests run in a full Flutter application, they can interact
// with the host side of a plugin implementation, unlike Dart unit tests.
//
// For more information about Flutter integration tests, please see
// https://docs.flutter.dev/cookbook/testing/integration/introduction
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:clx_flutter_net/clx_flutter_net.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
final ClxFlutterNet plugin = ClxFlutterNet();
final String? version = await plugin.getPlatformVersion();
// The version string depends on the host platform running the test, so
// just assert that some non-empty string is returned.
expect(version?.isNotEmpty, true);
});
}
class ApiConfig {
static const dev = "https://gateway.devclx.cn";
static const test = "https://gateway.testclx.cn";
static const pre = "https://gateway.preclx.cn";
static const prod = "https://gateway.91msl.com";
static const bannerProd = "https://clx-prod.oss-cn-beijing.aliyuncs.com/";
static Env? env;
static String get apiHost {
switch (env) {
case Env.dev: //开发环境
return dev;
case Env.test: //测试环境
return test;
case Env.pre: //预发布环境
return pre;
case Env.prod: //生产环境
return prod;
default: //默认是生产环境
return prod;
}
}
static String get apiH5Host {
switch (env) {
case Env.dev: //开发环境
return "https://dh5.clxkj.cn/";
case Env.test: //测试环境
return "https://h5.clxkj.cn/";
case Env.pre: //预发布环境
return "https://ph5.clxkj.cn/";
case Env.prod: //生产环境
return "https://h5.91msl.com/";
default: //默认是生产环境
return "https://h5.91msl.com/";
}
}
//支付 apihost
static String get payApiHost {
switch (env) {
case Env.dev: //开发环境
return PayApiHost.dev;
case Env.test: //测试环境
return PayApiHost.test;
case Env.pre: //预发布环境
return PayApiHost.pre;
case Env.prod: //生产环境
return PayApiHost.prod;
default: //默认是生产环境
return PayApiHost.prod;
}
}
}
abstract class PayApiHost {
static const dev = "https://dapi.clxkj.cn:8088";
static const test = "https://api.clxkj.cn:8088";
static const pre = "https://papi.clxkj.cn:8088";
static const prod = "https://api1.91msl.com:8088";
}
enum Env {
prod,
pre,
test,
dev,
}
import 'package:clx_flutter_net/clx_flutter_net.dart';
import 'api_config.dart';
class DioUtils extends BaseDio {
static final DioUtils _singleton = DioUtils._internal();
static DioUtils get instance => _singleton;
DioUtils._internal();
@override
Interceptor getHeadersInterceptor() {
return HeaderInterceptor();
}
@override
void onLoginExpire() {
// token 失效
}
@override
String setBaseUrl() {
return ApiConfig.apiHost;
}
@override
bool isProdEnv() => ApiConfig.apiHost == ApiConfig.prod; // 生产环境不设置代理
}
class HeaderInterceptor extends Interceptor {
@override
onRequest(RequestOptions options, handler) async {
options.headers['token'] = 'token';
options.headers['platform'] = "1";
options.headers["product-code"] = "clx_flutter_net";
return super.onRequest(options, handler);
}
}
import 'package:clx_flutter_net_example/api_config.dart';
import 'package:clx_flutter_net_example/dio_utils.dart';
import 'package:clx_flutter_net_example/net_util.dart';
import 'package:clx_flutter_net_example/service_api.dart';
import 'package:clx_flutter_util/clx_flutter_util.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:clx_flutter_net/clx_flutter_net.dart';
void main() {
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); // 初始化
await SpUtil.getInstance(); // 初始化本地存储
ApiConfig.env = Env.dev; // 配置环境
DioUtils.instance.initConfig(); // 初始化网络请求框架
runApp(const MyApp());
}
......@@ -16,35 +21,12 @@ class MyApp extends StatefulWidget {
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
final _clxFlutterNetPlugin = ClxFlutterNet();
String _result = '请求结果:';
@override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion =
await _clxFlutterNetPlugin.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
// initPlatformState();
}
@override
......@@ -54,8 +36,45 @@ class _MyAppState extends State<MyApp> {
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('Running on: $_platformVersion\n'),
body: Column(
children: [
Row(
children: [
ElevatedButton(
onPressed: () async {
var result = await NetUtil.getSync(
ServiceApi.getOwnerAgreementInfo,
);
setState(() {});
_result = "请求结果: $result";
},
child: const Text('get同步请求')),
],
),
Row(
children: [
ElevatedButton(
onPressed: () async {
NetUtil.get(
ServiceApi.getOwnerAgreementInfo,
queryParameters: {
"driverId": "1",
},
successCallback: (data) {
logger.d("请求结果: $data");
setState(() {});
_result = "请求结果: $data";
},
errorCallback: (code, msg) {
logger.d("请求结果: $msg");
},
);
},
child: const Text('get回调')),
],
),
Text(_result),
],
),
),
);
......
import 'package:clx_flutter_net/clx_flutter_net.dart';
import 'package:clx_flutter_util/clx_flutter_util.dart';
import 'dio_utils.dart';
import 'service_api.dart';
class NetUtil {
static Future<void> get(
url, {
required queryParameters,
NetSuccessCallback? successCallback,
NetSuccessListCallback? successListCallback,
NetErrorCallback? errorCallback,
Options? options,
}) async {
DioUtils.instance.request(
requestUrl: url,
queryParameters: queryParameters,
method: Method.get,
successCallback: (data) {
successListCallback?.call(data) ?? successCallback?.call(data);
},
errorCallback: (code, msg) {
errorCallback != null
? errorCallback.call(code, msg)
: ToastUtil.showToast(msg);
},
options: options);
}
static Future<void> post(
url, {
required data,
dynamic queryParameters,
NetSuccessCallback? successCallback,
NetSuccessListCallback? successListCallback,
NetErrorCallback? errorCallback,
Options? options,
}) async {
DioUtils.instance.request(
requestUrl: url,
method: Method.post,
data: data,
queryParameters: queryParameters,
successCallback: (data) {
successListCallback?.call(data) ?? successCallback?.call(data);
},
errorCallback: (code, msg) {
errorCallback != null
? errorCallback.call(code, msg)
: ToastUtil.showToast(msg);
},
options: options);
}
static Future<void> put(
url, {
Map<String, dynamic>? queryParameters,
Map<String, dynamic>? data,
required NetSuccessCallback successCallback,
NetErrorCallback? errorCallback,
Options? options,
}) async {
DioUtils.instance.request(
requestUrl: url,
method: Method.put,
queryParameters: queryParameters,
data: data,
successCallback: successCallback,
errorCallback: (code, msg) {
errorCallback != null
? errorCallback.call(code, msg)
: ToastUtil.showToast(msg);
},
options: options);
}
static Future<void> delete(
url, {
Map<String, dynamic>? queryParameters,
Map<String, dynamic>? data,
required NetSuccessCallback successCallback,
NetErrorCallback? errorCallback,
Options? options,
}) async {
DioUtils.instance.request(
requestUrl: url,
method: Method.delete,
queryParameters: queryParameters,
data: data,
successCallback: successCallback,
errorCallback: (code, msg) {
errorCallback != null
? errorCallback.call(code, msg)
: ToastUtil.showToast(msg);
},
options: options);
}
// 上传图片到服务 表单上传
static Future<String> uploadImage({
required String path,
}) async {
String imageUrl = '';
var formData = FormData.fromMap({
'file': await MultipartFile.fromFile(path, filename: 'image.png'),
'dir': 'carrier-driver',
});
var data = await DioUtils.instance.requestSync(
requestUrl: ServiceApi.uploadImage,
method: Method.post,
data: formData);
if (data != null) {
String url = data['data'];
imageUrl = url;
} else {
ToastUtil.showToast("图片上传失败,请联系公司管理员或客服");
}
return imageUrl;
}
//同步请求
static Future getSync(String url) async {
return DioUtils.instance
.requestSync(requestUrl: url, method: Method.get);
}
}
abstract class ServiceApi {
static const String getOwnerAgreementInfo =
'/clx-user/app/driverInfo/getDriverAgreementInfo';
static const String uploadImage = '/msl-document/fileUpload/uploadForm';
}
......@@ -40,6 +40,15 @@ packages:
relative: true
source: path
version: "0.0.1"
clx_flutter_util:
dependency: "direct main"
description:
path: "."
ref: "1.0.0"
resolved-ref: "84afbba15d0480d5ea05bd27e4eccb5fc5098172"
url: "https://t.clxkj.cn/openSourceLibrary/clx_flutter_util.git"
source: git
version: "0.0.1"
collection:
dependency: transitive
description:
......@@ -56,6 +65,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.6"
dio:
dependency: transitive
description:
name: dio
sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.4.0"
fake_async:
dependency: transitive
description:
......@@ -64,6 +81,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
file:
dependency: transitive
description:
......@@ -95,11 +120,32 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
fluttertoast:
dependency: transitive
description:
name: fluttertoast
sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1
url: "https://pub.flutter-io.cn"
source: hosted
version: "8.2.4"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.2"
integration_test:
dependency: "direct dev"
description: flutter
......@@ -113,6 +159,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
logger:
dependency: transitive
description:
name: logger
sha256: ba3bc83117b2b49bdd723c0ea7848e8285a0fbc597ba09203b20d329d020c24a
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
matcher:
dependency: transitive
description:
......@@ -145,6 +199,30 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.3"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
platform:
dependency: transitive
description:
......@@ -161,6 +239,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.7"
pretty_dio_logger:
dependency: transitive
description:
name: pretty_dio_logger
sha256: "00b80053063935cf9a6190da344c5373b9d0e92da4c944c878ff2fbef0ef6dc2"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
process:
dependency: transitive
description:
......@@ -169,6 +255,62 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.4"
shared_preferences:
dependency: transitive
description:
name: shared_preferences
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.4"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.2"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.2"
sky_engine:
dependency: transitive
description: flutter
......@@ -182,6 +324,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
sp_util:
dependency: transitive
description:
name: sp_util
sha256: "9da43dce5de79c17a787d0626bf01538d63090ca32521200d22a232171c495dc"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.3"
stack_trace:
dependency: transitive
description:
......@@ -214,6 +364,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
term_glyph:
dependency: transitive
description:
......@@ -230,6 +388,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.0"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.2"
vector_math:
dependency: transitive
description:
......@@ -262,6 +428,22 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
win32:
dependency: transitive
description:
name: win32
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.1.1"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.3"
sdks:
dart: ">=3.1.5 <4.0.0"
flutter: ">=3.3.0"
flutter: ">=3.7.0"
......@@ -28,6 +28,10 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
clx_flutter_util:
git:
ref: '1.0.0'
url: 'https://t.clxkj.cn/openSourceLibrary/clx_flutter_util.git'
dev_dependencies:
integration_test:
......
import 'package:clx_flutter_util/clx_flutter_util.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'proxy.dart';
import 'transformer.dart';
abstract class BaseDio {
final dio = Dio(
BaseOptions(
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
contentType: Headers.jsonContentType,
responseType: ResponseType.plain
// Transform the response data to a String encoded with UTF8.
// The default value is [ResponseType.JSON].
),
);
//初始化配置dio实例
void initConfig() {
dio.options.baseUrl = setBaseUrl();
dio.options.headers = setHeaders();
// 配置JSON转换器
dio.interceptors.add(getHeadersInterceptor());
dio.interceptors.add(getLogInterceptor());
//配置代理
setProxyUri(dio, isProdEnv());
}
final jsonTransformer = MyTransformer();
String setBaseUrl();
// 设置请求头拦截器
Interceptor getHeadersInterceptor();
// 是否是生产环境,默认为true,
// 生产环境不设置代理,默认不设置
bool isProdEnv() => true;
// 设置log拦截器
Interceptor getLogInterceptor() => _setLogInterceptor();
Map<String, dynamic>? setHeaders() => _setHeaders();
//设置代理
HttpClientAdapter _setProxy() {
return IOHttpClientAdapter();
}
HttpClientAdapter setProxy() => _setProxy();
void onLoginExpire() => _onLoginExpire();
void onNetSuccess(Response response, NetSuccessCallback successCallback,
NetErrorCallback? errorCallback) =>
_onNetSuccess(response, successCallback, errorCallback);
// 登录过期的处理
void _onLoginExpire() {
ToastUtil.showToast("登录过期,请重新登录");
}
Interceptor _setLogInterceptor() {
return PrettyDioLogger(
requestHeader: true,
requestBody: true,
responseBody: true,
responseHeader: false,
error: true,
compact: true,
maxWidth: 120);
}
//设置请求头
Map<String, dynamic>? _setHeaders() {
return {
'Content-Type': 'application/json;charset=UTF-8',
};
}
// 构建网络请求
void request({
required String requestUrl,
required Method method,
Options? options,
Map<String, dynamic>? queryParameters,
Map<String, dynamic>? data,
required NetSuccessCallback successCallback,
NetErrorCallback? errorCallback,
}) async {
try {
final Response response = await dio.request(
requestUrl,
queryParameters: queryParameters,
data: data,
options: _checkOptions(method.value, options),
);
onNetSuccess(response, successCallback, errorCallback);
} on DioException catch (e) {
if (e.response != null) {
logger.e(
"请求错误:${e.response!.statusCode} ${e.response!.requestOptions.path}");
} else {
// Something happened in setting up or sending the request that triggered an Error
logger.e("请求错误:${e.error}");
}
await errorCallback?.call(500, "服务器请求错误");
}
}
// 构建网络请求同步请求
Future<dynamic> requestSync({
required String requestUrl,
required Method method,
Options? options,
Map<String, dynamic>? queryParameters,
Object? data,
}) async {
try {
final Response response = await dio.request(
requestUrl,
queryParameters: queryParameters,
data: data,
options: _checkOptions(method.value, options),
);
try {
final String data = response.data.toString();
final bool isCompute = data.length > 10 * 1024;
// loggerNoStack.i('isCompute:$isCompute');
final Map<String, dynamic> map = isCompute
? await jsonTransformer.parseJson(data)
: jsonTransformer.parseAndDecode(data);
var responseCode = map['code']?.toString();
if (responseCode == "100" || responseCode == "-100") {
logger.e("登录过期:${map['msg']}");
onLoginExpire();
}
return map;
} catch (e) {
logger.e("数据解析错误:${e.toString()}");
return {"code": 400, "message": "数据解析错误"};
}
} on DioException catch (e) {
if (e.response != null) {
logger.e(
"请求错误:${e.response!.statusCode} ${e.response!.requestOptions.path}");
} else {
// Something happened in setting up or sending the request that triggered an Error
logger.e("请求错误:${e.error}");
}
return {"code": 500, "message": "服务器请求错误"};
}
}
// 处理网络请求成功逻辑
void _onNetSuccess(Response response, NetSuccessCallback successCallback,
NetErrorCallback? errorCallback) async {
try {
final String data = response.data.toString();
final bool isCompute = data.length > 10 * 1024;
// loggerNoStack.i('isCompute:$isCompute');
final Map<String, dynamic> map = isCompute
? await jsonTransformer.parseJson(data)
: jsonTransformer.parseAndDecode(data);
var responseCode = map['code']?.toString();
if (responseCode == "100" || responseCode == "-100") {
logger.e("登录过期:${map['msg']}");
onLoginExpire();
return;
}
if (map['code'] == 0) {
await successCallback.call(map['data']);
} else {
logger.e("请求错误:${map['code']} ${map['msg']}");
await errorCallback?.call(map['code'], map['msg']);
}
} catch (e) {
logger.e("数据解析错误:${e.toString()}");
await errorCallback?.call(400, "数据解析错误");
}
}
}
Options _checkOptions(String method, Options? options) {
options ??= Options();
options.method = method;
return options;
}
typedef NetSuccessCallback = Function(dynamic data);
typedef NetSuccessListCallback = Function(List? list);
typedef NetErrorCallback = Function(dynamic code, String? msg);
enum Method {
get,
post,
put,
patch,
delete,
}
extension MethodExtension on Method {
String get value => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'][index];
}
class BaseEntity {
BaseEntity(this.code, this.message, this.data, {this.total = 0,this.amount = 0.0,this.pageNum = 0});
BaseEntity.fromJson(Map<String, dynamic> json) {
code = json['code'];
message = json['msg'] as String;
data = json['data'];
total = json['total'];
amount = json['amount'];
pageNum = json['pageNum'];
}
dynamic code;
late String message;
dynamic data;
dynamic total;
dynamic amount;
dynamic pageNum;
}
// You have generated a new plugin project without specifying the `--platforms`
// flag. A plugin project with no platform support was generated. To add a
// platform, run `flutter create -t plugin --platforms <platforms> .` under the
// same directory. You can also find a detailed instruction on how to add
// platforms in the `pubspec.yaml` at
// https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms.
import 'clx_flutter_net_platform_interface.dart';
class ClxFlutterNet {
Future<String?> getPlatformVersion() {
return ClxFlutterNetPlatform.instance.getPlatformVersion();
}
}
export 'package:dio/dio.dart';
export 'package:dio/io.dart';
export 'proxy.dart';
export 'transformer.dart';
export 'base_dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'clx_flutter_net_platform_interface.dart';
/// An implementation of [ClxFlutterNetPlatform] that uses method channels.
class MethodChannelClxFlutterNet extends ClxFlutterNetPlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodChannel = const MethodChannel('clx_flutter_net');
@override
Future<String?> getPlatformVersion() async {
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
}
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'clx_flutter_net_method_channel.dart';
abstract class ClxFlutterNetPlatform extends PlatformInterface {
/// Constructs a ClxFlutterNetPlatform.
ClxFlutterNetPlatform() : super(token: _token);
static final Object _token = Object();
static ClxFlutterNetPlatform _instance = MethodChannelClxFlutterNet();
/// The default instance of [ClxFlutterNetPlatform] to use.
///
/// Defaults to [MethodChannelClxFlutterNet].
static ClxFlutterNetPlatform get instance => _instance;
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [ClxFlutterNetPlatform] when
/// they register themselves.
static set instance(ClxFlutterNetPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<String?> getPlatformVersion() {
throw UnimplementedError('platformVersion() has not been implemented.');
}
}
import 'dart:io';
import 'package:clx_flutter_net/clx_flutter_net.dart';
import 'package:clx_flutter_util/clx_flutter_util.dart';
import 'package:flutter/material.dart';
const spProxyIp = "proxyIP"; // ip
const spProxyPort = "proxyPort"; // port
const spSwitchProxy = "switchProxy"; // 开关代理
bool setProxyFlag = false; // 设置代理标识 true 设置 false 不设置
/// dio 设置代理uri
void setProxyUri(Dio? dio, bool isProdEnv) {
setProxyFlag = !isProdEnv; //生产环境不设置代理
if (!setProxyFlag) return;
String? proxyIP = SpUtil.getString(spProxyIp);
String? proxyPort = SpUtil.getString(spProxyPort);
(dio?.httpClientAdapter as IOHttpClientAdapter?)?.createHttpClient = () {
HttpClient client = HttpClient();
// 是否设置代理:非生产环境,开启代理后,设置代理
bool isSetProxy = setProxyFlag &&
proxyIP != null &&
proxyIP.isNotEmpty &&
proxyPort != null &&
proxyPort.isNotEmpty &&
SpUtil.getBool(spSwitchProxy) == true;
if (isSetProxy) {
client.findProxy = (uri) => "PROXY $proxyIP:$proxyPort";
client.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
}
return client;
};
}
/// 设置代理widget
setProxyWidget(context) {
return (!setProxyFlag)
? Container()
: InkWell(
onTap: () => setProxyDialog(context),
child: Container(
height: 50.0,
padding: const EdgeInsets.only(left: 15.0, right: 15.0),
color: Colors.white,
margin: const EdgeInsets.only(top: 1.0),
child: const Row(
children: <Widget>[
Expanded(
child: Text("设置代理", style: TextStyle(fontSize: 15.0)),
),
Icon(Icons.arrow_forward_ios, color: Colors.grey, size: 16.0)
],
),
),
);
}
var ipController = TextEditingController();
var portController = TextEditingController();
/// 设置代理dialog
setProxyDialog(context) {
ipController.text = SpUtil.getString(spProxyIp) ?? "";
portController.text = SpUtil.getString(spProxyPort) ?? "";
showDialog(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: const Text("设置代理", textAlign: TextAlign.center),
children: [
_textFieldItem(title: "IP地址:", controller: ipController),
_textFieldItem(title: "端\t\t\t\t口:", controller: portController),
Row(
children: [
const SizedBox(width: 20.0),
const Text("开启代理:"),
const Spacer(),
Builder(builder: (context) {
return Switch(
activeColor: Colors.blue,
activeTrackColor: Colors.grey,
inactiveThumbColor: Colors.blue,
inactiveTrackColor: Colors.grey,
value: SpUtil.getBool(spSwitchProxy) ?? false,
onChanged: (bool value) {
SpUtil.putBool(spSwitchProxy, value);
(context as Element).markNeedsBuild();
});
}),
const SizedBox(width: 20.0),
],
),
InkWell(
onTap: () {
if (!_checkInfo()) {
return;
}
SpUtil.putString(
spProxyIp, ipController.value.text.toString().trim());
SpUtil.putString(
spProxyPort, portController.value.text.toString().trim());
exit(0);
},
child: Container(
margin: const EdgeInsets.only(
left: 30.0, right: 30.0, top: 30.0, bottom: 15.0),
padding: const EdgeInsets.symmetric(vertical: 12.0),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(50.0),
),
alignment: Alignment.center,
child: const Text("确定", style: TextStyle(color: Colors.white)),
),
)
],
);
},
);
}
///校验信息
bool _checkInfo() {
String ip = ipController.value.text.toString().trim();
String port = portController.value.text.toString().trim();
bool setProxy = SpUtil.getBool(spSwitchProxy) ?? false;
if (!setProxy) return true;
if (ip.isEmpty) {
ToastUtil.showToast("请输入ip地址");
return false;
}
if (port.isEmpty) {
ToastUtil.showToast("请输入端口号");
return false;
}
return true;
}
/// ip、端口 输入框
Widget _textFieldItem({title, controller}) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
margin: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
),
child: Row(
children: <Widget>[
Text(title, style: const TextStyle(fontSize: 15.0)),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: TextField(
maxLines: 1,
controller: controller,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
counterText: "",
border: InputBorder.none, //去掉下划线
),
),
),
),
],
),
);
}
import 'dart:convert';
import 'package:flutter/foundation.dart';
class MyTransformer {
Map<String, dynamic> parseAndDecode(String response) {
return jsonDecode(response) as Map<String, dynamic>;
}
Future<Map<String, dynamic>> parseJson(String text) {
return compute(parseAndDecode, text);
}
}
name: clx_flutter_net
description: A new Flutter plugin project.
version: 0.0.1
homepage:
version: 1.0.0
homepage: clxflutter.top
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: '>=3.1.5 <4.0.0'
......@@ -11,6 +12,12 @@ dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
clx_flutter_util:
git:
ref: '1.0.0'
url: 'https://t.clxkj.cn/openSourceLibrary/clx_flutter_util.git'
dio: 5.4.0 # 网络请求 dio
pretty_dio_logger: ^1.3.1 # 网络请求日志打印
dev_dependencies:
flutter_test:
......
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:clx_flutter_net/clx_flutter_net_method_channel.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
MethodChannelClxFlutterNet platform = MethodChannelClxFlutterNet();
const MethodChannel channel = MethodChannel('clx_flutter_net');
setUp(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
channel,
(MethodCall methodCall) async {
return '42';
},
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
});
test('getPlatformVersion', () async {
expect(await platform.getPlatformVersion(), '42');
});
}
import 'package:flutter_test/flutter_test.dart';
import 'package:clx_flutter_net/clx_flutter_net.dart';
import 'package:clx_flutter_net/clx_flutter_net_platform_interface.dart';
import 'package:clx_flutter_net/clx_flutter_net_method_channel.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
class MockClxFlutterNetPlatform
with MockPlatformInterfaceMixin
implements ClxFlutterNetPlatform {
@override
Future<String?> getPlatformVersion() => Future.value('42');
}
void main() {
final ClxFlutterNetPlatform initialPlatform = ClxFlutterNetPlatform.instance;
test('$MethodChannelClxFlutterNet is the default instance', () {
expect(initialPlatform, isInstanceOf<MethodChannelClxFlutterNet>());
});
test('getPlatformVersion', () async {
ClxFlutterNet clxFlutterNetPlugin = ClxFlutterNet();
MockClxFlutterNetPlatform fakePlatform = MockClxFlutterNetPlatform();
ClxFlutterNetPlatform.instance = fakePlatform;
expect(await clxFlutterNetPlugin.getPlatformVersion(), '42');
});
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论