一、本地文件操作:

1.File文件操作:

说明:
临时目录:getTemporaryDirectory()
文档目录:getApplicationDocumentsDirectory()
外部存储目录(iOS不支持):getExternalStorageDirectory()

(1)添加PathProvider依赖,在pubspec.yaml中:

dependencies:
  path_provider: ^2.0.11

(2)文件读写:

void editFile() async {
  String dir = (await getApplicationDocumentsDirectory()).path;  // 获取文档目录
  File file = await File('$dir/counter.txt');  //获得文件对象
  String content = await file.readAsString();   // 读取文件内容
  content = content + "新内容";
  file.writeAsString(content);  //修改文件内容
}

2.Shared Preferences数据存储/获取:

说明:

存储位置:.../包名/shared_prefs/xxx.xml

(1)添加shared_preferences插件依赖,在pubspec.yaml中:

dependencies:
  shared_preferences: ^2.0.15  #preferences存储库

(2)shared_preferences数据增删查工具类:

class SharedPreferenceUtil {//shared_preferences数据增删查工具类
  late SharedPreferences _sharedPrefs; //Shared Preference操作类
  void init() async {
    _sharedPrefs = await SharedPreferences.getInstance();
  }
  void setInt(String key, int value) {
    _sharedPrefs.setInt(key, value);
  }
  int? getInt(String key) {
    return _sharedPrefs.getInt(key);
  }
  void setBool(String key, bool value) {
    _sharedPrefs.setBool(key, value);
  }
  bool? getBool(String key) {
    return _sharedPrefs.getBool(key);
  }
  void setDouble(String key, double value) {
    _sharedPrefs.setDouble(key, value);
  }
  double? getDouble(String key) {
    return _sharedPrefs.getDouble(key);
  }
  void setString(String key, String value) {
    _sharedPrefs.setString(key, value);
  }
  String? getString(String key) {
    return _sharedPrefs.getString(key);
  }
  void setStringList(String key, List<String> value) {
    _sharedPrefs.setStringList(key, value);
  }
  List<String>? getStringList(String key) {
    return _sharedPrefs.getStringList(key);
  }
  void remove(String key) {
    _sharedPrefs.remove(key);
  }
}

(3)使用工具类存储/读取数据:

...
SharedPreferenceUtil sharedPrefs = SharedPreferenceUtil(); //创建工具类
...
sharedPrefs.init();  //初始化
...
sharedPrefs.setString("key", "value");  //存储数据
String? value =  sharedPrefs.getString("key");  //读取数据
...

二、HTTP网络请求:

1.使用dio库实现:

(1)添加dio库依赖,在pubspec.yaml中:

dependencies:
  dio: ^4.0.6  #dio HTTP网络库

(2)使用dio实现网络请求:

class DioNetUtil {
  Dio dio = Dio();
  Map<String, CancelToken?> requestMap = {};  //取消标志,用于取消请求
  //添加代理主机,通过代理主机转发请求
  void _addProxy(HttpClient httpClient){
    httpClient.findProxy = (uri) {
      return "PROXY IP:端口";  //代理主机
      //return "DIRECT";   //不需要代理主机时返回"DIRECT"
    };
  }
  //添加证书校验
  void _verifyCer(HttpClient httpClient, String cerContent) {
    httpClient.badCertificateCallback = (X509Certificate cert, String host, int port) {
      if(cert.pem != cerContent) return false;  //证书校验失败
      return true; //证书校验通过,放行
    };
  }
  //外部调用,添加证书校验
  void initCerVerify(String cerContent){
    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
      //添加代理主机,通过代理主机转发请求
      //_addProxy(client);
      //添加证书校验
      _verifyCer(client, cerContent);
    };
  }
  //get请求
  Future<void> doGet(String url) async {
    requestMap[url] = CancelToken(); //取消标志,用于取消请求
    Response response = await dio.get(url, cancelToken: requestMap[url]);
    String json = response.data.toString();
    //...解析json
  }
  //post请求-请求参数为json
  Future<void> doPostByJson(String url, {Map<String, dynamic>? params}) async {
    requestMap[url] = CancelToken(); //取消标志,用于取消请求
    //1.发起post请求
    Response response;
    if (params != null) {//有请求参数时
      response = await dio.post(url, data: params, cancelToken: requestMap[url]);
    } else { //无请求参数时
      response = await dio.post(url, cancelToken: requestMap[url]);
    }
    //2.获取返回数据并解析
    String json = response.data.toString();
    //...解析json
  }
  //post请求-请求参数为Form表单,contentType为multipart/form-data
  Future<void> doPostByForm(String url, {Map<String, dynamic>? params}) async {
    requestMap[url] = CancelToken(); //取消标志,用于取消请求
    //1.发起post请求
    Response response;
    if (params != null) {//有请求参数时,封装请求参数到FormData
      FormData formData = FormData.fromMap(params);
      response = await dio.post(url, data: formData, cancelToken: requestMap[url]);
    } else {
      response = await dio.post(url, cancelToken: requestMap[url]);
    }
    //2.获取返回数据并解析
    String json = response.data.toString();
    //...解析json
  }
  //文件上传
  Future<void> uploadFile(String url, String filePath, {Map<String, dynamic>? params}) async {
    requestMap[url] = CancelToken(); //取消标志,用于取消请求
    //1.封装文件与请求参数到FormData
    Map<String, dynamic> params1 = {
      "file": File(filePath)
      //"files": [...]  //此字段支持多个文件上传
    };
    if (params != null) {
      params.forEach((key, value) { //遍历Map集合
        params1[key] = value; //添加参数到Map集合中
      });
    }
    FormData formData = FormData.fromMap(params1);
    //2.发起post请求,上传文件
    Response response = await dio.post(url, data: formData, cancelToken: requestMap[url], onSendProgress: (int progress, int total) {
      print("文件总长度: $total, 当前上传进度: $progress");
      if(progress == total) {
        print("文件上传完成");
        //...处理上传完后的逻辑
      }
    });
    //3.获取返回数据并解析
    String json = response.data.toString();
    //...解析json
  }
  //文件下载
  Future<void> downloadFile(String url, String saveFilePath) async {
    requestMap[url] = CancelToken(); //取消标志,用于取消请求
    try {
      //发起post请求,下载文件
      Response response = await dio.download(url, saveFilePath, cancelToken: requestMap[url], onReceiveProgress: (int count, int total) {
        print("文件总长度: $total, 当前下载进度: $count");
        if(count == total) {
          print("文件下载完成");
          //...处理下载完后的逻辑
        }
      });
    } on DioError catch (e) {
      if (e.type == DioErrorType.connectTimeout) {
        print("连接超时");
      } else if (e.type == DioErrorType.sendTimeout) {
        print("请求超时");
      } else if (e.type == DioErrorType.receiveTimeout) {
        print("接收超时");
      } else if (e.type == DioErrorType.response) {
        print("响应异常");
      } else {
        print("下载失败");
      }
    }
  }
  //取消请求
  Future<void> cancel(String url) async {
    if (requestMap[url] != null && !requestMap[url]!.isCancelled) {
      requestMap[url]!.cancel();
      requestMap[url] = null;
    }
  }
}

2.使用HttpClient实现:

class HttpClientUtil {
  late HttpClient httpClient;
  //初始化HttpClient
  HttpClient _createHttpClient(Uri uri) {
    if (httpClient != null) return httpClient;
    httpClient = HttpClient(context: _getSecurityContext()); //创建HttpClient时添加证书
    httpClient.idleTimeout = Duration(milliseconds: 10000); //对应请求头keep-alive
    httpClient.connectionTimeout = Duration(milliseconds: 10000); //连接超时时间
    httpClient.maxConnectionsPerHost = 5; //同一个url,允许同时连接的最大数
    httpClient.autoUncompress = true; //对应请求头Content-Encoding,true时使用gzip压缩
    //添加Basic认证或Digest认证
    //_addAuth(uri);
    //添加代理主机,通过代理主机转发请求
    //_addProxy(uri);
    return httpClient;
  }
  //添加Basic认证或Digest认证
  void _addAuth(Uri uri){
    httpClient.addCredentials(uri, "用户角色分组(后台添加)", HttpClientBasicCredentials("用户名", "密码"));   //添加Basic认证(Base64编码),对应请求头Authorization,值为:Basic xxx
    httpClient.addCredentials(uri, "用户角色分组(后台添加)", HttpClientDigestCredentials("用户名", "密码"));   //添加Digest认证(哈希运算),对应请求头Authorization,值为:Basic xxx
  }
  //添加代理主机,通过代理主机转发请求
  void _addProxy(){
    httpClient.findProxy = (uri) {
     return "PROXY IP:端口";  //代理主机
   //return "DIRECT";   //不需要代理主机时返回"DIRECT"
    };
    //代理主机添加Basic认证或Digest认证
    httpClient.addProxyCredentials("代理主机IP", 80, "用户角色分组", HttpClientBasicCredentials("用户名", "密码"));
    httpClient.addProxyCredentials("代理主机IP", 80, "用户角色分组", HttpClientDigestCredentials("用户名", "密码"));
  }
  //添加证书校验
  SecurityContext _getSecurityContext() {
    //1.1.添加证书,请求时会自动校验
    SecurityContext sc = SecurityContext();
    //sc.setTrustedCertificates("证书本地路径"); //支持PEM格式(无需密码)、PKCS12格式(需要证书密码)
    //1.2.上面添加的证书验证失败时,触发以下监听
    httpClient.badCertificateCallback = (X509Certificate cert, String host, int port) {
      return true; //不校验证书,全部放行
    };
    return sc;
  }
  //添加头字段
  void _addHeader(HttpClientRequest request) {
    request.headers.add("Connection", "keep-alive");
    request.headers.add("Content-Type", "application/json");
  }
  //执行网络请求
  Future<String?> doRequest(String url) async {
    Uri uri = Uri.parse(url);
    //1.创建一个HttpClient
    HttpClient httpClient = _createHttpClient(uri);
    try {
      //2.0.打开Http连接
      HttpClientRequest request = await httpClient.getUrl(uri);
      //2.1.POST方式打开Http连接
      //HttpClientRequest request = await httpClient.postUrl(uri);
      //2.2.GET方式打开Http连接
      //HttpClientRequest request = await httpClient.get("www.baidu.com", 80, "/path?key=value");
      //3.添加请求头
      _addHeader(request);
      //4.等待连接服务器(会将请求信息发送给服务器)
      HttpClientResponse response = await request.close();
      //5.读取返回的json
      String json = await response.transform(utf8.decoder).join();
      print("json: " + json);
      return json;
    } catch (e) {
      print("e: ${e.toString()}");
      return null;
    } finally {
      //6.关闭client
      httpClient.close();
    }
  }
}

三、TCP/IP长连接:

1.使用WebSocket实现长连接:

(1)添加WebSocket依赖,在pubspec.yaml中:

dependencies:
  web_socket_channel: ^2.0.0 #WebSocket库

(2)使用WebSocket实现长连接:

class WebSocketUtil {
  late IOWebSocketChannel channel;
  //1.创建websocket连接,并监听消息
  Future<void> connect(String ws, Map<String, dynamic>? headers) async {
    channel = IOWebSocketChannel.connect(ws, headers: headers);  //创建websocket连接
    channel.stream.listen((msg) {  //监听消息
      String json = msg;
      //...解析json
    });
  }
  //2.发送消息
  Future<void> sendMsg(String msg) async {
    channel.sink.add(msg); //发送消息
  }
  //3.关闭连接
  Future<void> close() async {
    channel.sink.close(); //关闭连接
  }
}

2.使用Socket实现长连接:

class SocketUtil {
  late var socket;
  //1.创建socket连接
  Future<void> connect(String host, int port) async {
    socket = Socket.connect(host, port); //创建socket连接
  }
  //2.读取消息
  Future<String> readMsg() {
    Future<String> msg = utf8.decoder.bind(socket).join(); //读取消息内容
    return msg;
  }
  //3.发送消息
  Future<void> sendMsg(String msg) async {
    socket.writeln(msg);
    socket.flush(); //发送消息
  }
  //4.关闭连接
  Future<void> close() async {
    socket.close(); //关闭连接
  }
}