Flutter网络请求插件fx_http的使用

Flutter网络请求插件fx_http的使用

fx_http

一个基础的Flutter网络请求工具,包含多种请求方式,自动数据转模型。

可在 pub.dev 搜索 'fx_http' 使用。

核心类 DioUtil

1、配置

使用前必须先初始化,指定接口服务器地址:

/// 初始化DioUtil的参数 (也可以使用initOption)  **** 切记在使用DioUtils前初始化,推荐在main入口函数中调用 ****
/// baseUrl           请求地址
/// isDebugMode       调试模式, debug时会打印接口日志到控制台,弹出接口错误提示
/// apiSuccessCode    api接口正确码
/// sendTimeout       发送超时时间 (秒)
/// connectTimeout    请求超时时间 (秒)
/// receiveTimeout    接收超时时间 (秒)
/// validateApiSuccess    根据返回的数据判断是否请求成功的闭包
static init(String baseUrl, {bool isDebugMode = false, int apiSuccessCode = 0, int sendTimeout = 15, int connectTimeout = 15, int receiveTimeout = 15, ValidateApiSuccess? validateApiSuccess});

/// 初始化
DioUtils.init("https://github.com/", isDebugMode: true, apiSuccessCode: 0);

也可以使用 DioUtils.initOption 初始化,自由度更高。

1.1 可以开启debugMode下的控制台日志,使用前必须初始化打印工具(推荐在main入口函数中调用),可以指定tag, 指定运行模式(release模式下,Log.e, Log.json 不会在控制台输出):

Log.init(tag: 'fx_http', isDebug: true);
Log.d('Hello, Teeup Flutter Developers');

/// 是否开启控制台日志 - 仅针对debug|profile(release模式下默认关闭)
void enableConsoleLogging({bool enable = true})

1.2 如果需要切换接口的服务器地址,可以使用updateOptions方法来修改,后面所有的接口请求都会使用新的地址发器请求。

/// 更改设置 (这里的更改是全局的 ====== 请注意使用)
/// baseUrl       请求地址
/// headers       请求头
void updateOptions({String? baseUrl, Map<String, dynamic>? headers});

1.3 添加拦截器(可以添加拦截器用于在请求发起前,或者收到response后做一些事情)

/// 添加自定义拦截器 (可以添加请求头)
void addInterceptors(Interceptor interceptor)

1.4 开启代理抓包,可用于测试接口

/// 开启抓包 (仅适用于非web端)
void openProxy(String proxyIp, int proxyPort);
// 关闭抓包
void closeProxy();

1.5 如果想取消所有请求,可以使用cancelAllRequest,如果想取消特定请求,可以使用cancelRequest

/// 取消所有请求
void cancelAllRequest();

/// 根据请求地址取消特定请求 (请求)
/// url  请求路径 比如:'trade/open'
bool cancelRequest({String? url})

2、发起请求,主要有四个方法可以使用:

2.1 异步请求方式,采用异步回调的方式返回结果,数据直接在onSuccess、onSuccessList或者onPageSuccessList中获取。单个请求时可用这个方法,如果有多个请求互相依赖时使用这种方式会形成嵌套调用,就不推荐了。

/// brief http请求 (通过回调函数回调结果)
/// @param method             请求方式
/// @param api                接口地址
/// @param params             post请求参数
/// @param queryParameters    get请求参数
/// @param isShow             请求时是否显示吐司
/// @param isPageData         是否是分页数据
/// @param isList             是否是列表数据
/// @param model              要解析的数据模型
/// @param onSuccess          请求成功回调 (返回数据)
/// @param onSuccessList      请求成功回调 (返回列表数据)
/// @param onPageSuccessList  请求成功回调 (返回分页列表数据)
/// @param onError            请求失败回调
/// @param cancelToken        取消请求的token
/// @param options            对请求的设置
void asyncRequestNetwork<T>(Method method, String api, {
  dynamic params,
  Map<String, dynamic>? queryParameters,
  bool isShow : false,      // 是否显示加载吐司
  bool isPageData : false,  // 是否是分页数据
  bool isList : false,
  BaseModel? model,
  Function(T? t)? onSuccess,
  Function(List<T> list)? onSuccessList,
  Function(List<T> list, int total, int perPage, int currentPage)? onPageSuccessList,
  Function(int code, String msg)? onError,
  CancelToken? cancelToken,
  Options? options
});

2.2 Future请求方式,可以使用await阻塞同步返回结果,也可以使用then异步返回结果。单个、多个都可以使用该方法,数据需要自己从返回值里面取一下。 特别适用于FutureBuilder构建的组建中。

/// brief http请求 (通过Future方式请求,可以使用await阻塞直到获取到结果)
/// @param method             请求方式
/// @param url                接口地址
/// @param isPageData         是否是分页数据
/// @param params             post请求参数
/// @param queryParameters    get请求参数
/// @param model              要解析的数据模型
/// @param cancelToken        取消请求的token
/// @param options            对请求的设置
Future<BaseEntity<T>> requestNetwork<T>(Method method, String url, {
  bool isPageData = false,
  bool isShow = false,      // 是否显示加载吐司
  dynamic params,
  Map<String, dynamic>? queryParameters,
  BaseModel? model,
  CancelToken? cancelToken,
  Options? options
})

2.3 上传图片|文件,Future请求方式,可以使用await阻塞同步返回结果,也可以使用then异步返回结果。

/// brief 上传图片 (通过Future方式请求,可以使用await阻塞直到获取到结果)
/// @param url                接口地址
/// @param filePath           图片地址
/// @param attachmentType     上传附件类型
/// @param customAttachmentType     上传附件类型 (attachmentType为custom时,取customAttachmentType的值, 若为空默认为"system")
/// @param fileName           图片名字
/// @param mime               图片格式
/// @param showProgress       是否显示上传进度
/// @param onSendProgress     发送文件进度回调
/// @param onReceiveProgress  接收文件进度回调
/// @param cancelToken        取消请求的token
/// @param options            对请求的设置
Future<BaseEntity<T>> uploadImage<T>(String url, String filePath, AttachmentType attachmentType, {
  String? customAttachmentType,
  String? fileName,
  String mime = 'png',
  bool showProgress = false,
  bool isShow = false,      // 是否显示加载吐司
  ProgressCallback? onSendProgress,
  ProgressCallback? onReceiveProgress,
  CancelToken? cancelToken,
  Options? options
})

2.4 下载图片|文件,Future请求方式,可以使用await阻塞同步返回结果,也可以使用then异步返回结果。

/// brief 下载文件 (通过Future方式请求,可以使用await阻塞直到获取到结果)
/// @param uri                文件地址
/// @param savePath           文件保存地址
/// @param showProgress       是否显示下载进度
/// @param deleteOnError      下载失败后删除文件
/// @param onReceiveProgress  接收文件进度回调
/// @param cancelToken        取消请求的token
/// @param options            对请求的设置
Future<bool> downloadFile(Uri uri, String savePath, AttachmentType attachmentType, {
  Map? data,
  bool showProgress = false,
  bool deleteOnError = true,
  bool isShow = false,      // 是否显示加载吐司
  ProgressCallback? onReceiveProgress,
  CancelToken? cancelToken,
  Options? options
});

3、临时需要发起不同于主接口请求,但是又不想改变主服务器地址的,可以直接指定服务器接口地址,使用以下两个方法:

3.1 异步请求方式,采用异步回调的方式返回结果,数据直接在onSuccess、onSuccessList或者onPageSuccessList中获取。单个请求时可用这个方法,如果有多个请求互相依赖时使用这种方式会形成嵌套调用,就不推荐了。

/// brief http请求 (通过回调函数回调结果)
/// @param method             请求方式
/// @param url                接口地址
/// @param params             post请求参数
/// @param queryParameters    get请求参数
/// @param isShow             请求时是否显示吐司
/// @param isPageData         是否是分页数据
/// @param isList             是否是列表数据
/// @param model              要解析的数据模型
/// @param onSuccess          请求成功回调 (返回数据)
/// @param onSuccessList      请求成功回调 (返回列表数据)
/// @param onPageSuccessList  请求成功回调 (返回分页列表数据)
/// @param onError            请求失败回调
/// @param cancelToken        取消请求的token
/// @param options            对请求的设置
/// @param needDecrypt        是否需要对结果解密
/// @param secret             解密密钥
/// @param interceptors       要添加的拦截器
/// @param isJson             后台返回的结果是否是json字符串
void newAsyncRequestNetwork<T>(String baseUrl, String path, Method method, {
  dynamic params,
  Map<String, dynamic>? queryParameters,
  bool isShow = false,      // 是否显示加载吐司
  bool isPageData = false,  // 是否是分页数据
  bool isList = false,
  BaseModel? model,
  Function(T? t)? onSuccess,
  Function(List<T> list)? onSuccessList,
  Function(List<T> list, int total, int perPage, int currentPage)? onPageSuccessList,
  Function(int code, String msg)? onError,
  CancelToken? cancelToken,
  Options? options,
  bool needDecrypt = false,
  String? secret,
  List<Interceptor>? interceptors,
  int sendTimeout = 15,
  int connectTimeout = 15,
  int receiveTimeout = 15,
  bool isJson = true
})

3.2 Future请求方式,可以使用await阻塞同步返回结果,也可以使用then异步返回结果。单个、多个都可以使用该方法,数据需要自己从返回值里面取一下。 特别适用于FutureBuilder构建的组建中。

/// brief http请求 (通过Future方式请求,可以使用await阻塞直到获取到结果)
/// @param method             请求方式
/// @param url                接口地址
/// @param isPageData         是否是分页数据
/// @param params             post请求参数
/// @param queryParameters    get请求参数
/// @param model              要解析的数据模型
/// @param cancelToken        取消请求的token
/// @param options            对请求的设置
/// @param needDecrypt        是否需要对结果解密
/// @param secret             解密密钥
/// @param interceptors       要添加的拦截器
/// @param isJson             后台返回的结果是否是json字符串
Future<BaseEntity<T>> newRequestNetwork<T>(String baseUrl, String path, Method method, {
  dynamic params,
  Map<String, dynamic>? queryParameters,
  bool isShow : false,      // 是否显示加载吐司
  bool isPageData : false,  // 是否是分页数据
  BaseModel? model,
  CancelToken? cancelToken,
  Options? options,
  bool needDecrypt = false,
  String? secret,
  List<Interceptor>? interceptors,
  int sendTimeout = 15,
  int connectTimeout = 15,
  int receiveTimeout = 15,
  bool isJson = true
})

4、核心实体抽象类 BaseModel

建议所有自定义模型都实现BaseModel的抽象方法,方便接口请求数据可以统一的转模型,提高数据模型的标准性

class TestModel implements BaseModel {
  int _age;
  String _sex;
  
  TestModel.fromJson(dynamic json) {
    _age = json["age"];
    _sex = json["sex"];
  }
  
  [@override](/user/override)
  BaseModel fromJson(json) {
    return TestModel.fromJson(json);
  }

  [@override](/user/override)
  Map<String, dynamic> toJson() {
    var map = <String, dynamic>{};
    map["age"] = _age;
    map["sex"] = _sex;
    return map;
  }
}

// 使用如下,传入model,返回的就已经是传入的model类型的数据了
BaseEntity<TestModel> result = await DioHttp.requestNetwork<TestModel>(Method.get, Api.Symbol_List, queryParameters: params, model: TestModel());
if (result.code == 0 && null != result.data) {
  Log.d(result.data!);
}

完整示例 Demo

import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:fx_http/fx_http.dart';
import 'package:sp_util/sp_util.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await SpUtil.getInstance();

  Log.init(tag: 'fx_http', isDebug: true);
  DioUtils.init("http://user.gmandarin.com/api/", isDebugMode: true, apiSuccessCode: 0, validateApiSuccess: (result) => (0 == result.code && result.success));
  DioUtils.instance.enableConsoleLogging();

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key) {
    configLoading();
  }

  void configLoading() {
    EasyLoading.instance
      ..displayDuration = const Duration(milliseconds: 2000)
      ..indicatorType = EasyLoadingIndicatorType.fadingCircle
      ..loadingStyle = EasyLoadingStyle.custom
      ..indicatorSize = 45.0
      ..radius = 10.0
      ..progressColor = Colors.transparent
      ..backgroundColor = Colors.transparent
      ..indicatorColor = Colors.transparent
      ..textColor = Colors.white
      ..maskColor = Colors.black26
      ..userInteractions = false
      ..indicatorWidget = const SizedBox(
        width: 70,
        height: 70,
        child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.white)),
      )
      ..dismissOnTap = true
      ..customAnimation = CustomAnimation();
  }

  // This widget is the root of your application.
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FxHttp',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'FxHttp'),
      builder: EasyLoading.init(
          builder: (BuildContext context, Widget? child) {
            return MediaQuery(
              data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
              child: GestureDetector(
                onTap: () {
                  FocusScopeNode currentFocus = FocusScope.of(context);
                  if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
                    FocusManager.instance.primaryFocus!.unfocus();
                  }
                },
                child: child,
              ),
            );
          }
      )
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: FutureBuilder<List<Map>>(
        future: _requestData(),
        initialData: const [],
        builder: (BuildContext context, AsyncSnapshot<List<Map>> snapshot) {
          if (snapshot.hasData && snapshot.data!.isNotEmpty) {
            return ListView.separated(
                itemBuilder: (BuildContext context, int index) {
                  return Container(
                    color: Colors.white,
                    padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(snapshot.data![index]["name"] ?? "无标题", style: const TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.bold)),
                        Row(
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: [
                            Text(snapshot.data![index]["category"] ?? "无标题", style: const TextStyle(fontSize: 13, color: Colors.grey, fontWeight: FontWeight.normal)),
                            const SizedBox(width: 20),
                            Text("点差: ${snapshot.data![index]["spread"] ?? "无标题"}", style: const TextStyle(fontSize: 13, color: Colors.grey, fontWeight: FontWeight.normal)),
                            const SizedBox(width: 20),
                            Text("精度: ${snapshot.data![index]["digits"] ?? "无标题"}", style: const TextStyle(fontSize: 13, color: Colors.grey, fontWeight: FontWeight.normal))
                          ],
                        )
                      ]
                    ),
                  );
                },
                separatorBuilder: (BuildContext context, int index) {
                  return Divider(height: 1, color: Colors.grey[300]);
                },
                itemCount: snapshot.data!.length
            );
          } else {
            return const Center(
              child: Text("没有数据")
            );
          }
        },
      )// This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  Future<List<Map>> _requestData() async {
    EasyLoading.show();
    var result = await DioHttp.requestNetwork<Map>(Method.get, "symbols");
    EasyLoading.dismiss();
    return result.listData;
  }
}

class CustomAnimation extends EasyLoadingAnimation {
  CustomAnimation();

  [@override](/user/override)
  Widget buildWidget(
      Widget child,
      AnimationController controller,
      AlignmentGeometry alignment,
      ) {
    double opacity = controller.value;
    return Opacity(
      opacity: opacity,
      child: RotationTransition(
        turns: controller,
        child: child,
      ),
    );
  }
}

更多关于Flutter网络请求插件fx_http的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter网络请求插件fx_http的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


fx_http 是一个基于 Dio 的 Flutter 网络请求插件,它简化了网络请求的流程,并提供了更友好的 API。fx_http 支持 GET、POST、PUT、DELETE 等常见的 HTTP 请求方法,并且可以轻松处理 JSON 数据、文件上传、下载等功能。

安装

首先,你需要在 pubspec.yaml 文件中添加 fx_http 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  fx_http: ^1.0.0  # 请根据实际情况使用最新版本

然后运行 flutter pub get 来安装依赖。

基本用法

1. 初始化

在使用 fx_http 之前,通常需要先进行初始化配置。你可以在 main.dart 中进行初始化:

import 'package:fx_http/fx_http.dart';

void main() {
  FxHttp.init(
    baseUrl: 'https://jsonplaceholder.typicode.com', // 设置基础URL
    connectTimeout: 5000, // 连接超时时间
    receiveTimeout: 3000, // 接收数据超时时间
  );
  runApp(MyApp());
}

2. 发起 GET 请求

import 'package:fx_http/fx_http.dart';

Future<void> fetchData() async {
  try {
    var response = await FxHttp.get('/posts/1');
    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}

3. 发起 POST 请求

import 'package:fx_http/fx_http.dart';

Future<void> postData() async {
  try {
    var response = await FxHttp.post('/posts', data: {
      'title': 'foo',
      'body': 'bar',
      'userId': 1,
    });
    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}

4. 发起 PUT 请求

import 'package:fx_http/fx_http.dart';

Future<void> updateData() async {
  try {
    var response = await FxHttp.put('/posts/1', data: {
      'title': 'foo updated',
      'body': 'bar updated',
      'userId': 1,
    });
    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}

5. 发起 DELETE 请求

import 'package:fx_http/fx_http.dart';

Future<void> deleteData() async {
  try {
    var response = await FxHttp.delete('/posts/1');
    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}

6. 文件上传

import 'package:fx_http/fx_http.dart';
import 'package:http_parser/http_parser.dart';

Future<void> uploadFile() async {
  try {
    var file = await MultipartFile.fromFile(
      '/path/to/file',
      contentType: MediaType('image', 'jpeg'),
    );

    var response = await FxHttp.post('/upload', data: FormData.fromMap({
      'file': file,
    }));

    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}

7. 文件下载

import 'package:fx_http/fx_http.dart';
import 'dart:io';

Future<void> downloadFile() async {
  try {
    var response = await FxHttp.download(
      '/file/path',
      '/local/path/to/save/file',
    );
    print('File downloaded to: ${response.data}');
  } catch (e) {
    print('Error: $e');
  }
}

高级配置

fx_http 提供了丰富的配置选项,例如设置请求头、拦截器、缓存等。

1. 设置请求头

FxHttp.setHeaders({
  'Authorization': 'Bearer your_token',
});

2. 添加拦截器

FxHttp.addInterceptor(InterceptorsWrapper(
  onRequest: (options, handler) {
    // 在请求发送之前做一些处理
    return handler.next(options);
  },
  onResponse: (response, handler) {
    // 在收到响应之前做一些处理
    return handler.next(response);
  },
  onError: (error, handler) {
    // 在请求失败时做一些处理
    return handler.next(error);
  },
));

3. 缓存配置

FxHttp.setCacheOptions(
  cacheOptions: CacheOptions(
    store: MemCacheStore(),
    policy: CachePolicy.request,
    maxAge: Duration(days: 1),
  ),
);
回到顶部