三方库网站:https://pub.dev
1 http安装和导入
安装
在pubspec.yaml中添加:http: ^0.12.1
导入
在需要使用的地方 import头文件
import 'package:http/http.dart' as http;
2 dio
文档: https://github.com/flutterchina/dio/blob/master/README-ZH.md
安装
dependencies:
dio: ^4.0.4
导入
import 'package:dio/dio.dart';
3 get 请求
http的get请求
final response = await http
.get('http://rap2api.taobao.org/app/mock/283772/api/chat/list');
dio的get请求
final dio = Dio();
dio.get('http://rap2api.taobao.org/app/mock/283772/api/chat/list');
4 dio下载进度显示
//savePath可以是一个字符串,也可以是个回调
void download2(Dio dio, String url, String savePath) {
dio
.download(url, (header) {
return savePath;
}, onReceiveProgress: showDownloadProgress)
.whenComplete(() => print('完成'))
.catchError((e) => print(e));
}
void download1(Dio dio, String url, savePath) {
dio
.download(url, savePath, onReceiveProgress: showDownloadProgress)
.then((value) => print(value))
.whenComplete(() => print('结束了'))
.catchError((e) => print(e));
}
void showDownloadProgress(received, total) {
if (total != -1) {
print((received / total * 100).toStringAsFixed(0) + "%");
}
}
5 定义数据模型 Chat
class Chat {
final String name;
final String message;
final String imageUrl;
const Chat({this.name, this.message, this.imageUrl});
//map转模型-->工厂构造函数
factory Chat.fromJson(Map json) {
return Chat(
name: json['name'],
message: json['message'],
imageUrl: json['imageUrl'],
);
}
}
6 关于Map和Json:
Map转Json : json.encode(map)=>返回json
Json转Map:json.decode(str)=>返回map
import 'dart:convert';
final chat = {
'name': '张三',
'message': '吃了吗?',
};
//Map转Json
final chatJson = json.encode(chat);
print(chatJson);
//Json转Map
final newChat = json.decode(chatJson);
print(newChat['name']);
print(newChat['message']);
print(newChat is Map);
7 完整写请求+UI刷新(FutureBuilder)
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../const.dart';
import 'dart:convert';
class ChatPage extends StatefulWidget {
@override
_ChatPageState createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
bool _cancleConnect = false;
List<Chat> _datas = [];
@override
void initState() {
super.initState();
}
Future<List<Chat>> getDatas() async {
//不再是取消连接了!
_cancleConnect = false;
final response = await http
.get('http://rap2api.taobao.org/app/mock/283772/api/chat/list');
if (response.statusCode == 200) {
//获取相应数据,并转成Map类型!
final responseBody = json.decode(response.body);
//转模型数组 map中遍历的结果需要返回出去
List<Chat> chatList = responseBody['chat_list'].map<Chat>((item) {
return Chat.fromJson(item);
}).toList();
return chatList;
} else {
throw Exception('statusCode:${response.statusCode}');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('微信'),
backgroundColor: WeChatThemeColor,
),
body: FutureBuilder(
future: getDatas(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
//正在加载
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Text('Loading...'),
);
}
//加载完毕
return ListView(
children: snapshot.data.map<Widget>((item) {
return ListTile(
title: Text(item.name),
subtitle: Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.only(right: 10),
height: 25,
child: Text(
item.message,
overflow: TextOverflow.ellipsis,
),
),
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
image: DecorationImage(
image: NetworkImage(item.imageUrl))),
));
}).toList(),
);
},
),
);
}
}
class Chat {
final String name;
final String message;
final String imageUrl;
const Chat({this.name, this.message, this.imageUrl});
factory Chat.fromJson(Map json) {
return Chat(
name: json['name'],
message: json['message'],
imageUrl: json['imageUrl'],
);
}
}
8 完整的请求+UI刷新(初始化时请求数据)
_cancleConnect = false;
List<Chat> _datas = [];
@override
void initState() {
super.initState();
print('Chat的init来了!');
getDatas()
.then((List<Chat> datas) {
print('数据来了!');
if (!_cancleConnect) {//如果超时后数据的返回,将抛弃数据
print('更新数据');
setState(() {
_datas = datas;
});
}
})
.catchError((e) {
print('错误$e');
})
.whenComplete(() {
print('完毕!');
})
.timeout(Duration(seconds: 6))
.catchError((timeout) {
_cancleConnect = true;
print('超时输出:$timeout');
});
}
Future<List<Chat>> getDatas() async {
//不再是取消连接了!
_cancleConnect = false;
final response = await http
.get('http://rap2api.taobao.org/app/mock/283772/api/chat/list');
if (response.statusCode == 200) {
//获取相应数据,并转成Map类型!
final responseBody = json.decode(response.body);
//转模型数组 map中遍历的结果需要返回出去
List<Chat> chatList = responseBody['chat_list'].map<Chat>((item) {
return Chat.fromJson(item);
}).toList();
return chatList;
} else {
throw Exception('statusCode:${response.statusCode}');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('微信'),
backgroundColor: WeChatThemeColor,
),
body: Container(
child: _datas.length == 0
? Center(
child: Text("Loading..."),
)
: ListView.builder(
itemCount: _datas.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(_datas[index].name),
subtitle: Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.only(right: 10),
height: 25,
child: Text(
_datas[index].message,
overflow: TextOverflow.ellipsis,
),
),
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
image: DecorationImage(
image: NetworkImage(_datas[index].imageUrl))),
));
},
),
),
);
}
}
class Chat {
final String name;
final String message;
final String imageUrl;
const Chat({this.name, this.message, this.imageUrl});
factory Chat.fromJson(Map json) {
return Chat(
name: json['name'],
message: json['message'],
imageUrl: json['imageUrl'],
);
}
}
9 dio请求的封装
网络异常统一格式类hi_error.dart
///需要登录的异常
class NeedLogin extends HiNetError {
NeedLogin({int code: 401, String message: '请先登录'}) : super(code, message);
}
///需要授权的异常
class NeedAuth extends HiNetError {
NeedAuth(String message, {int code: 403, dynamic data})
: super(code, message, data: data);
}
///网络异常统一格式类
class HiNetError implements Exception {
final int code;
final String message;
final dynamic data;
HiNetError(this.code, this.message, {this.data});
}
网络请求抽象类 & 统一网络层返回格式hi_net_adapter.dart
import 'dart:convert';
import 'package:flutter_bilibli/http/request/base_request.dart';
///网络请求抽象类
abstract class HiNetAdapter {
Future<HiNetResponse<T>> send<T>(BaseRequest request);
}
/// 统一网络层返回格式
class HiNetResponse<T> {
HiNetResponse({
this.data,
this.request,
this.statusCode,
this.statusMessage,
this.extra,
});
/// Response body. may have been transformed, please refer to [ResponseType].
T data;
/// The corresponding request info.
BaseRequest request;
/// Http status code.
int statusCode;
/// Returns the reason phrase associated with the status code.
/// The reason phrase must be set before the body is written
/// to. Setting the reason phrase after writing to the body.
String statusMessage;
/// Custom field that you can retrieve it later in `then`.
dynamic extra;
/// We are more concerned about `data` field.
@override
String toString() {
if (data is Map) {
return json.encode(data);
}
return data.toString();
}
}
dio适配器 dio_adapter.dart
import 'package:dio/dio.dart';
import 'package:flutter_bilibli/http/core/hi_error.dart';
import 'package:flutter_bilibli/http/core/hi_net_adapter.dart';
import 'package:flutter_bilibli/http/request/base_request.dart';
///Dio适配器
class DioAdapter extends HiNetAdapter {
@override
Future<HiNetResponse<T>> send<T>(BaseRequest request) async {
var response, options = Options(headers: request.header);
var error;
try {
if (request.httpMethod() == HttpMethod.GET) {
response = await Dio().get(request.url(), options: options);
} else if (request.httpMethod() == HttpMethod.POST) {
response = await Dio()
.post(request.url(), data: request.params, options: options);
} else if (request.httpMethod() == HttpMethod.DELETE) {
response = await Dio()
.delete(request.url(), data: request.params, options: options);
}
} on DioError catch (e) {
error = e;
response = e.response;
}
if (error != null) {
///抛出HiNetError
throw HiNetError(response?.statusCode ?? -1, error.toString(),
data: HiNetResponse(
data: response.data,
request: request,
statusCode: response.statusCode,
statusMessage: response.statusMessage,
extra: response));
}
return HiNetResponse(
data: response.data,
request: request,
statusCode: response.statusCode,
statusMessage: response.statusMessage,
extra: response);
}
}
网络请求入口hi_net.dart
import 'package:flutter_bilibli/http/core/dio_adapter.dart';
import '../request/base_request.dart';
import 'hi_error.dart';
import 'hi_net_adapter.dart';
///1.支持网络库插拔设计,且不干扰业务层
///2.基于配置请求请求,简洁易用
///3.Adapter设计,扩展性强
///4.统一异常和返回处理
class HiNet {
HiNet._();
static HiNet _instance;
static HiNet getInstance() {
if (_instance == null) {
_instance = HiNet._();
}
return _instance;
}
Future fire(BaseRequest request) async {
HiNetResponse response;
var error;
try {
response = await send(request);
} on HiNetError catch (e) {
error = e;
response = e.data;
printLog(e.message);
} catch (e) {
//其它异常
error = e;
printLog(e);
}
if (response == null) {
printLog(error);
}
var result = response.data;
printLog(result);
var status = response.statusCode;
switch (status) {
case 200:
return result;
break;
case 401:
throw NeedLogin();
break;
case 403:
throw NeedAuth(result.toString(), data: result);
break;
default:
throw HiNetError(status, result.toString(), data: result);
break;
}
}
Future<HiNetResponse<T>> send<T>(BaseRequest request) async {
///使用Dio发送请求
HiNetAdapter adapter = DioAdapter();
return adapter.send(request);
}
void printLog(log) {
print('hi_net:' + log.toString());
}
}
网络请求抽象类的封装base_request.dart
import 'package:flutter_bilibli/http/dao/login_dao.dart';
enum HttpMethod { GET, POST, DELETE }
///基础请求
abstract class BaseRequest {
// curl -X GET "http://api.devio.org/uapi/test/test?requestPrams=11" -H "accept: */*"
// curl -X GET "https://api.devio.org/uapi/test/test/1
var pathParams;
var useHttps = true;
String authority() {
return "api.devio.org";
}
HttpMethod httpMethod();
String path();
String url() {
Uri uri;
var pathStr = path();
//拼接path参数
if (pathParams != null) {
if (path().endsWith("/")) {
pathStr = "${path()}$pathParams";
} else {
pathStr = "${path()}/$pathParams";
}
}
//http和https切换
if (useHttps) {
uri = Uri.https(authority(), pathStr, params);
} else {
uri = Uri.http(authority(), pathStr, params);
}
if (needLogin()) {
//给需要登录的接口携带登录令牌
addHeader(LoginDao.BOARDING_PASS, LoginDao.getBoardingPass());
}
print('url:${uri.toString()}');
return uri.toString();
}
bool needLogin();
Map<String, String> params = Map();
///添加参数
BaseRequest add(String k, Object v) {
params[k] = v.toString();
return this;
}
Map<String, dynamic> header = {
'course-flag': 'fa',
//访问令牌,在课程公告获取
"auth-token": "MjAyMC0wNi0yMyAwMzoyNTowMQ==fa",
};
///添加header
BaseRequest addHeader(String k, Object v) {
header[k] = v.toString();
return this;
}
}
登录请求参数类login_request.dart
import 'package:flutter_bilibli/http/request/base_request.dart';
class LoginRequest extends BaseRequest {
@override
HttpMethod httpMethod() {
return HttpMethod.POST;
}
@override
bool needLogin() {
return false;
}
@override
String path() {
return "/uapi/user/login";
}
}
注册请求参数类registration_request.dart
import 'package:flutter_bilibli/http/request/base_request.dart';
class RegistrationRequest extends BaseRequest {
@override
HttpMethod httpMethod() {
return HttpMethod.POST;
}
@override
bool needLogin() {
return false;
}
@override
String path() {
return '/uapi/user/registration';
}
}
登录和注册请求数据接口的封装login_dao.dart
import 'package:flutter_bilibli/db/hi_cache.dart';
import 'package:flutter_bilibli/http/core/hi_net.dart';
import 'package:flutter_bilibli/http/request/base_request.dart';
import 'package:flutter_bilibli/http/request/login_request.dart';
import 'package:flutter_bilibli/http/request/registration_request.dart';
class LoginDao {
static const BOARDING_PASS = "boarding-pass";
static login(String userName, String password) {
return _send(userName, password);
}
static registration(
String userName, String password, String imoocId, String orderId) {
return _send(userName, password, imoocId: imoocId, orderId: orderId);
}
static _send(String userName, String password, {imoocId, orderId}) async {
BaseRequest request;
if (imoocId != null && orderId != null) {
request = RegistrationRequest();
} else {
request = LoginRequest();
}
request
.add("userName", userName)
.add("password", password)
.add("imoocId", imoocId)
.add("orderId", orderId);
var result = await HiNet.getInstance().fire(request);
print(result);
if (result['code'] == 0 && result['data'] != null) {
//保存登录令牌
HiCache.getInstance().setString(BOARDING_PASS, result['data']);
}
return result;
}
static getBoardingPass() {
return HiCache.getInstance().get(BOARDING_PASS);
}
}
10 错误DioError:CERTIFICATE_VERIFY_FAILED
void main() {
HttpOverrides.global = new MyHttpOverrides();
runApp(MyApp());
}
class MyHttpOverrides extends HttpOverrides{
@override
HttpClient createHttpClient(SecurityContext context){
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
}
}