文件操作
APP目录
Android 和 iOS 的应用存储目录不同,PathProvider (opens new window)插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置:
-
临时目录: getTemporaryDirectory() ,可以使用 来获取临时目录; 系统可随时清除临时目录的文件。在 Android上,这是getCacheDir() (opens new window)返回的值,在 iOS 上,这对应于NSTemporaryDirectory() (opens new window)返回的值。
-
文档目录: getApplicationDocumentsDirectory()来获取应用程序的文档目录,该目录用于存储只有自己可以访问的文件。只有当应用程序被卸载时,系统才会清除该目录。
在 Android 上,这是AppData目录,在 iOS 上,这对应于NSDocumentDirectory。。 -
外部存储目录:getExternalStorageDirectory()来获取外部存储目录,如 SD 卡;
Android 下结果是Android SDK 中getExternalStorageDirectory的返回值。
iOS不支持外部目录,所以在 iOS 下调用该方法会抛出UnsupportedError异常,
使用:
pubspec.yaml中添加引用:path_provider: ^2.0.2
文件读写演示:
import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
class FileOperationRoute extends StatefulWidget {
FileOperationRoute({Key? key}) : super(key: key);
@override
_FileOperationRouteState createState() => _FileOperationRouteState();
}
class _FileOperationRouteState extends State<FileOperationRoute> {
int _counter = 0;
@override
void initState() {
super.initState();
//从文件读取点击次数
_readCounter().then((int value) {
setState(() {
_counter = value;
});
});
}
Future<File> _getLocalFile() async {
// 获取应用目录
String dir = (await getApplicationDocumentsDirectory()).path;
return File('$dir/counter.txt');
}
Future<int> _readCounter() async {
try {
File file = await _getLocalFile();
// 读取点击次数(以字符串)
String contents = await file.readAsString();
return int.parse(contents);
} on FileSystemException {
return 0;
}
}
_incrementCounter() async {
setState(() {
_counter++;
});
// 将点击次数以字符串类型写到文件中
await (await _getLocalFile()).writeAsString('$_counter');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('文件操作')),
body: Center(
child: Text('点击了 $_counter 次'),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
在实际开发中,如果要存储一些简单的数据,使用shared_preferences插件会比较简单
网络请求
1.Dio库
发起 GET 请求 :
Response response;
response=await dio.get("/test?id=12&name=wendu")
print(response.data.toString());
对于GET请求我们可以将query参数通过对象来传递,上面的代码等同于:
response=await dio.get("/test",queryParameters:{"id":12,"name":"wendu"})
print(response);
发起一个 POST 请求:
response=await dio.post("/test",data:{"id":12,"name":"wendu"})
发起多个并发请求:
response= await Future.wait([dio.post("/info"),dio.get("/token")]);
下载文件:
response=await dio.download("https://www.google.com/",_savePath);
发送 FormData:
FormData formData = FormData.from({
"name": "wendux",
"age": 25,
});
response = await dio.post("/info", data: formData)
如果发送的数据是FormData,则dio会将请求header的contentType设为“multipart/form-data”。
通过FormData上传多个文件:
FormData formData = FormData.from({
"name": "wendux",
"age": 25,
"file1": UploadFileInfo(File("./upload.txt"), "upload1.txt"),
"file2": UploadFileInfo(File("./upload.txt"), "upload2.txt"),
// 支持文件数组上传
"files": [
UploadFileInfo(File("./example/upload.txt"), "upload.txt"),
UploadFileInfo(File("./example/upload.txt"), "upload.txt")
]
});
response = await dio.post("/info", data: formData)
dio内部仍然使用HttpClient发起的请求,所以代理、请求认证、证书校验等和HttpClient是相同的,我们可以在onHttpClientCreate回调中设置,例如:
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
//设置代理
client.findProxy = (uri) {
return "PROXY 192.168.1.2:8888";
};
//校验证书
httpClient.badCertificateCallback=(X509Certificate cert, String host, int port){
if(cert.pem==PEM){
return true; //证书一致,则允许发送数据
}
return false;
};
};
注意,onHttpClientCreate会在当前dio实例内部需要创建HttpClient时调用,所以通过此回调配置HttpClient会对整个dio实例生效,如果应用需要多种代理或证书校验策略,可以创建不同的dio实例来分别实现。
dio主页:https://github.com/flutterchina/dio
2.websocket
演示WebSocket通信过程
import 'package:flutter/material.dart';
import 'package:web_socket_channel/io.dart';
class WebSocketRoute extends StatefulWidget {
@override
_WebSocketRouteState createState() => _WebSocketRouteState();
}
class _WebSocketRouteState extends State<WebSocketRoute> {
TextEditingController _controller = TextEditingController();
IOWebSocketChannel channel;
String _text = "";
@override
void initState() {
//创建websocket连接
channel = IOWebSocketChannel.connect('wss://echo.websocket.events');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("WebSocket(内容回显)"),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Form(
child: TextFormField(
controller: _controller,
decoration: InputDecoration(labelText: 'Send a message'),
),
),
//当服务器传输的数据是指定为二进制时,StreamBuilder的snapshot.data的类型就是List<int>,是文本时,则为String
StreamBuilder(
stream: channel.stream,
builder: (context, snapshot) {
//网络不通会走到这
if (snapshot.hasError) {
_text = "网络不通...";
} else if (snapshot.hasData) {
_text = "echo: "+snapshot.data;
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Text(_text),
);
},
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _sendMessage,
tooltip: 'Send message',
child: Icon(Icons.send),
),
);
}
void _sendMessage() {
if (_controller.text.isNotEmpty) {
channel.sink.add(_controller.text);
}
}
@override
void dispose() {
channel.sink.close();
super.dispose();
}
}
3.JSON转Dart Model
- Android Studio - Settings - Plugins - 搜索JsonToDart 插件
- 文件夹右键New - Json To Dart - 输入json文件自动生成
生成示例如下:
import 'as_t.dart';
class HotKeyModel {
int? id;
String? link;
String? name;
int? order;
int? visible;
HotKeyModel({this.id, this.link, this.name, this.order, this.visible});
HotKeyModel.fromJson(Map<String, dynamic> json) {
id = asT<int?>(json['id']);
link = asT<String?>(json['link']);
name = asT<String?>(json['name']);
order = asT<int?>(json['order']);
visible = asT<int?>(json['visible']);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = this.id;
data['link'] = this.link;
data['name'] = this.name;
data['order'] = this.order;
data['visible'] = this.visible;
return data;
}
}