今天来学学flutter的网络请求 Dio插件的使用 dio 是 Flutter 里最常用的 HTTP 网络请求库之一,很多项目都会用它替代原生的 http 包,因为它功能更完整
(话说看到这个Dio我就想起了某个埃及艳妇 狗头狗头狗头 )
OK回归正题
首先根目录执行
打开你的pubspec.yaml 文件
可以看到这个
1 2 3 4 5 6 7 8 dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.8 dio: ^5.9.2
基本使用
Dio().get(地址).then().catchError()
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 import 'package:flutter/material.dart' ;import 'package:dio/dio.dart' ;void main() { runApp(MaterialApp(home: HomePage())); } class HomePage extends StatefulWidget { const HomePage({super .key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State <HomePage > { String text = "点击获取数据" ; void getData() async { Response response = await Dio().get ('https://genshin.hoyoverse.com/en/' ); setState(() { text = response.data.toString(); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("原神网页" ), centerTitle: true ), body: Center( child: SingleChildScrollView( child: Padding( padding: EdgeInsets.all(20 ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ ElevatedButton( onPressed: getData, child: Text("获取网页" , style: TextStyle(fontSize: 18 )), ), SizedBox(height: 20 ), Text(text, textAlign: TextAlign.center), ], ), ), ), ), floatingActionButton: FloatingActionButton( onPressed: getData, child: Icon(Icons.refresh), ), ); } }
封装Dio工具
建立连接 → 发送数据 → 等待服务器返回 ↑ ↑ ↑ connect send receive
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import 'package:flutter/material.dart' ;import 'package:dio/dio.dart' ;void main() {}class DioUtils { final Dio _dio = Dio(); DioUtils() { _dio.options.baseUrl = 'https://github.com/' ; _dio.options.connectTimeout = Duration (seconds: 5 ); _dio.options.sendTimeout = Duration (seconds: 5 ); _dio.options.receiveTimeout = Duration (seconds: 5 ); } }
我们可以创建Dio工具类和实例对象,但是这种写法较为繁琐
使用“..”级操作符 作用是对同一个对象连续调用多个属性或方法时,不需要反复写对象名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import 'package:flutter/material.dart' ;import 'package:dio/dio.dart' ;void main() {}class DioUtils { final Dio _dio = Dio(); DioUtils() { _dio.options ..baseUrl = 'https://github.com/' ..connectTimeout = Duration (seconds: 5 ) ..sendTimeout = Duration (seconds: 5 ) ..receiveTimeout = Duration (seconds: 5 ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 var buffer = StringBuffer ();buffer.write('Hello' ); buffer.write(' ' ); buffer.write('World' ); var result = buffer.toString();var result = (StringBuffer () ..write('Hello' ) ..write(' ' ) ..write('World' )) .toString();
使用BaseOptions flutter 的 BaseOptions 是 Dio 网络库中的核心配置类,用于定义全局请求的基础参数。它本身不是 Flutter SDK 的一部分,而是来自 dio 包
BaseOptions 封装了所有 HTTP 请求的默认配置 ,创建 Dio 实例时传入,后续所有请求都会继承这些基础参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import 'package:flutter/material.dart' ;import 'package:dio/dio.dart' ;void main() {}class DioUtils { final Dio _dio = Dio( BaseOptions( baseUrl: 'https://github.com/' , connectTimeout: Duration (seconds: 5 ), sendTimeout: Duration (seconds: 5 ), receiveTimeout: Duration (seconds: 5 ), ), ); }
Dio拦截器(Interceptors) 拦截器是 Dio 的核心机制,允许你在请求发出前 和响应返回后 插入自定义逻辑,实现统一的日志、鉴权、错误处理、缓存等
Flutter 里的 Dio 拦截器(Interceptors)主要用于统一处理请求、响应、错误 ,比如:
自动添加 Token
打印网络日志
刷新 Token
统一错误处理
请求加密/解密
Loading 状态管理
Dio 的拦截器语法核心是继承 Interceptor,然后重写三个方法:
1 2 3 onRequest() onResponse() onError()
三个阶段执行顺序 一次请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 dio.get() | ↓ onRequest() | ↓ 服务器 | ↓ onResponse() | ↓ 业务代码
如果失败:
1 2 3 4 5 6 7 8 9 10 dio.get() | ↓ onRequest() | ↓ 服务器 | ↓ onError()
基本结构 先导入:
1 import 'package:dio/dio.dart' ;
创建 Dio:
添加拦截器:
1 2 3 dio.interceptors.add( MyInterceptor(), );
定义拦截器:
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 class MyInterceptor extends Interceptor { @override void onRequest( RequestOptions options, RequestInterceptorHandler handler, ) { print ("请求地址: ${options.uri} " ); handler.next(options); } @override void onResponse( Response response, ResponseInterceptorHandler handler, ) { print ("响应数据: ${response.data} " ); handler.next(response); } @override void onError( DioException err, ErrorInterceptorHandler handler, ) { print ("请求失败: ${err.message} " ); handler.next(err); } }
handler 三种操作 Request
意思: 处理完了,继续发送请求
Response
意思: 继续返回数据
Error
意思: 继续抛错误
我们先试一下在终端输出 其中onRequest() onResponse() onError()函数里的参数可以直接写参数名,无需声明类型
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 import 'package:dio/dio.dart' ;void main() async { Dio dio = Dio(); dio.interceptors.add( InterceptorsWrapper( onRequest: (options, handler) { print ("===发送请求===" ); print ("请求地址: ${options.uri} " ); handler.next(options); }, onResponse: (response, handler) { print ("===收到响应===" ); print (response.data); handler.next(response); }, onError: (error, handler) { print ("===发生错误===" ); print (error.message); handler.next(error); }, ), ); try { Response res = await dio.get ( 'https://jsonplaceholder.typicode.com/posts/1' , ); print ("===最终结果===" ); print (res.data); } catch (e) { print (e); } }
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 import 'package:flutter/material.dart' ;import 'package:dio/dio.dart' ;void main() {}class DioUtils { final Dio _dio = Dio(); DioUtils() { _dio.options ..baseUrl = 'https://jsonplaceholder.typicode.com' ..connectTimeout = Duration (seconds: 5 ) ..sendTimeout = Duration (seconds: 5 ) ..receiveTimeout = Duration (seconds: 5 ); _addInterceptors(); } void _addInterceptors() { _dio.interceptors.add( InterceptorsWrapper( onRequest: (context, handler) { handler.next(context); }, onResponse: (context, handler) { if (context.statusCode! >= 200 && context.statusCode! < 300 ) { handler.next(context); return ; } handler.reject(DioException(requestOptions: context.requestOptions)); }, onError: (context, handler) { handler.reject(context); }, ), ); } Future<Response> get (String url, {Map <String , dynamic >? params}) { return _dio.get (url, queryParameters: params); } }
然后与MaterialApp结合 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 import 'package:flutter/material.dart' ;import 'package:dio/dio.dart' ;void main() { runApp(const MaterialApp(home: DioDemoPage())); } class DioDemoPage extends StatefulWidget { const DioDemoPage({super .key}); @override State<DioDemoPage> createState() => _DioDemoPageState(); } class _DioDemoPageState extends State <DioDemoPage > { final Dio dio = Dio(); String result = "点击按钮发送请求" ; @override void initState() { super .initState(); dio.interceptors.add( InterceptorsWrapper( onRequest: (options, handler) { print ("===== 请求拦截器 =====" ); print ("请求地址: ${options.uri} " ); handler.next(options); }, onResponse: (response, handler) { print ("===== 响应拦截器 =====" ); print ("状态码: ${response.statusCode} " ); handler.next(response); }, onError: (error, handler) { print ("===== 错误拦截器 =====" ); print (error.message); handler.next(error); }, ), ); } Future<void > loadData() async { try { Response response = await dio.get ( "https://jsonplaceholder.typicode.com/posts/1" , ); setState(() { result = response.data.toString(); }); } catch (e) { setState(() { result = e.toString(); }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("Dio拦截器演示" ), centerTitle: true ), body: Padding( padding: const EdgeInsets.all(16 ), child: Center( child: Column( children: [ ElevatedButton(onPressed: loadData, child: const Text("发送请求" )), const SizedBox(height: 20 ), Expanded(child: SingleChildScrollView(child: Text(result))), ], ), ), ), ); } }
初始化获取数据 我们使用测试网站https://jsonplaceholder.typicode.com/
我们可以使用如下后缀来测试数据
这里我们使用/posts/1获得第一篇测试文章的数据
对比一下网站内容,完全一致
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 import 'package:dio/dio.dart' ;import 'package:flutter/material.dart' ;void main() { runApp(const MaterialApp(home: MainPage())); } class MainPage extends StatefulWidget { const MainPage({super .key}); @override State<MainPage> createState() => _MainPageState(); } class _MainPageState extends State <MainPage > { String result = '点击获取数据' ; void initState() { super .initState(); } void _getChannels() async { DioUtils util = DioUtils(); try { Response<dynamic > response = await util.get ('/posts/1' ); setState(() { result = response.data.toString(); }); print (response); } catch (e) { setState(() { result = e.toString(); }); print (e); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Dio' )), body: Padding( padding: EdgeInsets.all(16 ), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton(onPressed: _getChannels, child: Text("获取数据" )), const SizedBox(height: 20 ), Expanded(child: SingleChildScrollView(child: Text(result))), ], ), ), ), ); } } class DioUtils { final Dio _dio = Dio(); DioUtils() { _dio.options ..baseUrl = 'https://jsonplaceholder.typicode.com' ..connectTimeout = Duration (seconds: 5 ) ..receiveTimeout = Duration (seconds: 5 ); _addInterceptors(); } void _addInterceptors() { _dio.interceptors.add( InterceptorsWrapper( onRequest: (context, handler) { handler.next(context); }, onResponse: (context, handler) { if (context.statusCode! >= 200 && context.statusCode! < 300 ) { handler.next(context); return ; } handler.reject(DioException(requestOptions: context.requestOptions)); }, onError: (context, handler) { handler.reject(context); }, ), ); } Future<Response<dynamic >> get (String url, {Map <String , dynamic >? params}) { return _dio.get (url, queryParameters: params); } }
评论区