Flutter api

single instance

import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:dio/dio.dart';

class TimerLoading {
  static const int durationTime = 2;
  static Timer _timer;
  static TimerLoading _instance = TimerLoading._internal();
  factory TimerLoading() => _instance;

  TimerLoading._internal() {
    // 初始化 变量 比如 timer
  }

  static TimerLoading getInstance() {
    return TimerLoading();
  }

  showToast({String text}) {
    EasyLoading.showToast(text);
    Future.delayed(new Duration(seconds: durationTime),() {
      //EasyLoading.dismiss();
    });
  }
}

  factory Manager() =>_getInstance();
  static Manager get instance => _getInstance();
  static Manager _instance;
  Manager._internal() {

  }
  static Manager _getInstance() {
    if (_instance == null) {
      _instance = new Manager._internal();
    }
    return _instance;
  }

Manager manager = new Manager();
Manager manager2 = Manager.instance;

判断泛型数据类型

/// Response body. may have been transformed, please refer to [ResponseType].
  T data;
enum ResponseType {
  /// Transform the response data to JSON object only when the
  /// content-type of response is "application/json" .
  json,

  /// Get the response stream without any transformation. The
  /// Response data will be a `ResponseBody` instance.
  ///
  ///    Response<ResponseBody> rs = await Dio().get<ResponseBody>(
  ///      url,
  ///      options: Options(
  ///        responseType: ResponseType.stream,
  ///      ),
  ///    );
  stream,

  /// Transform the response data to a String encoded with UTF8.
  plain,

  /// Get original bytes, the type of [Response.data] will be List<int>
  bytes
}


* response.data.runtimeType

json string to map

import 'dart:convert' as convert;
Map<String, dynamic> jsonData = convert.jsonDecode(response.data);
// 其中 response.data是json string

登陆:遇到token 失效 我们需要自己实现拦截器,在拦截器去刷新token,并重新进行网络请求

_dio.interceptors.add(DioLogInterceptor());
class DioLogInterceptor extends Interceptor {
      @override
  Future onError(DioError err) async {
      // 获取本次网络请求的请求参数
          params: err.response.request.queryParameters,
          path: err.response.request.uri.path,
          method: err.response.request.method
  }
}

如果普通网络请求使用 Dio 单例,则需要使用dio的lock锁住单例,另外创建新的Dio对象去刷新token和retry.结束后再打开锁。注意异常后也要打开锁。

Dio put请求 以及参数注意事项

必须严格按照 Map<String, dynamic>类型 ,如果你是用Map()则会有意想不到的问题。

Future<bool> refreshToken() async {
    Dio newDio = Dio();
    newDio.options.baseUrl = Address.BASE_URL;
    newDio.options.headers["token"] = Global.profile.getAuthorization;
    
    Map refreshParams = Map<String, dynamic>();
    refreshParams["refreshToken"] = Global.profile.refreshToken;
    refreshParams["token"] = Global.profile.getAuthorization;
    Response refreshTokenResponse;
    try {
      refreshTokenResponse = await newDio.request(Address.REFRESH_TOKEN,
          queryParameters: refreshParams, options: Options(method: "PUT"));
    } on DioError catch (e) {
      
    }
    return true;
  }

json_serializable

flutter packages pub run build_runner build --delete-conflicting-outputs //删除并重新创建.g.dart文件
flutter packages pub run build_runner build

https://caijinglong.github.io/json2dart/index.html

convert.jsonDecode的崩溃是悄无声息的 使用之前务必做类型判断,因为你永远不知道服务端给你的response是Map还是String!!! 看心情

convert.jsonDecode

Map<String, dynamic> jsonData;
      if (response is Map) {
        jsonData = response.data;
      } else {
        jsonData = convert.jsonDecode(response.data);
      }

更好用的json解析: flutter_json_format插件。选中文件 右键Generate -> flutter json format 自动生成。上面的那个json_serializable序列化方式总是有问题,不靠谱。

就像前面的String转Map 明明发生了异常 但是为何悄无声息? 因为Dart的异常会让接下来的函数的代码不执行 但是并不会影响整个程序的执行。
请求地址 直接拷贝到浏览器会有好效果

按钮

_buildBottomButton({int type, String title}) {
    return Container(
      child: Padding(
        padding: type == 1 ? EdgeInsets.only(left: dimens.dimens_16,right: 8) : EdgeInsets.only(left: 8,right: dimens.dimens_16),
        child: ButtonTheme(
//        minWidth: 80,
//        height: 44,
          child: FlatButton(
            shape: new RoundedRectangleBorder(
                borderRadius: new BorderRadius.circular(8.0),
                side: BorderSide(
                    color: type == 1 ? Colors.black12 : Theme
                        .of(context)
                        .accentColor)),
            color: type == 1 ? Colors.black12 : Theme
                .of(context)
                .accentColor,
            textColor: Colors.white,
            padding: EdgeInsets.all(8.0),
            onPressed: () {},
            child: Text(
              title,
              style: TextStyle(
                color: type == 1 ? Theme
                    .of(context)
                    .accentColor : Colors.white,
                fontSize: 17,
              ),
            ),
          ),
        ),
      ),
    );
  }

HTTPS私有证书校验:Dio Image WebView三个场景都会遇到。外网则不会有这个问题。内网则会遇到。

import 'package:dio/adapter.dart';
httpsConfigure() {
    /// 配置证书
    /*
    ByteData data =
        await rootBundle.load('assets/certificate/xxx.crt');
    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
        (client) {
      SecurityContext securityContext = new SecurityContext();
      securityContext.setTrustedCertificatesBytes(data.buffer.asUint8List());
      client = HttpClient(context: securityContext);
      return client;
    };
     */

    /// 信任所有证书
    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
        (client) {
      client.badCertificateCallback =
          (X509Certificate cert, String host, int port) {
        return true;
      };
    };
  }

webview 可以使用插件flutter_webview_plugin 参数有关于忽略私有证书的

上面是Dio的配置,如果是NetworkImage 加载一张https的图片,我们需要去看NetworkImage的源码 里面有类似介绍,但是在正式环境不可用。stackoverflow的大神的解决方法是把源码拷贝出来自己重新封装后使用!

Vague Search

// value 是search string
// widget.contacts是数据源
onSubmitted: (value) {
          var result = widget.contacts
              .where((element) => vagueSearch(element.name, value));
},

bool vagueSearch(String name, String searchString) {
    return name.indexOf(searchString) > -1 ? true : false;
  }

打包 命令行配置环境

flutter build ios --dart-define=SERVER_ENV=sit,API_HOST=https://xxx...
sit是对应SIT环境,可配置其他环境
API_HOST,SERVER_ENV参照文件
配合ENVConfig

GlobalKey的使用:性能优化

  1. 利用GolbalKey获取到对应Widget的State对象
  • 创建继承自StatefulWidget的自定义Widget:MyWidget
  • 构造函数带上Key 参数
  • 创建MyWidget的实例的时候,先创建一个GlobalKey对象(假设MyWidget对应的State类是MyWidgetState),,然后将key实例传入构造函数
  • 在外界就可以获取MyWidget对应的State对象,也就是MyWidgetState实例。key.currentState.
  • 可以在外部愉快的调用MyWidgetState实例的函数了,比如我们可以局部刷新MyWidget,来避免多余的性能开销。我们也借此实现无context的push操作。
  • 新手往往会随意的调用setState,有时候明明只需要刷新一个小的Widget却刷新了整颗Widget。如果是频繁刷新,那么性能开销极其庞大。因此我们可以将这个小的Wideget单独抽取出来,利用上面讲的思路,只刷新必要的部分。
  • 另一种局部刷新的思路:我们构建一个无限ListView,Item上一个按钮点击后,要改变这个Item的按钮状态(比如点赞),这个时候我们刷新整个ListView是不合适的,怎么做到只刷新当前Item? 甚至是只刷新当前按钮?

其实很简单,只刷新当前Item,我们只需要将Item单独抽取成一个StatefulWidget,让每个Item都有自己的State类,构造函数传入model对象,我们每次点击按钮,则仅仅改变一个model的某个属性值,一个model对应一个StatefulWidget,因此可以直接调用setState(){model.xx=xx}来只刷新item。
而站在item的角度,仅刷新按钮,我们可以采用前面介绍的GlobalKey来实现。

  • 后面有时间专门写一个demo实现,这里是思路。

代码整理小技巧

属于本函数内部的重复代码,没必要在函数外新开一个函数进行封装,可以在函数内部做封装,非常便于阅读,避免了业务散落一地的感觉,参照下面代码的refensh函数。

Future<bool> _checkToken() async {
    Function refresh = () async {
      Log.v("need update token");
      bool refreshTokenResult = await _refreshToken();
      Log.v("update token result : $refreshTokenResult");
      return refreshTokenResult;
    };

    /// check token
    String token = Global.profile.getAuthorization;
    if (null != token) {
      var jwtRes;
      try {
        jwtRes = _parseJwt(token);
      } catch (e) {
        Log.v("parseJwt failed :$e");
        return refresh();
      }
      if (null != jwtRes && null != jwtRes["exp"]) {
        int time = jwtRes["exp"];
        int now = TimeUtil.currentTimeMillis();
        if (now / 1000 > time) {
          return refresh();
        }
      }
    }
    return false;
  }

上拉底部widget 只需将datasource.length+1

Widget _itemBuilder(BuildContext buildContext, List<dynamic> list, int index) {
  if (list.length > index) {
    return CompanyListItem(
      data: list[index],
    );
  } else {
    return Center(
      child: Padding(
        padding: EdgeInsets.all(10.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Text(
              'loading...     ',
              style: TextStyle(fontSize: 16.0),
            ),
            CircularProgressIndicator()
          ],
        ),
      ),
    );
  }
}

正则在线测试

posted @ 2020-09-19 23:16  yxg889  阅读(298)  评论(0编辑  收藏  举报