Flutter网络请求插件flutter_nb_net的使用

Flutter网络请求插件flutter_nb_net的使用

可能是 Flutter 上最强的网络框架, 基于dio实现的非侵入式框架(不影响原有功能). 学习成本低、使用简单, 一行代码发起网络请求, 甚至无需初始化。

功能特性

  • RESTful API 设计
    • GET/POST/PUT/HEAD/DELETE/PATCH/DOWNLOAD
  • 可取消请求
  • 异步解析,数据量大不再卡顿
  • 全局错误处理(减少崩溃率)
  • 自定义解析器,支持全局和单个请求
  • 自定义解析方法
  • 配置请求参数
  • 漂亮的日志打印
  • 证书快速配置
  • 代理配置
  • 拦截器配置
  • 强制缓存模式/自定义缓存Key/缓存有效期/LRU缓存算法/缓存任何数据
  • 监听上传/下载进度

开始使用

pubspec.yaml 文件中添加依赖:

dependencies:
  flutter_nb_net: ^1.0.3

使用示例

全局配置

NetOptions.instance
      // 添加头信息
      .addHeaders({"aaa": '111'})
      // 设置基础URL
      .setBaseUrl("https://www.wanandroid.com/")
      // 设置代理/HTTPS
      .setHttpClientAdapter(IOHttpClientAdapter()
        ..onHttpClientCreate = (client) {
          client.findProxy = (uri) {
            return 'PROXY 192.168.20.43:8888';
          };
          client.badCertificateCallback =
              (X509Certificate cert, String host, int port) => true;
          return client;
        })
      // 添加 Cookie 管理器
      .addInterceptor(CookieManager(CookieJar()))
      // 添加缓存管理器
      .addInterceptor(DioCacheManager(CacheConfig(
        baseUrl: "https://www.wanandroid.com/",
      )).interceptor)
      // 添加缓存拦截器
      .addInterceptor(DioCacheInterceptor(
          options: CacheOptions(
        store: MemCacheStore(),
        policy: CachePolicy.forceCache,
        hitCacheOnErrorExcept: [401, 403],
        maxStale: const Duration(days: 7),
        priority: CachePriority.normal,
        cipher: null,
        keyBuilder: CacheOptions.defaultCacheKeyBuilder,
        allowPostMethod: false,
      )))
       // 设置全局解析器
      .setHttpDecoder(MyHttpDecoder.getInstance())
       // 设置超时时间
      .setConnectTimeout(const Duration(milliseconds: 3000))
      // 允许打印log,默认为 true
      .enableLogger(true)
      .create();

发起请求

获取原始数据
var appResponse = await get("banner/json");
appResponse.when(success: (dynamic) {
  // var size = model.data?.length;
  debugPrint("成功返回$dynamic");
}, failure: (String msg, int code) {
  debugPrint("失败了:msg=$msg/code=$code");
});
获取带泛型的数据
var appResponse = await get("banner/json", decodeType: BannerModel());
appResponse.when(success: (BannerModel model) {
  var size = model.data?.length;
  debugPrint("成功返回$size条");
}, failure: (String msg, int code) {
  debugPrint("失败了:msg=$msg/code=$code");
});
获取完整泛型的数据
var appResponse = await get<BannerModel, BannerModel>("banner/json",
    decodeType: BannerModel());
appResponse.when(success: (BannerModel model) {
  var size = model.data?.length;
  debugPrint("成功返回$size条");
}, failure: (String msg, int code) {
  debugPrint("失败了:msg=$msg/code=$code");
});
发起 POST 请求
var appResponse = await post<UserWrapperModel, UserWrapperModel>(
    "user/login",
    decodeType: UserWrapperModel(),
    queryParameters: {"username": '你的账号', "password": '你的密码'});
appResponse.when(success: (UserWrapperModel model) {
  var nickname = model.data?.nickname;
  debugPrint("成功返回nickname=$nickname");
}, failure: (String msg, int code) {
  debugPrint("失败了:msg=$msg/code=$code");
});
发起包含文件上传的 POST 请求
var path = '/Users/apple/Library/Developer/CoreSimulator/Devices/89F6C1CC-378B-48B3-9B8F-BA43E7870781/data/Containers/Data/Application/05B810A0-7552-4C3A-8080-800C06A15EC7/tmp/image_picker_289C3878-B39A-41AC-907F-18AE7A9DAE6E-8483-00001BCFA0738BBB.jpg';

var params = {'file': await MultipartFile.fromFile(path)};

var appResponse = await post<UserWrapperModel, UserWrapperModel>(
    "v1/task/task/headPortrait",
    options: Options(contentType: 'formData'),
    decodeType: UserWrapperModel(),
    data: params);

appResponse.when(success: (UserWrapperModel model) {
  var nickname = model.data?.nickname;
  debugPrint("成功返回nickname=$nickname");
}, failure: (String msg, int code) {
  debugPrint("失败了:msg=$msg/code=$code");
});
使用自定义解析器的 POST 请求
var appResponse = await post<UserModel, UserModel>("user/login",
    decodeType: UserModel(),
    httpDecode: MyHttpDecoder.getInstance(),
    queryParameters: {"username": '', "password": ''});
appResponse.when(success: (UserModel model) {
  var nickname = model.nickname;
  debugPrint("成功返回nickname=$nickname");
}, failure: (String msg, int code) {
  debugPrint("失败了:msg=$msg/code=$code");
});
使用自定义解析器的 GET 请求
var appResponse = await get<BannerBean, List<BannerBean>>("banner/json",
    decodeType: BannerBean(), httpDecode: MyHttpDecoder.getInstance());
appResponse.when(success: (List<BannerBean> model) {
  var size = model.length;
  debugPrint("成功返回$size条");
}, failure: (String msg, int code) {
  debugPrint("失败了:$msg");
});
自定保存和携带 cookie 的请求
var appResponse = await get<CollectModel, CollectModel>(
    "lg/collect/list/0/json",
    decodeType: CollectModel(),
    httpDecode: MyHttpDecoder.getInstance());
appResponse.when(success: (CollectModel model) {
  var size = model.datas?.length;
  debugPrint("成功返回$size条");
}, failure: (String msg, int code) {
  debugPrint("失败了:$msg");
});
带缓存的 GET 请求
var appResponse = await get<BannerModel, BannerModel>("banner/json",
    options: buildCacheOptions(const Duration(days: 7)),
    decodeType: BannerModel());
appResponse.when(success: (BannerModel model) {
  var size = model.data?.length;
  debugPrint("成功返回$size条");
}, failure: (String msg, int code) {
  debugPrint("失败了:msg=$msg/code=$code");
});
通过回调解析返回的请求
var appResponse = await get<BannerModel, List<BannerBean>>("banner/json",
    options: buildCacheOptions(const Duration(days: 7)),
    decodeType: BannerModel(), converter: (response) {
  var errorCode = response.data['errorCode'];

  /// 请求成功
  if (errorCode == 0) {
    var data = response.data['data'];
    var dataList = List<BannerBean>.from(
        data.map((item) => BannerBean.fromJson(item)).toList());
    return Result.success(dataList);
  } else {
    var errorMsg = response.data['errorMsg'];
    return Result.failure(msg: errorMsg, code: errorCode);
  }
});
appResponse.when(success: (List<BannerBean> model) {
  debugPrint("成功返回${model.length}条");
}, failure: (String msg, int code) {
  debugPrint("失败了:msg=$msg/code=$code");
});

完整示例代码

import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:flutter/material.dart';
import 'package:flutter_nb_net/flutter_net.dart';
import 'model/banner_model.dart';
import 'model/collect_model.dart';
import 'model/user_model.dart';
import 'model/user_wrapper_model.dart';
import 'my_http_decoder.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  NetOptions.instance
      // 添加头信息
      .addHeaders({"aaa": '111'})
      .setBaseUrl("https://www.wanandroid.com/")
      // 设置代理/HTTPS
      // .setHttpClientAdapter(IOHttpClientAdapter()
      //   ..onHttpClientCreate = (client) {
      //     client.findProxy = (uri) {
      //       return 'PROXY 192.168.20.43:8888';
      //     };
      //     client.badCertificateCallback =
      //         (X509Certificate cert, String host, int port) => true;
      //     return client;
      //   })
      // 添加 Cookie 管理器
      .addInterceptor(CookieManager(CookieJar()))
      // 添加缓存管理器
      // .addInterceptor(DioCacheManager(CacheConfig(
      //   baseUrl: "https://www.wanandroid.com/",
      // )).interceptor)
      // 添加缓存拦截器
      .addInterceptor(DioCacheInterceptor(
          options: CacheOptions(
        store: MemCacheStore(),
        policy: CachePolicy.forceCache,
        hitCacheOnErrorExcept: [401, 403],
        maxStale: const Duration(days: 7),
        priority: CachePriority.normal,
        cipher: null,
        keyBuilder: CacheOptions.defaultCacheKeyBuilder,
        allowPostMethod: false,
      )))
      // 设置全局解析器
      // .setHttpDecoder(MyHttpDecoder.getInstance())
      // 设置超时时间
      .setConnectTimeout(const Duration(milliseconds: 3000))
      // 允许打印log,默认为 true
      .enableLogger(true)
      .create();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  /// Get 请求原始数据
  void requestGet() async {
    var appResponse = await get("banner/json");
    appResponse.when(success: (dynamic) {
      debugPrint("成功返回$dynamic");
    }, failure: (String msg, int code) {
      debugPrint("失败了:msg=$msg/code=$code");
    });
  }

  /// Get 请求数据,不带泛型
  void requestGet1() async {
    var appResponse = await get("banner/json", decodeType: BannerModel());
    appResponse.when(success: (BannerModel model) {
      var size = model.data?.length;
      debugPrint("不带泛型成功返回$size条");
    }, failure: (String msg, int code) {
      debugPrint("失败了:msg=$msg/code=$code");
    });
  }

  /// Get 请求数据,完整的泛型
  void requestGet2() async {
    var appResponse = await get<BannerModel, BannerModel>("banner/json",
        decodeType: BannerModel());
    appResponse.when(success: (BannerModel model) {
      var size = model.data?.length;
      debugPrint("成功返回$size条");
    }, failure: (String msg, int code) {
      debugPrint("失败了:msg=$msg/code=$code");
    });
  }

  /// Post 请求
  void requestPost() async {
    var appResponse = await post<UserWrapperModel, UserWrapperModel>(
        "user/login",
        decodeType: UserWrapperModel(),
        queryParameters: {"username": '你的账号', "password": '你的密码'});
    appResponse.when(success: (UserWrapperModel model) {
      var nickname = model.data?.nickname;
      debugPrint("成功返回nickname=$nickname");
    }, failure: (String msg, int code) {
      debugPrint("失败了:msg=$msg/code=$code");
    });
  }

  /// Post 请求
  void requestPostFile() async {
    var path = '/Users/apple/Library/Developer/CoreSimulator/Devices/89F6C1CC-378B-48B3-9B8F-BA43E7870781/data/Containers/Data/Application/05B810A0-7552-4C3A-8080-800C06A15EC7/tmp/image_picker_289C3878-B39A-41AC-907F-18AE7A9DAE6E-8483-00001BCFA0738BBB.jpg';

    var params = {'file': await MultipartFile.fromFile(path)};

    var appResponse = await post<UserWrapperModel, UserWrapperModel>(
        "v1/task/task/headPortrait",
        options: Options(contentType: 'formData'),
        decodeType: UserWrapperModel(),
        data: params);

    appResponse.when(success: (UserWrapperModel model) {
      var nickname = model.data?.nickname;
      debugPrint("成功返回nickname=$nickname");
    }, failure: (String msg, int code) {
      debugPrint("失败了:msg=$msg/code=$code");
    });
  }

  /// 自定义Decoder的 Post 请求
  void requestCustomDecoderPost() async {
    var appResponse = await post<UserModel, UserModel>("user/login",
        decodeType: UserModel(),
        httpDecode: MyHttpDecoder.getInstance(),
        queryParameters: {"username": '', "password": ''});
    appResponse.when(success: (UserModel model) {
      var nickname = model.nickname;
      debugPrint("成功返回nickname=$nickname");
    }, failure: (String msg, int code) {
      debugPrint("失败了:msg=$msg/code=$code");
    });
  }

  /// 自定义Decoder的 Get 请求
  void requestCustomGet() async {
    var appResponse = await get<BannerBean, List<BannerBean>>("banner/json",
        decodeType: BannerBean(), httpDecode: MyHttpDecoder.getInstance());
    appResponse.when(success: (List<BannerBean> model) {
      var size = model.length;
      debugPrint("成功返回$size条");
    }, failure: (String msg, int code) {
      debugPrint("失败了:$msg");
    });
  }

  /// 自定保存和携带 cookie 的请求
  void requestCookieGet() async {
    var appResponse = await get<CollectModel, CollectModel>(
        "lg/collect/list/0/json",
        decodeType: CollectModel(),
        httpDecode: MyHttpDecoder.getInstance());
    appResponse.when(success: (CollectModel model) {
      var size = model.datas?.length;
      debugPrint("成功返回$size条");
    }, failure: (String msg, int code) {
      debugPrint("失败了:$msg");
    });
  }

  /// 带缓存的 Get 请求
  void requestCacheGet() async {
    var appResponse = await get<BannerModel, BannerModel>("banner/json",
        options: buildCacheOptions(const Duration(days: 7)),
        decodeType: BannerModel());
    appResponse.when(success: (BannerModel model) {
      var size = model.data?.length;
      debugPrint("成功返回$size条");
    }, failure: (String msg, int code) {
      debugPrint("失败了:msg=$msg/code=$code");
    });
  }

  /// 通过回调解析返回的请求
  void requestCallBack() async {
    var appResponse = await get<BannerModel, List<BannerBean>>("banner/json",
        options: buildCacheOptions(const Duration(days: 7)),
        decodeType: BannerModel(), converter: (response) {
      var errorCode = response.data['errorCode'];

      /// 请求成功
      if (errorCode == 0) {
        var data = response.data['data'];
        var dataList = List<BannerBean>.from(
            data.map((item) => BannerBean.fromJson(item)).toList());
        return Result.success(dataList);
      } else {
        var errorMsg = response.data['errorMsg'];
        return Result.failure(msg: errorMsg, code: errorCode);
      }
    });
    appResponse.when(success: (List<BannerBean> model) {
      debugPrint("成功返回${model.length}条");
    }, failure: (String msg, int code) {
      debugPrint("失败了:msg=$msg/code=$code");
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Dio'),
        ),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            TextButton(
              onPressed: () {
                requestGet();
              },
              child: const Text('Get,原始数据'),
            ),
            TextButton(
              onPressed: () {
                requestGet1();
              },
              child: const Text('Get 不带泛型'),
            ),
            TextButton(
              onPressed: () {
                requestGet2();
              },
              child: const Text('Get 带泛型'),
            ),
            TextButton(
              onPressed: () {
                requestPostFile();
              },
              child: const Text('上传图片'),
            ),
            TextButton(
              onPressed: () {
                requestPost();
              },
              child: const Text('Post'),
            ),
            TextButton(
              onPressed: () {
                requestCustomGet();
              },
              child: const Text('requestCustomGet'),
            ),
            TextButton(
              onPressed: () {
                requestCustomDecoderPost();
              },
              child: const Text('requestCustomPost'),
            ),
            const SizedBox(
              height: 20,
            ),
            const Text("请先打开34行注释,才能测试cookie"),
            TextButton(
              onPressed: () {
                requestCookieGet();
              },
              child: const Text('requestCookieGet'),
            ),
            const SizedBox(
              height: 20,
            ),
            const Text("请先打开39行注释,才能测试缓存"),
            TextButton(
              onPressed: () {
                requestCacheGet();
              },
              child: const Text('requestCacheGet'),
            ),
            TextButton(
              onPressed: () {
                requestCallBack();
              },
              child: const Text('requestCallBack'),
            ),
          ],
        ));
  }
}

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

1 回复

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


当然,下面是一个关于如何使用 flutter_nb_net 插件进行网络请求的示例代码。flutter_nb_net 是一个 Flutter 插件,用于简化网络请求处理。下面是一个基本的示例,展示如何配置和使用它来进行 GET 和 POST 请求。

首先,确保你已经在 pubspec.yaml 文件中添加了 flutter_nb_net 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_nb_net: ^latest_version  # 请替换为最新版本号

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

接下来,配置和使用 flutter_nb_net 插件进行网络请求。以下是一个完整的示例:

import 'package:flutter/material.dart';
import 'package:flutter_nb_net/flutter_nb_net.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Network Request Example'),
        ),
        body: Center(
          child: MyNetworkRequestWidget(),
        ),
      ),
    );
  }
}

class MyNetworkRequestWidget extends StatefulWidget {
  @override
  _MyNetworkRequestWidgetState createState() => _MyNetworkRequestWidgetState();
}

class _MyNetworkRequestWidgetState extends State<MyNetworkRequestWidget> {
  final NetRequest _netRequest = NetRequest();

  @override
  void initState() {
    super.initState();
    // 配置网络请求
    _netRequest.init(
      baseUrl: 'https://api.example.com', // 替换为你的API基础URL
      debugMode: true, // 是否开启调试模式
      timeout: 10000, // 请求超时时间(毫秒)
    );

    // 发起GET请求示例
    _netRequest.getRequest('path/to/your/endpoint', params: {'key': 'value'}).then((response) {
      print('GET 请求响应: ${response.data}');
    }).catchError((error) {
      print('GET 请求错误: $error');
    });

    // 发起POST请求示例
    var postData = {'key': 'value'};
    _netRequest.postRequest('path/to/your/endpoint', data: postData).then((response) {
      print('POST 请求响应: ${response.data}');
    }).catchError((error) {
      print('POST 请求错误: $error');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text('网络请求已发送,请查看控制台输出');
  }
}

// NetRequestResult 是请求结果的封装类,包含 data, statusCode, headers 等信息
class NetRequestResult {
  final int statusCode;
  final String? message;
  final dynamic data;
  final Map<String, List<String>>? headers;

  NetRequestResult({
    required this.statusCode,
    this.message,
    this.data,
    this.headers,
  });

  @override
  String toString() {
    return 'NetRequestResult{statusCode: $statusCode, message: $message, data: $data, headers: $headers}';
  }
}

注意事项

  1. 依赖版本:请确保将 flutter_nb_net: ^latest_version 替换为实际的最新版本号。
  2. API基础URL:将 baseUrl 替换为你的实际API基础URL。
  3. 请求路径:将 path/to/your/endpoint 替换为你的实际API路径。
  4. 调试模式debugModetrue 时,插件会输出详细的请求和响应日志,便于调试。

结论

通过上述代码,你可以看到如何使用 flutter_nb_net 插件进行基本的 GET 和 POST 请求。该插件封装了底层的网络请求逻辑,使代码更加简洁和易于维护。

回到顶部