# 开源资料

返回:Flutter

# UI

# json_serializable

# path_provider

TIP

不同的平台对应的文件系统是不同的,比如文件路径,因此 Flutter 中获取文件路径需要原生支持,原生端通过 MethodChannel 传递文件路径到 Flutter,如果没有特殊的需求,推荐大家使用 Google 官方维护的插件 path_provider

# getTemporaryDirectory

临时目录,适用于下载的缓存文件,此目录随时可以清除,此目录为应用程序私有目录,其他应用程序无法访问此目录。

  • Android 上对应 getCacheDir
  • iOS 上对应 NSCachesDirectory

# getApplicationSupportDirectory

应用程序可以在其中放置应用程序支持文件的目录的路径。将此文件用于您不想向用户公开的文件。 您的应用不应将此目录用于存放用户数据文件。

  • 在 iOS 上,对应 NSApplicationSupportDirectory ,如果此目录不存在,则会自动创建。
  • 在 Android 上,对应 getFilesDir

# getLibraryDirectory

应用程序可以在其中存储持久性文件,备份文件以及对用户不可见的文件的目录路径,例如 storage.sqlite.db。

  • 在 Android 上,此函数抛出[UnsupportedError]异常,没有等效项路径存在。

# getApplicationDocumentsDirectory

应用程序可能在其中放置用户生成的数据或应用程序无法重新创建的数据的目录路径。

  • 在 iOS 上,对应 NSDocumentDirectory API。 如果数据不是用户生成的,考虑使用[getApplicationSupportDirectory]
  • 在 Android 上,对应 getDataDirectory API。 如果要让用户看到数据,请考虑改用[getExternalStorageDirectory]

# getExternalStorageDirectory

应用程序可以访问顶级存储的目录的路径。由于此功能仅在 Android 上可用,因此应在发出此函数调用之前确定当前操作系统。

  • 在 iOS 上,此功能会引发[UnsupportedError]异常,因为无法在应用程序的沙箱外部访问。
  • 在 Android 上,对应 getExternalFilesDir(null)

# getExternalCacheDirectories

存储特定于应用程序的外部缓存数据的目录的路径。 这些路径通常位于外部存储(如单独的分区或 SD 卡)上。 电话可能具有多个可用的存储目录。 由于此功能仅在 Android 上可用,因此应在发出此函数调用之前确定当前操作系统。 在 iOS 上,此功能会抛出 UnsupportedError,因为这是不可能的在应用程序的沙箱外部访问。

  • 在 Android 上,对应 Context.getExternalCacheDirs()或 API Level 低于 19 的 Context.getExternalCacheDir()。

# getExternalStorageDirectories

可以存储应用程序特定数据的目录的路径。 这些路径通常位于外部存储(如单独的分区或 SD 卡)上。

  • 由于此功能仅在 Android 上可用,因此应在发出此函数调用之前确定当前操作系统。
    • 在 Android 上,对应 Context.getExternalFilesDirs(String type)或 API Level 低于 19 的 Context.getExternalFilesDir(String type)。
  • 在 iOS 上,此功能会抛出 UnsupportedError,因为这是不可能的在应用程序的沙箱外部访问。

# getDownloadsDirectory

存储下载文件的目录的路径,这通常仅与台式机操作系统有关。

  • 在 Android 和 iOS 上,此函数将引发[UnsupportedError]异常。

# path_provider 使用

import 'dart:io';

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

///
/// desc:
///

class PathProviderDemo extends StatefulWidget {
  
  _PathProviderDemoState createState() => _PathProviderDemoState();
}

class _PathProviderDemoState extends State<PathProviderDemo> {
  Future<Directory> _tempDirectory;
  Future<Directory> _appSupportDirectory;
  Future<Directory> _appLibraryDirectory;
  Future<Directory> _appDocumentsDirectory;
  Future<Directory> _externalStorageDirectory;
  Future<List<Directory>> _externalStorageDirectories;
  Future<List<Directory>> _externalCacheDirectories;
  Future<Directory> _downloadDirectory;

  
  void initState() {
    super.initState();
    setState(() {
      _tempDirectory = getTemporaryDirectory();
      _appSupportDirectory = getApplicationSupportDirectory();
      _appLibraryDirectory = getLibraryDirectory();
      _appDocumentsDirectory = getApplicationDocumentsDirectory();
      _externalStorageDirectory = getExternalStorageDirectory();
      _externalCacheDirectories = getExternalCacheDirectories();
      _externalStorageDirectories = getExternalStorageDirectories();
      _downloadDirectory = getDownloadsDirectory();
    });
  }

  Widget _buildDirectory(
      BuildContext context, AsyncSnapshot<Directory> snapshot) {
    Text text = const Text('');
    if (snapshot.connectionState == ConnectionState.done) {
      if (snapshot.hasError) {
        text = Text('Error: ${snapshot.error}');
      } else if (snapshot.hasData) {
        text = Text('path: ${snapshot.data.path}');
      } else {
        text = const Text('path unavailable');
      }
    }
    return Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: text);
  }

  Widget _buildDirectories(
      BuildContext context, AsyncSnapshot<List<Directory>> snapshot) {
    Text text = const Text('');
    if (snapshot.connectionState == ConnectionState.done) {
      if (snapshot.hasError) {
        text = Text('Error: ${snapshot.error}');
      } else if (snapshot.hasData) {
        final String combined =
            snapshot.data.map((Directory d) => d.path).join(', ');
        text = Text('paths: $combined');
      } else {
        text = const Text('path unavailable');
      }
    }
    return Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16), child: text);
  }

  Widget _buildItem(String title, Future<Directory> future) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: Text(title),
        ),
        FutureBuilder<Directory>(future: future, builder: _buildDirectory),
      ],
    );
  }

  Widget _buildItem1(String title, Future<List<Directory>> future) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: Text(title),
        ),
        FutureBuilder<List<Directory>>(
            future: future,
            builder: _buildDirectories),
      ],
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: ListView(
          itemExtent: 120,
          children: <Widget>[
            _buildItem('getTemporaryDirectory', _tempDirectory),
            _buildItem('getApplicationSupportDirectory', _appSupportDirectory),
            _buildItem('getLibraryDirectory', _appLibraryDirectory),
            _buildItem(
                'getApplicationDocumentsDirectory', _appDocumentsDirectory),
            _buildItem(
                'getExternalStorageDirectory', _externalStorageDirectory),
            _buildItem('getDownloadsDirectory', _downloadDirectory),

            _buildItem1('getExternalStorageDirectories',_externalStorageDirectories),
            _buildItem1('getExternalCacheDirectories',_externalCacheDirectories),

          ],
        ),
      ),
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

# 文件夹创建

_createDir() async {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = '${documentsDirectory.path}${Platform.pathSeparator}dirName';
    var dir = Directory(path);
    var exist = dir.existsSync();
    if (exist) {
      print('当前文件夹已经存在');
    } else {
      var result = await dir.create();
      print('$result');
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12
  • Platform.pathSeparator 表示路径分隔符,对于 Android 和 iOS 来说表示‘/’。
  • create 中有一个可选参数 recursive ,默认值为 false,false 表示只能创建最后一级文件夹,如果创建 “dir1/dir2” 这种嵌套文件夹,recursive为 false 时将抛出异常,设置为 true 可以创建嵌套文件夹。

下面在根目录创建“dir1/dir2”文件夹,代码如下:

var dir2 =await Directory('${documentsDirectory.path}${Platform.pathSeparator}dir1${Platform.pathSeparator}dir2${Platform.pathSeparator}')
    .create(recursive: true);
1
2

# 文件夹遍历

_dirList() async {
  Directory documentsDirectory = await getApplicationDocumentsDirectory();
  String path = '${documentsDirectory.path}${Platform.pathSeparator}dirName';

  Stream<FileSystemEntity> fileList = Directory(path).list();

  await for(FileSystemEntity fileSystemEntity in fileList){
    print('$fileSystemEntity');
  }
}
1
2
3
4
5
6
7
8
9
10
  • 可选参数 recursive,默认值为 false,表示只遍历当前目录;设置为 true 时表示遍历当前目录及子目录。

# 判断文件的类型

await for(FileSystemEntity fileSystemEntity in fileList){
  print('$fileSystemEntity');
  FileSystemEntityType type = FileSystemEntity.typeSync(fileSystemEntity.path);
}
1
2
3
4
  • file:文件
  • directory:文件夹
  • link:链接文件
  • notFound:未知

# 文件夹重命名

_dirRename() async{
  Directory documentsDirectory = await getApplicationDocumentsDirectory();
  String path = '${documentsDirectory.path}${Platform.pathSeparator}dirName';
  var dir = Directory(path);
  var dir3= await dir.rename('${dir.parent.absolute.path}${Platform.pathSeparator}dir3');
}
1
2
3
4
5
6

# 文件夹删除

_deleteDir() async {
  Directory documentsDirectory = await getApplicationDocumentsDirectory();
  String path = '${documentsDirectory.path}${Platform.pathSeparator}dir3';
  var dir = await Directory(path).delete();
}
1
2
3
4
5
  • 有一个可选参数 recursive,默认值为 false,为 false 时如果删除的文件夹下还有内容将无法删除,抛出异常;设置为 true 时,删除当前文件夹及文件夹下所有内容

# 文件创建

_createFile() async {
  Directory documentsDirectory = await getApplicationDocumentsDirectory();
  String path = '${documentsDirectory.path}${Platform.pathSeparator}dirName${Platform.pathSeparator}file.txt';

  var file = await File(path).create(recursive: true);
}
1
2
3
4
5
6
  • create 中有一个可选参数 recursive,默认值为 false,为 false 时只创建文件,文件夹路径不存在抛出异常;设置为 true 时,创建文件及不存在的路径文件夹

# 写入数据

  • 向 file.txt 文件写入字符串
file.writeAsString('老孟 Flutter');
1
  • 向 file.txt 文件写入 bytes 数据
file.writeAsBytes(Utf8Encoder().convert("老孟 Flutter bytes 格式"));
1
  • 上面两种方法都是覆盖写入,向末尾追加内容
file.openWrite(mode: FileMode.append).write('老孟 Flutter 追加到末尾');
1

# 读取数据

  • 一行一行的读取数据
List<String> lines = await file.readAsLines();
lines.forEach((element) {
  print('$element');
});
1
2
3
4
  • 读取 bytes 并转换为 String
Utf8Decoder().convert(await file.readAsBytes());
1

# 删除文件

file.delete();
1

# 读取 asset 文件

_loadAsset(BuildContext context) async{
  // 或者String jsonData = await rootBundle.loadString(jsonFile);
  var jsonStr = await DefaultAssetBundle.of(context)
      .loadString('assets/json/data.json');
  var list = json.decode(jsonStr);
  list.forEach((element) {
    print('$element');
  });
}
1
2
3
4
5
6
7
8
9

# Android 文件存储

Android 文件存储分为内部存储外部存储

# 内部存储

用于保存应用的私有文件,其他应用无法访问这些数据,创建的文件在此应用的包名目录下,没有 root 权限 的手机无法在手机的 文件管理 应用中看到此目录,不过可以通过 Android Studio 工具查看,路径为:data/data/包名

  • cache 目录:对应 getTemporaryDirectory 方法,用于缓存文件,此目录随时可能被系统清除。
  • files 目录:对应 getApplicationSupportDirectory 方法。
  • code_cache:此目录存储 Flutter 相关代码和资源。
    • flutter_engine/skia:Flutter 渲染引擎。
    • flutter_guidePVWGWK/flutter_guide/build/flutter_assets:Flutter 资源文件。
  • shared_prefs: SharePreferences 的默认路径。
  • app_flutter:对应 getApplicationDocumentsDirectory 方法。
  • app_flutter/dbName:使用 sqlite 的默认路径,sqlite 也可以指定位置。

# SharePreferences 和 sqlite 是两种保存数据的第三方插件

内部存储的特点:

  • 安全性,其他应用无法访问这些数据。
  • 当应用卸载的时候,这些数据也会被删除,避免垃圾文件。
  • 不需要申请额外权限。
  • 存储的空间有限,此目录数据随时可能被系统清除,也可以通过 设置 中的 清除数据 可以清除此目录数据。
  • 国内特色,不同手机厂商对此目录做了不同的限制,比如总体大小限制、单个应用程序所占空间大小限制、清除数据策略不同等。

# 外部存储

这里面有一个特殊的目录:Android/data/包名

# 简单数据持久化

TIP

保存数据到本地磁盘是应用程序常用功能之一,比如保存用户登录信息、用户配置信息等。而保存这些信息通常使用 shared_preferences,它保存数据的形式为 Key-Value(键值对),支持 Android 和 iOS。shared_preferences 是一个第三方插件,在 Android 中使用 SharedPreferences,在 iOS 中使用 NSUserDefaults

shared_preferences 就是最受欢迎的框架之一, 适用于简单数据的持久化,复杂且大量数据的持久化建议使用 SQLite

shared_preferences 持久化保存数据,但在一下情况下会删除数据:

  • 卸载应用程序。
  • 在设置中清除应用数据。

# shared_preferences

shared_preferences 支持的数据类型有 int、double、bool、string、stringList

_saveData() async {
  var prefs = await SharedPreferences.getInstance();
  prefs.setInt('Key_Int', 12);
}

Future<int> _readData() async {
  var prefs = await SharedPreferences.getInstance();
  var result = prefs.getInt('Key_Int');
  return result ?? 0;
}
1
2
3
4
5
6
7
8
9
10
_saveData() async {
    var prefs = await SharedPreferences.getInstance();
    prefs.setDouble('Key_Double', 12.0);
  }

Future<double> _readData() async {
    var prefs = await SharedPreferences.getInstance();
    var result = prefs.getDouble('Key_Double');
    return result ?? 0.0;
  }
1
2
3
4
5
6
7
8
9
10
_saveData() async {
  var prefs = await SharedPreferences.getInstance();
  prefs.setString('Key', 'laomeng');
}

Future<String> _readData() async {
  var prefs = await SharedPreferences.getInstance();
  var result = prefs.getString('Key');
  return result ?? '';
}
1
2
3
4
5
6
7
8
9
10
_saveData() async {
  var prefs = await SharedPreferences.getInstance();
  prefs.setStringList('Key_StringList', ['laomeng','Flutter']);
}

Future<List<String>> _readData() async {
  var prefs = await SharedPreferences.getInstance();
  var result = prefs.getStringList('Key_StringList');
  return result ?? [];
}
1
2
3
4
5
6
7
8
9
10

# 删除指定 Key 的数据

Future<bool> _deleteData() async {
  var prefs = await SharedPreferences.getInstance();
  prefs.remove('Key');
}
1
2
3
4

# 清除所有数据

Future<bool> _clearData() async {
  var prefs = await SharedPreferences.getInstance();
  prefs.clear(); // 此方法谨慎使用
}
1
2
3
4

# Key 相关操作

  • 获取所有的 Key:
Future<Set<String>> _getKeys() async {
  var prefs = await SharedPreferences.getInstance();
  var keys = prefs.getKeys();
  return keys ?? [];
}
1
2
3
4
5
  • 检测是否 Key 是否存在:
Future<bool> _containsKey() async {
  var prefs = await SharedPreferences.getInstance();
  return prefs.containsKey('Key') ?? false;
}
1
2
3
4

# 大量复杂数据持久化

# 单例模式创建 SQLite 访问

使用 SQLite 并不是一定要使用单例模式,单例模式是为了保证整个应用程序仅有一个数据库实例和全局访问。

class DBProvider{

  static final DBProvider _singleton = DBProvider._internal();

  factory DBProvider() {
    return _singleton;
  }

  DBProvider._internal();
}
1
2
3
4
5
6
7
8
9
10
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';

class DBProvider {
  static final DBProvider _singleton = DBProvider._internal();

  factory DBProvider() {
    return _singleton;
  }

  DBProvider._internal();

  static Database _db;

  Future<Database> get db async {
    if (_db != null) {
      return _db;
    }
    _db = await _initDB();
    return _db;
  }

  Future<Database> _initDB() async {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path, 'dbName');
    return await openDatabase(path,
        version: 1, onCreate: _onCreate, onUpgrade: _onUpgrade);
  }

  ///
  /// 创建Table
  ///
  Future _onCreate(Database db, int version) async {}

  ///
  /// 更新Table
  ///
  Future _onUpgrade(Database db, int oldVersion, int newVersion) async {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
///
/// 创建Table
///
Future _onCreate(Database db, int version) async {
  return await db.execute("CREATE TABLE User ("
      "id integer primary key AUTOINCREMENT,"
      "name TEXT,"
      "age TEXT,"
      "sex integer"
      ")");
}
1
2
3
4
5
6
7
8
9
10
11

# 保存数据

先创建一个 User 的 Model 类,用于数据的保存:

class User {
  int id;
  String name;
  int age;
  int sex;

  User({this.id, this.name, this.age, this.sex});

  User.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    name = json['name'];
    age = json['age'];
    sex = json['sex'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['id'] = this.id;
    data['name'] = this.name;
    data['age'] = this.age;
    data['sex'] = this.sex;
    return data;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Future saveData(User user) async {
  var _db = await db;
  return await _db.insert('User', user.toJson());
}
1
2
3
4

# 案例:输入姓名、年龄、性别,点击保存

class _AddUser extends StatefulWidget {
  
  __AddUserState createState() => __AddUserState();
}

class __AddUserState extends State<_AddUser> {
  String _radioGroupValue = '0';
  TextEditingController _nameController;
  TextEditingController _ageController;

  
  void initState() {
    super.initState();
    _nameController = TextEditingController();
    _ageController = TextEditingController();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('保存数据'),
      ),
      body: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              Text('姓名:'),
              Flexible(
                child: TextField(
                  controller: _nameController,
                ),
              ),
            ],
          ),
          Row(
            children: <Widget>[
              Text('年龄:'),
              Flexible(
                child: TextField(
                  controller: _ageController,
                ),
              ),
            ],
          ),
          Row(
            children: <Widget>[
              Text('性别:'),
              Flexible(
                child: RadioListTile(
                  title: Text('男'),
                  value: '0',
                  groupValue: _radioGroupValue,
                  onChanged: (value) {
                    setState(() {
                      _radioGroupValue = value;
                    });
                  },
                ),
              ),
              Flexible(
                child: RadioListTile(
                  title: Text('女'),
                  value: '1',
                  groupValue: _radioGroupValue,
                  onChanged: (value) {
                    setState(() {
                      _radioGroupValue = value;
                    });
                  },
                ),
              ),
            ],
          ),
          Builder(
            builder: (context) {
              return RaisedButton(
                child: Text('保存'),
                onPressed: () async {
                  var user = User(
                      name: '${_nameController.text}',
                      age: int.parse('${_ageController.text}'),
                      sex: int.parse('$_radioGroupValue'));

                  int result = await DBProvider().saveData(user);
                  if (result > 0) {
                    Scaffold.of(context).showSnackBar(SnackBar(
                      content: Text('保存数据成功,result:$result'),
                    ));
                  } else {
                    Scaffold.of(context).showSnackBar(SnackBar(
                      content: Text('保存数据失败,result:$result'),
                    ));
                  }
                },
              );
            },
          )
        ],
      ),
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

# 网络请求 Dio

# 基础使用

发起 get 请求:

Response response=await Dio().get('https://xxx.com/test?name=\'laomeng\'&page=1');
print(response.data.toString());
1
2

请求参数也可通过如下方式:

Response response=await Dio().get("https://xxx.com/test",queryParameters: {'name':'laomeng','page':1});
1

发起 post 请求:

Response response=await Dio().post("https://xxx.com/test",queryParameters: {'name':'laomeng','page':1});
1

发送 FormData :

var formData = FormData.fromMap({
  "name": "laomeng",
  "page": 1,
});
Response response=await Dio().post('https://xxx.com/test',data:formData );
1
2
3
4
5

上传文件:

var formData = FormData.fromMap({
  'name': 'laomeng',
  'file': await MultipartFile.fromFile("./text.txt",filename: "upload.txt"),
  'files': [
    await MultipartFile.fromFile("./text1.txt", filename: "text1.txt"),
    await MultipartFile.fromFile("./text2.txt", filename: "text2.txt"),
  ]
});
Response response=await Dio().post('https://xxx.com/test',data:formData );
1
2
3
4
5
6
7
8
9

监听上传进度:

response = await Dio().post(
  'https://xxx.com/test',
  data: formData,
  onSendProgress: (int sent, int total) {
    print("$sent $total");
  },
);
1
2
3
4
5
6
7

# 拦截器

拦截器可以在请求前或响应之后做统一的预处理,比如给所有的请求的 header 添加 token 等。添加 打印日志 拦截器,网络请求的相关信息会打印到控制台,方便调试和定位问题:

_dio = Dio(options)..interceptors.add(LogInterceptor());
1
  • LogInterceptor 是 Dio 包自带的拦截器

# 自定义拦截器

class MyInterceptor extends Interceptor{

  
  Future onRequest(RequestOptions options) {
    // TODO: implement onRequest
    return super.onRequest(options);
  }

  
  Future onResponse(Response response) {
    // TODO: implement onResponse
    return super.onResponse(response);
  }

  
  Future onError(DioError err) {
    // TODO: implement onError
    return super.onError(err);
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# onRequest

请求前调用,一般是添加请求的公共部分,比如添加 token:


Future onRequest(RequestOptions options) {
  options.headers['token'] = 'token';
  return super.onRequest(options);
}
1
2
3
4
5

# onResponse

响应后调用,一般用于通用数据解析等。

# onError

请求发生异常时调用,一般用于异常功能处理

# 请求取消

通过 cancel token 取消请求:

CancelToken cancelToken = CancelToken();

Response response = await Dio().post("https://xxx.com/test",
  queryParameters: {'name': 'laomeng', 'page': 1},
  cancelToken: cancelToken);
1
2
3
4
5

取消:

cancelToken.cancel();
1

# Dio 封装

使用 Dio 的时候通常会创建一个单例并设置默认配置:

///
/// des: dio 封装
///
class HttpManager {
  factory HttpManager() => _getInstance();
  static HttpManager _instance;

  Dio get http => _dio;
  Dio _dio;

  static const int CONNECT_TIMEOUT = 50000;
  static const int RECEIVE_TIMEOUT = 30000;

  static _getInstance() {
    if (_instance == null) {
      _instance = HttpManager._();
    }
    return _instance;
  }

  ///
  /// 初始化
  ///
  HttpManager._() {
    var options = BaseOptions(
        connectTimeout: CONNECT_TIMEOUT, receiveTimeout: RECEIVE_TIMEOUT);
    _dio = Dio(options)..interceptors.add(LogInterceptor());
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

使用:

HttpManager().http.post('');
1