相关文章推荐
有腹肌的烈酒  ·  MAC ...·  4 月前    · 
迷茫的警车  ·  java url 反转义-掘金·  1 年前    · 
潇洒的西瓜  ·  [原创]springboot 中 ...·  1 年前    · 
void uploadPhoto(File file) async{
    String path = file.path;
    var name = path.substring(path.lastIndexOf("/") + 1, path.length);
    String imageType = name.substring(name.lastIndexOf(".") + 1, name.length);
    var image = await MultipartFile.fromFile(
        path,
        filename: name,
    print("文件路径="+path);
    print("文件名="+name);
    print("文件image=$image");
    FormData formData = FormData.fromMap({
      "image": image
    });
    asyncRequestNetwork<Map>(
      Method.post,
      url: HttpConstants.UPDATE_USER_PHOTO,
      params: formData,
      isList: false,
      isShow: true,
      onSuccess: (data){
        if(data != null) {
          String url = data["URL"];
          view.provider.setUserPhoto(url);
          UserInfoBean userInfoBean = Provider.of<UserProvider>(view.context).user;
          print("provider==="+userInfoBean.phone);
          userInfoBean.photoURI = url;
          Provider.of<UserProvider>(view.context).saveUser(userInfoBean);

封装dio请求dio_utils.dart

class DioUtils {
  static final DioUtils _singleton = DioUtils._internal();
  static DioUtils get instance => DioUtils();
  factory DioUtils() {
    return _singleton;
  static Dio _dio;
  Dio getDio(){
    return _dio;
  DioUtils._internal(){
    var options = BaseOptions(
      connectTimeout: 15000,
      receiveTimeout: 15000,
      responseType: ResponseType.plain,
      validateStatus: (status){
        // 不使用http状态码判断状态,使用AdapterInterceptor来处理(适用于标准REST风格)
        return true;
    _dio = Dio(options);
    /// Fiddler抓包代理配置 https://www.jianshu.com/p/d831b1f7c45b
//    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
//        (HttpClient client) {
//      client.findProxy = (uri) {
//        //proxy all request to localhost:8888
//        return "PROXY 10.41.0.132:8888";
//      };
//      client.badCertificateCallback =
//          (X509Certificate cert, String host, int port) => true;
//    };
    /// 统一添加身份验证请求头
    _dio.interceptors.add(AuthInterceptor());
    /// 打印Log(生产模式去除)
    if (!Constant.inProduction){
      _dio.interceptors.add(LoggingInterceptor());
    /// 适配数据(根据自己的数据结构,可自行选择添加)
    _dio.interceptors.add(AdapterInterceptor());
    ///cookie
    _dio.interceptors.add(CookieManager(
        PersistCookieJar(dir:StorageManager.cookieDirectory.path+"/.cookies/")));
  // 数据返回格式统一,统一处理异常
  Future<BaseEntity<T>> _request<T>(String method, String url, {
    dynamic data, Map<String, dynamic> queryParameters,
    CancelToken cancelToken, Options options
  }) async {
    var response = await _dio.request(url, data: data, queryParameters: queryParameters, options: _checkOptions(method, options), cancelToken: cancelToken);
    try {
      /// 集成测试无法使用 isolate
      Map<String, dynamic> _map = Constant.isTest ? parseData(response.data.toString()) : await compute(parseData, response.data.toString());
      return BaseEntity.fromJson(_map);
    }catch(e){
      print(e);
      return BaseEntity(ExceptionHandle.parse_error, "网络错误,请重试!", null);
  Options _checkOptions(method, options) {
    if (options == null) {
      options = new Options();
    options.method = method;
    return options;
  Future requestNetwork<T>(Method method, String url, {
        Function(T t) onSuccess, 
        Function(List<T> list) onSuccessList, 
        Function(int code, String msg) onError,
        dynamic params, Map<String, dynamic> queryParameters, 
        CancelToken cancelToken, Options options, bool isList : false
  }) async {
    String m = _getRequestMethod(method);
    return await _request<T>(m, url,
        data: params,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken).then((BaseEntity<T> result){
      if (result.code == 200){
        if (isList){
          if (onSuccessList != null){
            onSuccessList(result.listData);
        }else{
          if (onSuccess != null){
            onSuccess(result.data);
      }else{
        _onError(result.code, result.message, onError);
    }, onError: (e, _){
      _cancelLogPrint(e, url);
      NetError error = ExceptionHandle.handleException(e);
      _onError(error.code, error.msg, onError);
    });
  /// 统一处理(onSuccess返回T对象,onSuccessList返回List<T>)
  asyncRequestNetwork<T>(Method method, String url, {
    Function(T t) onSuccess, 
    Function(List<T> list) onSuccessList, 
    Function(int code, String msg) onError,
    dynamic params, Map<String, dynamic> queryParameters, 
    CancelToken cancelToken, Options options, bool isList : false
  }){
    String m = _getRequestMethod(method);
    Observable.fromFuture(_request<T>(m, url, data: params, queryParameters: queryParameters, options: options, cancelToken: cancelToken))
        .asBroadcastStream()
        .listen((result){
          //print("@@@@@@@@"+result.message.toString());
      if (result.code == 200){
        if (isList){
          if (onSuccessList != null){
            onSuccessList(result.listData);
        }else{
          if (onSuccess != null){
            //print("onSuccess=${result.data}");
            onSuccess(result.data);
      }else{
        _onError(result.code, result.message, onError);
    }, onError: (e){
      _cancelLogPrint(e, url);
      NetError error = ExceptionHandle.handleException(e);
      _onError(error.code, error.msg, onError);
    });
  _cancelLogPrint(dynamic e, String url){
    if (e is DioError && CancelToken.isCancel(e)){
      Log.i("取消请求接口: $url");
  _onError(int code, String msg, Function(int code, String mag) onError){
    if (code == null){
      code = ExceptionHandle.unknown_error;
      msg = "未知异常";
    Log.e("接口请求异常: code: $code, msg: $msg");
    if (onError != null) {
      onError(code, msg);
  String _getRequestMethod(Method method){
    String m;
    switch(method){
      case Method.get:
        m = "GET";
        break;
      case Method.post:
        m = "POST";
        break;
      case Method.put:
        m = "PUT";
        break;
      case Method.patch:
        m = "PATCH";
        break;
      case Method.delete:
        m = "DELETE";
        break;
      case Method.head:
        m = "HEAD";
        break;
    return m;
Map<String, dynamic> parseData(String data){
  return json.decode(data);
enum Method {
  get,
  post,
  put,
  patch,
  delete,

dio请求拦截器intercept.dart

class AuthInterceptor extends Interceptor{
  @override
  onRequest(RequestOptions options) {
    String dn = DeviceManager.getDeviceName();
    String dt = DeviceManager.getDeviceType();
    String av = DeviceManager.getAppVersion();
    String os = DeviceManager.getDeviceVersion();
    String rdi = "DT="+dt+";"+"DN="+dn+";"+"AV="+av+";"+"OS="+os;
    options.contentType = "application/x-www-form-urlencoded";
    options.headers = {"RDI":rdi};
    return super.onRequest(options);
class LoggingInterceptor extends Interceptor{
  DateTime startTime;
  DateTime endTime;
  @override
  onRequest(RequestOptions options) {
    startTime = DateTime.now();
    Log.d("----------Start----------");
    Log.i("RequestUrl: " + options.baseUrl + options.path);
    Log.d("queryParameters: ${Transformer.urlEncodeMap(options.queryParameters)}");
    Log.d("RequestMethod: " + options.method);
    Log.d("RequestHeaders:" + options.headers.toString());
    //Log.d("RequestContentType: ${options.contentType}");
    //Log.d("RequestData: ${options.data.toString()}");
    return super.onRequest(options);
  @override
  onResponse(Response response) {
    endTime = DateTime.now();
    int duration = endTime.difference(startTime).inMilliseconds;
    if (response.statusCode == ExceptionHandle.success){
      Log.d("ResponseCode: ${response.statusCode}");
    }else {
      Log.e("ResponseCode: ${response.statusCode}");
    // 输出结果
    Log.json(response.data.toString());
    Log.d("----------End: $duration 毫秒----------");
    return super.onResponse(response);
  @override
  onError(DioError err) {
    Log.d("----------Error-----------");
    return super.onError(err);
class AdapterInterceptor extends Interceptor{
  static const String msg = "msg";
  static const String slash = "\"";
  static const String message = "message";
  static const String defaultText = "\"无返回信息\"";
  static const String notFound = "未找到查询信息";
  static const String failureFormat = "{\"code\":%d,\"message\":\"%s\"}";
  static const String successFormat = "{\"code\":0,\"data\":%s,\"message\":\"\"}";
  @override
  onResponse(Response response) {
    return super.onResponse(response);
  @override
  onError(DioError err) {
    if (err.response != null){
      adapterData(err.response);
    return super.onError(err);
  Response adapterData(Response response){
    String result;
    String content = response.data == null ? "" : response.data.toString();
    /// 成功时,直接格式化返回
    if (response.statusCode == ExceptionHandle.success || response.statusCode == ExceptionHandle.success_not_content){
      if (content == null || content.isEmpty){
        content = defaultText;
      result = sprintf(successFormat, [content]);
      response.statusCode = ExceptionHandle.success;
    }else{
      if (response.statusCode == ExceptionHandle.not_found){
        /// 错误数据格式化后,按照成功数据返回
        result = sprintf(failureFormat, [response.statusCode, notFound]);
        response.statusCode = ExceptionHandle.success;
      }else {
        if (content == null || content.isEmpty){
          // 一般为网络断开等异常
          result = content;
        }else {
          String msg;
          try {
            content = content.replaceAll("\\", "");
            if (slash == content.substring(0, 1)){
              content = content.substring(1, content.length - 1);
            Map<String, dynamic> map = json.decode(content);
            if (map.containsKey(message)){
              msg = map[message];
            }else if(map.containsKey(msg)){
              msg = map[msg];
            }else {
              msg = "未知异常";
            result = sprintf(failureFormat, [response.statusCode, msg]);
            // 401 token失效时,单独处理,其他一律为成功
            if (response.statusCode == ExceptionHandle.unauthorized){
              response.statusCode = ExceptionHandle.unauthorized;
            }else {
              response.statusCode = ExceptionHandle.success;
          } catch (e) {
            Log.d("异常信息:$e");
            // 解析异常直接按照返回原数据处理(一般为返回500,503 HTML页面代码)
            result = sprintf(failureFormat, [response.statusCode, "服务器异常(${response.statusCode})"]);
    response.data = result;
    return response;

由于使用了MVP的设计模式,所以进一步封装在了basePresenter中

void asyncRequestNetwork<T>(Method method, {@required String url, bool isShow : true, bool isClose: true, Function(T t) onSuccess, Function(List<T> list) onSuccessList, Function(int code, String msg) onError,
    dynamic params, Map<String, dynamic> queryParameters, CancelToken cancelToken, Options options, bool isList : false}){
    if (isShow) view.showProgress();
    DioUtils.instance.asyncRequestNetwork<T>(method, url,
        params: params,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken?? _cancelToken,
        isList: isList,
        onSuccess: (data){
          if (isClose) view.closeProgress();
          if (onSuccess != null) {
            onSuccess(data);
        onSuccessList: (data){
          if (isClose) view.closeProgress();
          if (onSuccessList != null) {
            onSuccessList(data);
        onError: (code, msg){
          _onError(code, msg, onError);
   _onError(int code, String msg, Function(int code, String msg) onError){
    /// 异常时直接关闭加载圈,不受isClose影响
    view.closeProgress();
    if (code != ExceptionHandle.cancel_error){
      view.showToast(msg);
    /// 页面如果dispose,则不回调onError
    if (onError != null && view.getContext() != null) {
      onError(code, msg);
                    上传文件具体实现如下:void uploadPhoto(File file) async{    String path = file.path;    var name = path.substring(path.lastIndexOf("/") + 1, path.length);    String imageType = name.substring(name.lastIndexO...
				
上一篇讲解了截图和保存图片的方法,这篇博客将对图片base64上传sentry,以及动态获取本地app版本号的方法做讲解。 首先来看下代码部分: 对图片base64处理: Future<String> base64Img() async { try { RenderRepaintBoundary boundary = imgKey.currentContext.findRenderObject(); var image = await boundary.toIm
原生app转换成flutter for web后,发现用image库的decodeimage函数在网页上慢到无法忍受,通过查资料了解用网页的canvas直接加载处理。以下是图片选择/拍照,压缩后转到base64的主要代码,希望能帮到有同样困惑的小伙伴们。 import 'package:image_picker/image_picker.dart'; import 'package:image_picker_for_web/image_picker_for_web.dart'; ///摄像...
上传文件数组的时候,后端总是收不到文件数组。检查本地数据发现没有问题。 问题出现在FormData对文件的处理上,如果直接用FormData.map处理数据类似下面这样,对于单张/单个文件的上传是没有影响的。 Map<String, dynamic> formMap = { 'nickname': nickname, 'motto': motto, formMap['avatar'] = Mul 图片有多条,法人信息,授权信息,等 有必填图片,有非必填图片,文件大小限制为2MB. 必填的图片上传,则本次均不录入数据库. 图片要求在一次确认后,审核人员才能显示. 需要进行相关得记录,用于后续查看. 为了满足以上实现,有两种实现方式 单个批量接口 所有的文件通过一个接口批量上传. 文件单条上传+确认上传. 文件大小在2MB,如果批
之前发过一篇上传多张图片的博客,那个是自己实现的,但是是一张一张上传最后页面上显示出来所有上传图片,这篇文章介绍一下一次性选择多张图片上传 首先引入依赖:multi_image_picker,这个插件可以选择多张图片,插件地址https://pub.flutter-io.cn/packages/multi_image_picker 然后画个小页面 import 'package:flutter/material.dart'; import 'dart:io'; //ByteData这里需要引入d
借助Dio,将其封装为一个工具类(dio_utils.dart) 封装请求服务(service_api.dart) 根据请求参数封装 请求model (RequireModel) 根据返回数据类型封装返回Entitiy(list_entity.dart。因为返回的data中是一个list,如果是一个string,直接在entiy中解析好,上层进行调用即可) data中的单个对象进行封装(PersionEntity),方便解析 总的来说封装了这么多就是为了后期使用方便。
如果您在 Flutter 项目的 `android` 目录中找不到 `include_flutter.groovy` 文件,可以尝试以下解决方案: 1. 确保您的 Flutter 版本足够新。`include_flutter.groovy` 文件是在 Flutter 1.12 版本之后添加的,如果您使用的是更早的版本,可能会导致找不到这个文件。 2. 如果您在使用 Flutter Module(即在原生 Android 项目中使用 Flutter),请确保您已在 Android 项目的根目录下运行过 `flutter create .` 命令。这会在 Android 项目中创建 `flutter_module` 目录,并在其中生成 `include_flutter.groovy` 文件。 3. 如果您在使用 Flutter Plugin(即在 Flutter 项目中使用原生 Android 插件),请确保您已在 Flutter 项目的根目录下运行过 `flutter create .` 命令。这会在 Flutter 项目中创建 `android` 目录,并在其中生成 `include_flutter.groovy` 文件。 如果以上解决方案都无法帮助您解决问题,可能是您的 Flutter 安装有问题。建议您尝试重新安装 Flutter,或者提交 issue 到 Flutter 官方问题跟踪系统中寻求帮助。
Android webview解决JS报错chromium: [INFO:CONSOLE(1)] "Uncaught TypeError: Cannot read property 'getItem' 20725 解决在Android P上的提醒弹窗 (Detected problems with API compatibility(visit g.co/dev/appcompat for more info) 15953 Android解决gradle "Error:Cause: unable to find valid certification path to requested target" 14682
Android webview解决JS报错chromium: [INFO:CONSOLE(1)] "Uncaught TypeError: Cannot read property 'getItem' np解决了 Flutter动态权限申请教程 PermissionGroup为啥识别不了 Flutter动态权限申请教程 代码没给全吗? Flutter中 输入控件TextField设置内容并保持光标在末尾 你知道了吗 Android完美解决多次点击Toast一直提示不消失问题 长脸先生: 哈哈 对呀 但是怎么解决呢