三方库网站: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转Jsonfinal chatJson = json.encode(chat);print(chatJson);//Json转Mapfinal 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 = [];@overridevoid 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}');}}@overrideWidget 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 = [];@overridevoid 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}');}}@overrideWidget 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.@overrideString 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 {@overrideFuture<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) {///抛出HiNetErrorthrow 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/1var 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",};///添加headerBaseRequest 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 {@overrideHttpMethod httpMethod() {return HttpMethod.POST;}@overridebool needLogin() {return false;}@overrideString path() {return "/uapi/user/login";}}
注册请求参数类registration_request.dart
import 'package:flutter_bilibli/http/request/base_request.dart';class RegistrationRequest extends BaseRequest {@overrideHttpMethod httpMethod() {return HttpMethod.POST;}@overridebool needLogin() {return false;}@overrideString 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{@overrideHttpClient createHttpClient(SecurityContext context){return super.createHttpClient(context)..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;}}
