import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:new_learn/core/network/dio_client.dart'; import 'package:new_learn/core/network/post_api.dart'; /// ============================================================ /// Dio 网络请求 Demo 页 /// /// 一、演示内容: /// 1. GET 单条:PostApi.fetchPost(1) → 加载中/成功/失败三种 UI; /// 2. GET 列表:PostApi.fetchPosts() → 展示 `List`; /// 3. POST 新建:PostApi.createPost() → 展示服务器回执; /// 4. 错误兜底:请求不存在的路径,演示统一错误提示。 /// /// 二、页面写法要点: /// 1. 请求期间 _loading=true 显示 CircularProgressIndicator, /// 按钮禁用,防止重复点击; /// 2. try/catch 只 catch ApiException —— 封装层已把 DioException /// 翻译成中文提示,页面不用关心底层细节; /// 3. 所有 await 之后 setState 前必须 if (!mounted) return; /// (用户可能已切走页面,context 已失效)。 /// ============================================================ class DioDemoPage extends StatefulWidget { const DioDemoPage({super.key}); @override State createState() => _DioDemoPageState(); } class _DioDemoPageState extends State { bool _loading = false; // 请求中 String? _error; // 错误提示 String _resultTitle = '点击上方按钮发起请求'; // 结果区标题 String _resultBody = ''; // 结果区内容 /// 统一请求入口:loading → 请求 → 成功/失败 → 结束 Future _run(Future Function() action) async { setState(() { _loading = true; _error = null; }); try { await action(); } on ApiException catch (e) { if (!mounted) return; setState(() => _error = '请求失败:${e.message}'); } catch (e) { if (!mounted) return; setState(() => _error = '未知异常:$e'); } finally { if (mounted) setState(() => _loading = false); } } /// GET 单条帖子 Future _fetchOne() async { await _run(() async { final post = await PostApi.fetchPost(1); if (!mounted) return; setState(() { _resultTitle = 'GET /posts/1 成功'; _resultBody = 'id: ${post.id}\nuserId: ${post.userId}\n' 'title: ${post.title}\nbody: ${post.body}'; }); }); } /// GET 帖子列表(前 5 条) Future _fetchList() async { await _run(() async { final posts = await PostApi.fetchPosts(userId: 1); if (!mounted) return; setState(() { _resultTitle = 'GET /posts?userId=1 成功,共 ${posts.length} 条'; _resultBody = posts .take(5) .map((p) => '#${p.id} ${p.title}') .join('\n'); }); }); } /// POST 新建帖子 Future _create() async { await _run(() async { final post = await PostApi.createPost( title: 'Flutter 学习 Demo', body: 'Dio 封装 POST 请求测试', ); if (!mounted) return; setState(() { _resultTitle = 'POST /posts 成功(模拟创建)'; _resultBody = 'id: ${post.id}\nuserId: ${post.userId}\n' 'title: ${post.title}\nbody: ${post.body}'; }); }); } /// 错误演示:请求不存在的接口 Future _fetchError() async { await _run(() async { await DioClient.instance.get('/not-exist-path'); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Dio 网络请求封装 Demo')), body: SingleChildScrollView( padding: EdgeInsets.all(16.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 三个操作按钮 Wrap( spacing: 8, runSpacing: 8, children: [ FilledButton.icon( onPressed: _loading ? null : _fetchOne, icon: const Icon(Icons.south_west), label: const Text('GET 单条'), ), FilledButton.icon( onPressed: _loading ? null : _fetchList, icon: const Icon(Icons.list), label: const Text('GET 列表'), ), FilledButton.icon( onPressed: _loading ? null : _create, icon: const Icon(Icons.add), label: const Text('POST 新建'), ), OutlinedButton.icon( onPressed: _loading ? null : _fetchError, icon: const Icon(Icons.warning_amber), label: const Text('故意报错'), ), ], ), SizedBox(height: 20.h), // 加载中 if (_loading) Padding( padding: EdgeInsets.symmetric(vertical: 40.h), child: Center(child: CircularProgressIndicator()), ), // 错误提示 if (_error != null) Container( width: double.infinity, margin: EdgeInsets.only(top: 12.h), padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: const Color(0xFFFDECEA), borderRadius: BorderRadius.circular(8.r), ), child: Text( _error!, style: const TextStyle(color: Color(0xFFD32F2F)), ), ), // 结果展示 SizedBox(height: 12.h), Container( width: double.infinity, padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), border: Border.all(color: const Color(0xFFE5E7EB)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _resultTitle, style: TextStyle( fontSize: 15.sp, fontWeight: FontWeight.bold, color: Color(0xFF333333), ), ), SizedBox(height: 8.h), SelectableText( _resultBody.isEmpty ? '(暂无数据)' : _resultBody, style: TextStyle( fontSize: 13.sp, height: 1.6, color: Color(0xFF666666), ), ), ], ), ), SizedBox(height: 12.h), // 日志提示 Text( '💡 请求/响应日志在控制台打印(debugPrint),' '可在 DioClient 拦截器里查看完整 URL。', style: TextStyle(fontSize: 12.sp, color: Color(0xFF999999)), ), ], ), ), ); } }