三方库网站:https://pub.dev

1 http安装和导入

安装

在pubspec.yaml中添加:http: ^0.12.1
image.png

导入

在需要使用的地方 import头文件

  1. import 'package:http/http.dart' as http;

2 dio

文档: https://github.com/flutterchina/dio/blob/master/README-ZH.md

安装

  1. dependencies:
  2. dio: ^4.0.4

导入

  1. import 'package:dio/dio.dart';

3 get 请求

http的get请求

  1. final response = await http
  2. .get('http://rap2api.taobao.org/app/mock/283772/api/chat/list');

dio的get请求

  1. final dio = Dio();
  2. dio.get('http://rap2api.taobao.org/app/mock/283772/api/chat/list');

4 dio下载进度显示

  1. //savePath可以是一个字符串,也可以是个回调
  2. void download2(Dio dio, String url, String savePath) {
  3. dio
  4. .download(url, (header) {
  5. return savePath;
  6. }, onReceiveProgress: showDownloadProgress)
  7. .whenComplete(() => print('完成'))
  8. .catchError((e) => print(e));
  9. }
  10. void download1(Dio dio, String url, savePath) {
  11. dio
  12. .download(url, savePath, onReceiveProgress: showDownloadProgress)
  13. .then((value) => print(value))
  14. .whenComplete(() => print('结束了'))
  15. .catchError((e) => print(e));
  16. }
  17. void showDownloadProgress(received, total) {
  18. if (total != -1) {
  19. print((received / total * 100).toStringAsFixed(0) + "%");
  20. }
  21. }

5 定义数据模型 Chat

  1. class Chat {
  2. final String name;
  3. final String message;
  4. final String imageUrl;
  5. const Chat({this.name, this.message, this.imageUrl});
  6. //map转模型-->工厂构造函数
  7. factory Chat.fromJson(Map json) {
  8. return Chat(
  9. name: json['name'],
  10. message: json['message'],
  11. imageUrl: json['imageUrl'],
  12. );
  13. }
  14. }

6 关于Map和Json:

Map转Json : json.encode(map)=>返回json

Json转Map:json.decode(str)=>返回map

  1. import 'dart:convert';
  2. final chat = {
  3. 'name': '张三',
  4. 'message': '吃了吗?',
  5. };
  6. //Map转Json
  7. final chatJson = json.encode(chat);
  8. print(chatJson);
  9. //Json转Map
  10. final newChat = json.decode(chatJson);
  11. print(newChat['name']);
  12. print(newChat['message']);
  13. print(newChat is Map);

7 完整写请求+UI刷新(FutureBuilder)

  1. import 'package:flutter/material.dart';
  2. import 'package:http/http.dart' as http;
  3. import '../const.dart';
  4. import 'dart:convert';
  5. class ChatPage extends StatefulWidget {
  6. @override
  7. _ChatPageState createState() => _ChatPageState();
  8. }
  9. class _ChatPageState extends State<ChatPage> {
  10. bool _cancleConnect = false;
  11. List<Chat> _datas = [];
  12. @override
  13. void initState() {
  14. super.initState();
  15. }
  16. Future<List<Chat>> getDatas() async {
  17. //不再是取消连接了!
  18. _cancleConnect = false;
  19. final response = await http
  20. .get('http://rap2api.taobao.org/app/mock/283772/api/chat/list');
  21. if (response.statusCode == 200) {
  22. //获取相应数据,并转成Map类型!
  23. final responseBody = json.decode(response.body);
  24. //转模型数组 map中遍历的结果需要返回出去
  25. List<Chat> chatList = responseBody['chat_list'].map<Chat>((item) {
  26. return Chat.fromJson(item);
  27. }).toList();
  28. return chatList;
  29. } else {
  30. throw Exception('statusCode:${response.statusCode}');
  31. }
  32. }
  33. @override
  34. Widget build(BuildContext context) {
  35. return Scaffold(
  36. appBar: AppBar(
  37. title: Text('微信'),
  38. backgroundColor: WeChatThemeColor,
  39. ),
  40. body: FutureBuilder(
  41. future: getDatas(),
  42. builder: (BuildContext context, AsyncSnapshot snapshot) {
  43. //正在加载
  44. if (snapshot.connectionState == ConnectionState.waiting) {
  45. return Center(
  46. child: Text('Loading...'),
  47. );
  48. }
  49. //加载完毕
  50. return ListView(
  51. children: snapshot.data.map<Widget>((item) {
  52. return ListTile(
  53. title: Text(item.name),
  54. subtitle: Container(
  55. alignment: Alignment.bottomCenter,
  56. padding: EdgeInsets.only(right: 10),
  57. height: 25,
  58. child: Text(
  59. item.message,
  60. overflow: TextOverflow.ellipsis,
  61. ),
  62. ),
  63. leading: Container(
  64. width: 44,
  65. height: 44,
  66. decoration: BoxDecoration(
  67. borderRadius: BorderRadius.circular(6.0),
  68. image: DecorationImage(
  69. image: NetworkImage(item.imageUrl))),
  70. ));
  71. }).toList(),
  72. );
  73. },
  74. ),
  75. );
  76. }
  77. }
  78. class Chat {
  79. final String name;
  80. final String message;
  81. final String imageUrl;
  82. const Chat({this.name, this.message, this.imageUrl});
  83. factory Chat.fromJson(Map json) {
  84. return Chat(
  85. name: json['name'],
  86. message: json['message'],
  87. imageUrl: json['imageUrl'],
  88. );
  89. }
  90. }

8 完整的请求+UI刷新(初始化时请求数据)

  1. _cancleConnect = false;
  2. List<Chat> _datas = [];
  3. @override
  4. void initState() {
  5. super.initState();
  6. print('Chat的init来了!');
  7. getDatas()
  8. .then((List<Chat> datas) {
  9. print('数据来了!');
  10. if (!_cancleConnect) {//如果超时后数据的返回,将抛弃数据
  11. print('更新数据');
  12. setState(() {
  13. _datas = datas;
  14. });
  15. }
  16. })
  17. .catchError((e) {
  18. print('错误$e');
  19. })
  20. .whenComplete(() {
  21. print('完毕!');
  22. })
  23. .timeout(Duration(seconds: 6))
  24. .catchError((timeout) {
  25. _cancleConnect = true;
  26. print('超时输出:$timeout');
  27. });
  28. }
  29. Future<List<Chat>> getDatas() async {
  30. //不再是取消连接了!
  31. _cancleConnect = false;
  32. final response = await http
  33. .get('http://rap2api.taobao.org/app/mock/283772/api/chat/list');
  34. if (response.statusCode == 200) {
  35. //获取相应数据,并转成Map类型!
  36. final responseBody = json.decode(response.body);
  37. //转模型数组 map中遍历的结果需要返回出去
  38. List<Chat> chatList = responseBody['chat_list'].map<Chat>((item) {
  39. return Chat.fromJson(item);
  40. }).toList();
  41. return chatList;
  42. } else {
  43. throw Exception('statusCode:${response.statusCode}');
  44. }
  45. }
  46. @override
  47. Widget build(BuildContext context) {
  48. return Scaffold(
  49. appBar: AppBar(
  50. title: Text('微信'),
  51. backgroundColor: WeChatThemeColor,
  52. ),
  53. body: Container(
  54. child: _datas.length == 0
  55. ? Center(
  56. child: Text("Loading..."),
  57. )
  58. : ListView.builder(
  59. itemCount: _datas.length,
  60. itemBuilder: (BuildContext context, int index) {
  61. return ListTile(
  62. title: Text(_datas[index].name),
  63. subtitle: Container(
  64. alignment: Alignment.bottomCenter,
  65. padding: EdgeInsets.only(right: 10),
  66. height: 25,
  67. child: Text(
  68. _datas[index].message,
  69. overflow: TextOverflow.ellipsis,
  70. ),
  71. ),
  72. leading: Container(
  73. width: 44,
  74. height: 44,
  75. decoration: BoxDecoration(
  76. borderRadius: BorderRadius.circular(6.0),
  77. image: DecorationImage(
  78. image: NetworkImage(_datas[index].imageUrl))),
  79. ));
  80. },
  81. ),
  82. ),
  83. );
  84. }
  85. }
  86. class Chat {
  87. final String name;
  88. final String message;
  89. final String imageUrl;
  90. const Chat({this.name, this.message, this.imageUrl});
  91. factory Chat.fromJson(Map json) {
  92. return Chat(
  93. name: json['name'],
  94. message: json['message'],
  95. imageUrl: json['imageUrl'],
  96. );
  97. }
  98. }

9 dio请求的封装

image.png

网络异常统一格式类hi_error.dart

  1. ///需要登录的异常
  2. class NeedLogin extends HiNetError {
  3. NeedLogin({int code: 401, String message: '请先登录'}) : super(code, message);
  4. }
  5. ///需要授权的异常
  6. class NeedAuth extends HiNetError {
  7. NeedAuth(String message, {int code: 403, dynamic data})
  8. : super(code, message, data: data);
  9. }
  10. ///网络异常统一格式类
  11. class HiNetError implements Exception {
  12. final int code;
  13. final String message;
  14. final dynamic data;
  15. HiNetError(this.code, this.message, {this.data});
  16. }

网络请求抽象类 & 统一网络层返回格式hi_net_adapter.dart

  1. import 'dart:convert';
  2. import 'package:flutter_bilibli/http/request/base_request.dart';
  3. ///网络请求抽象类
  4. abstract class HiNetAdapter {
  5. Future<HiNetResponse<T>> send<T>(BaseRequest request);
  6. }
  7. /// 统一网络层返回格式
  8. class HiNetResponse<T> {
  9. HiNetResponse({
  10. this.data,
  11. this.request,
  12. this.statusCode,
  13. this.statusMessage,
  14. this.extra,
  15. });
  16. /// Response body. may have been transformed, please refer to [ResponseType].
  17. T data;
  18. /// The corresponding request info.
  19. BaseRequest request;
  20. /// Http status code.
  21. int statusCode;
  22. /// Returns the reason phrase associated with the status code.
  23. /// The reason phrase must be set before the body is written
  24. /// to. Setting the reason phrase after writing to the body.
  25. String statusMessage;
  26. /// Custom field that you can retrieve it later in `then`.
  27. dynamic extra;
  28. /// We are more concerned about `data` field.
  29. @override
  30. String toString() {
  31. if (data is Map) {
  32. return json.encode(data);
  33. }
  34. return data.toString();
  35. }
  36. }

dio适配器 dio_adapter.dart

  1. import 'package:dio/dio.dart';
  2. import 'package:flutter_bilibli/http/core/hi_error.dart';
  3. import 'package:flutter_bilibli/http/core/hi_net_adapter.dart';
  4. import 'package:flutter_bilibli/http/request/base_request.dart';
  5. ///Dio适配器
  6. class DioAdapter extends HiNetAdapter {
  7. @override
  8. Future<HiNetResponse<T>> send<T>(BaseRequest request) async {
  9. var response, options = Options(headers: request.header);
  10. var error;
  11. try {
  12. if (request.httpMethod() == HttpMethod.GET) {
  13. response = await Dio().get(request.url(), options: options);
  14. } else if (request.httpMethod() == HttpMethod.POST) {
  15. response = await Dio()
  16. .post(request.url(), data: request.params, options: options);
  17. } else if (request.httpMethod() == HttpMethod.DELETE) {
  18. response = await Dio()
  19. .delete(request.url(), data: request.params, options: options);
  20. }
  21. } on DioError catch (e) {
  22. error = e;
  23. response = e.response;
  24. }
  25. if (error != null) {
  26. ///抛出HiNetError
  27. throw HiNetError(response?.statusCode ?? -1, error.toString(),
  28. data: HiNetResponse(
  29. data: response.data,
  30. request: request,
  31. statusCode: response.statusCode,
  32. statusMessage: response.statusMessage,
  33. extra: response));
  34. }
  35. return HiNetResponse(
  36. data: response.data,
  37. request: request,
  38. statusCode: response.statusCode,
  39. statusMessage: response.statusMessage,
  40. extra: response);
  41. }
  42. }

网络请求入口hi_net.dart

  1. import 'package:flutter_bilibli/http/core/dio_adapter.dart';
  2. import '../request/base_request.dart';
  3. import 'hi_error.dart';
  4. import 'hi_net_adapter.dart';
  5. ///1.支持网络库插拔设计,且不干扰业务层
  6. ///2.基于配置请求请求,简洁易用
  7. ///3.Adapter设计,扩展性强
  8. ///4.统一异常和返回处理
  9. class HiNet {
  10. HiNet._();
  11. static HiNet _instance;
  12. static HiNet getInstance() {
  13. if (_instance == null) {
  14. _instance = HiNet._();
  15. }
  16. return _instance;
  17. }
  18. Future fire(BaseRequest request) async {
  19. HiNetResponse response;
  20. var error;
  21. try {
  22. response = await send(request);
  23. } on HiNetError catch (e) {
  24. error = e;
  25. response = e.data;
  26. printLog(e.message);
  27. } catch (e) {
  28. //其它异常
  29. error = e;
  30. printLog(e);
  31. }
  32. if (response == null) {
  33. printLog(error);
  34. }
  35. var result = response.data;
  36. printLog(result);
  37. var status = response.statusCode;
  38. switch (status) {
  39. case 200:
  40. return result;
  41. break;
  42. case 401:
  43. throw NeedLogin();
  44. break;
  45. case 403:
  46. throw NeedAuth(result.toString(), data: result);
  47. break;
  48. default:
  49. throw HiNetError(status, result.toString(), data: result);
  50. break;
  51. }
  52. }
  53. Future<HiNetResponse<T>> send<T>(BaseRequest request) async {
  54. ///使用Dio发送请求
  55. HiNetAdapter adapter = DioAdapter();
  56. return adapter.send(request);
  57. }
  58. void printLog(log) {
  59. print('hi_net:' + log.toString());
  60. }
  61. }

网络请求抽象类的封装base_request.dart

  1. import 'package:flutter_bilibli/http/dao/login_dao.dart';
  2. enum HttpMethod { GET, POST, DELETE }
  3. ///基础请求
  4. abstract class BaseRequest {
  5. // curl -X GET "http://api.devio.org/uapi/test/test?requestPrams=11" -H "accept: */*"
  6. // curl -X GET "https://api.devio.org/uapi/test/test/1
  7. var pathParams;
  8. var useHttps = true;
  9. String authority() {
  10. return "api.devio.org";
  11. }
  12. HttpMethod httpMethod();
  13. String path();
  14. String url() {
  15. Uri uri;
  16. var pathStr = path();
  17. //拼接path参数
  18. if (pathParams != null) {
  19. if (path().endsWith("/")) {
  20. pathStr = "${path()}$pathParams";
  21. } else {
  22. pathStr = "${path()}/$pathParams";
  23. }
  24. }
  25. //http和https切换
  26. if (useHttps) {
  27. uri = Uri.https(authority(), pathStr, params);
  28. } else {
  29. uri = Uri.http(authority(), pathStr, params);
  30. }
  31. if (needLogin()) {
  32. //给需要登录的接口携带登录令牌
  33. addHeader(LoginDao.BOARDING_PASS, LoginDao.getBoardingPass());
  34. }
  35. print('url:${uri.toString()}');
  36. return uri.toString();
  37. }
  38. bool needLogin();
  39. Map<String, String> params = Map();
  40. ///添加参数
  41. BaseRequest add(String k, Object v) {
  42. params[k] = v.toString();
  43. return this;
  44. }
  45. Map<String, dynamic> header = {
  46. 'course-flag': 'fa',
  47. //访问令牌,在课程公告获取
  48. "auth-token": "MjAyMC0wNi0yMyAwMzoyNTowMQ==fa",
  49. };
  50. ///添加header
  51. BaseRequest addHeader(String k, Object v) {
  52. header[k] = v.toString();
  53. return this;
  54. }
  55. }

登录请求参数类login_request.dart

  1. import 'package:flutter_bilibli/http/request/base_request.dart';
  2. class LoginRequest extends BaseRequest {
  3. @override
  4. HttpMethod httpMethod() {
  5. return HttpMethod.POST;
  6. }
  7. @override
  8. bool needLogin() {
  9. return false;
  10. }
  11. @override
  12. String path() {
  13. return "/uapi/user/login";
  14. }
  15. }

注册请求参数类registration_request.dart

  1. import 'package:flutter_bilibli/http/request/base_request.dart';
  2. class RegistrationRequest extends BaseRequest {
  3. @override
  4. HttpMethod httpMethod() {
  5. return HttpMethod.POST;
  6. }
  7. @override
  8. bool needLogin() {
  9. return false;
  10. }
  11. @override
  12. String path() {
  13. return '/uapi/user/registration';
  14. }
  15. }

登录和注册请求数据接口的封装login_dao.dart

  1. import 'package:flutter_bilibli/db/hi_cache.dart';
  2. import 'package:flutter_bilibli/http/core/hi_net.dart';
  3. import 'package:flutter_bilibli/http/request/base_request.dart';
  4. import 'package:flutter_bilibli/http/request/login_request.dart';
  5. import 'package:flutter_bilibli/http/request/registration_request.dart';
  6. class LoginDao {
  7. static const BOARDING_PASS = "boarding-pass";
  8. static login(String userName, String password) {
  9. return _send(userName, password);
  10. }
  11. static registration(
  12. String userName, String password, String imoocId, String orderId) {
  13. return _send(userName, password, imoocId: imoocId, orderId: orderId);
  14. }
  15. static _send(String userName, String password, {imoocId, orderId}) async {
  16. BaseRequest request;
  17. if (imoocId != null && orderId != null) {
  18. request = RegistrationRequest();
  19. } else {
  20. request = LoginRequest();
  21. }
  22. request
  23. .add("userName", userName)
  24. .add("password", password)
  25. .add("imoocId", imoocId)
  26. .add("orderId", orderId);
  27. var result = await HiNet.getInstance().fire(request);
  28. print(result);
  29. if (result['code'] == 0 && result['data'] != null) {
  30. //保存登录令牌
  31. HiCache.getInstance().setString(BOARDING_PASS, result['data']);
  32. }
  33. return result;
  34. }
  35. static getBoardingPass() {
  36. return HiCache.getInstance().get(BOARDING_PASS);
  37. }
  38. }

10 错误DioError:CERTIFICATE_VERIFY_FAILED

  1. void main() {
  2. HttpOverrides.global = new MyHttpOverrides();
  3. runApp(MyApp());
  4. }
  5. class MyHttpOverrides extends HttpOverrides{
  6. @override
  7. HttpClient createHttpClient(SecurityContext context){
  8. return super.createHttpClient(context)
  9. ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
  10. }
  11. }