import 'package:flutter/material.dart'; import 'package:yx_net_inspector/yx_net_inspector.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'dart:math'; class DemoPage extends StatefulWidget { const DemoPage({super.key}); @override State createState() => _DemoPageState(); } class _DemoPageState extends State { int _requestCounter = 0; final Random _random = Random(); bool _isLoading = false; // 真实的开放API端点 final List> _endpoints = [ { 'name': '用户列表', 'method': 'GET', 'url': 'https://jsonplaceholder.typicode.com/users', 'description': '获取用户列表 (JSONPlaceholder)', 'icon': Icons.people, }, { 'name': '文章列表', 'method': 'GET', 'url': 'https://jsonplaceholder.typicode.com/posts', 'description': '获取文章列表 (JSONPlaceholder)', 'icon': Icons.article, }, { 'name': '随机猫咪事实', 'method': 'GET', 'url': 'https://catfact.ninja/fact', 'description': '获取随机猫咪事实 (Cat Facts API)', 'icon': Icons.pets, }, { 'name': '创建文章', 'method': 'POST', 'url': 'https://jsonplaceholder.typicode.com/posts', 'description': '创建新文章 (JSONPlaceholder)', 'icon': Icons.post_add, }, { 'name': 'HTTP测试', 'method': 'GET', 'url': 'https://httpbin.org/json', 'description': '测试HTTP响应 (HTTPBin)', 'icon': Icons.http, }, { 'name': '用户信息', 'method': 'GET', 'url': 'https://reqres.in/api/users/2', 'description': '获取单个用户 (ReqRes)', 'icon': Icons.person, }, { 'name': 'IP地址信息', 'method': 'GET', 'url': 'https://httpbin.org/ip', 'description': '获取IP地址信息 (HTTPBin)', 'icon': Icons.location_on, }, { 'name': '用户代理', 'method': 'GET', 'url': 'https://httpbin.org/user-agent', 'description': '获取用户代理信息 (HTTPBin)', 'icon': Icons.info, }, { 'name': '更新用户', 'method': 'PUT', 'url': 'https://reqres.in/api/users/2', 'description': '更新用户信息 (ReqRes)', 'icon': Icons.edit, }, { 'name': '删除用户', 'method': 'DELETE', 'url': 'https://reqres.in/api/users/2', 'description': '删除用户 (ReqRes)', 'icon': Icons.delete, }, ]; @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 状态卡片 _buildStatusCard(), const SizedBox(height: 20), // 快速操作区域 _buildQuickActions(), const SizedBox(height: 20), // API端点列表 Text( 'API 端点测试', style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), ...(_endpoints.map((endpoint) => _buildEndpointCard(endpoint))), const SizedBox(height: 20), // 批量测试区域 _buildBatchTestSection(), const SizedBox(height: 20), // 使用提示 _buildHelpSection(), ], ), ); } Widget _buildStatusCard() { return Card( elevation: 4, child: Padding( padding: const EdgeInsets.all(20), child: Row( children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.blue.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.network_check, size: 32, color: Colors.blue, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '网络请求统计', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( '已发送请求: $_requestCounter', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: Colors.blue, fontWeight: FontWeight.w600, ), ), ], ), ), if (_isLoading) const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ), ], ), ), ); } Widget _buildQuickActions() { return Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '快速操作', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), Wrap( spacing: 8, runSpacing: 8, children: [ ElevatedButton.icon( onPressed: _simulateRandomRequest, icon: const Icon(Icons.shuffle, size: 18), label: const Text('随机请求'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, ), ), ElevatedButton.icon( onPressed: _simulateMultipleRequests, icon: const Icon(Icons.burst_mode, size: 18), label: const Text('批量请求'), style: ElevatedButton.styleFrom( backgroundColor: Colors.orange, ), ), ElevatedButton.icon( onPressed: _simulateErrorRequest, icon: const Icon(Icons.error_outline, size: 18), label: const Text('错误请求'), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, ), ), OutlinedButton.icon( onPressed: _clearLogs, icon: const Icon(Icons.clear_all, size: 18), label: const Text('清空日志'), ), ], ), ], ), ), ); } Widget _buildEndpointCard(Map endpoint) { return Card( margin: const EdgeInsets.only(bottom: 8), child: ListTile( leading: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: _getMethodColor(endpoint['method']).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( endpoint['icon'], color: _getMethodColor(endpoint['method']), size: 20, ), ), title: Text( endpoint['name'], style: const TextStyle(fontWeight: FontWeight.w600), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(endpoint['description']), const SizedBox(height: 4), Row( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: _getMethodColor(endpoint['method']), borderRadius: BorderRadius.circular(4), ), child: Text( endpoint['method'], style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 8), Expanded( child: Text( endpoint['url'], style: TextStyle( fontSize: 12, color: Colors.grey[600], fontFamily: 'monospace', ), overflow: TextOverflow.ellipsis, ), ), ], ), ], ), trailing: IconButton( icon: const Icon(Icons.play_arrow), onPressed: () => _simulateSpecificRequest(endpoint), tooltip: '发送请求', ), isThreeLine: true, ), ); } Widget _buildBatchTestSection() { return Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '批量测试', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), Text( '测试大量网络请求的性能和日志管理功能', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey[600], ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => _simulateBatchRequests(10), icon: const Icon(Icons.speed), label: const Text('10个请求'), ), ), const SizedBox(width: 8), Expanded( child: ElevatedButton.icon( onPressed: () => _simulateBatchRequests(50), icon: const Icon(Icons.flash_on), label: const Text('50个请求'), style: ElevatedButton.styleFrom( backgroundColor: Colors.deepOrange, ), ), ), ], ), ], ), ), ); } Widget _buildHelpSection() { return Card( color: Colors.blue.shade50, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.lightbulb_outline, color: Colors.blue.shade700), const SizedBox(width: 8), Text( '使用提示', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: Colors.blue.shade700, ), ), ], ), const SizedBox(height: 12), _buildHelpItem('1. 点击屏幕上的蓝色悬浮球打开网络检查器'), _buildHelpItem('2. 发送各种类型的网络请求查看详细信息'), _buildHelpItem('3. 在检查器中搜索和过滤特定请求'), _buildHelpItem('4. 点击日志条目查看完整的请求/响应详情'), _buildHelpItem('5. 使用批量测试验证性能和内存管理'), ], ), ), ); } Widget _buildHelpItem(String text) { return Padding( padding: const EdgeInsets.only(bottom: 4), child: Text( text, style: TextStyle( color: Colors.blue.shade700, fontSize: 13, ), ), ); } Color _getMethodColor(String method) { switch (method.toUpperCase()) { case 'GET': return Colors.green; case 'POST': return Colors.blue; case 'PUT': return Colors.orange; case 'DELETE': return Colors.red; case 'PATCH': return Colors.purple; default: return Colors.grey; } } void _simulateRandomRequest() { final endpoint = _endpoints[_random.nextInt(_endpoints.length)]; _simulateSpecificRequest(endpoint); } void _simulateSpecificRequest(Map endpoint) { setState(() { _requestCounter++; _isLoading = true; }); _sendRealHttpRequest(endpoint); } Future _sendRealHttpRequest(Map endpoint) async { final requestId = 'request_${DateTime.now().millisecondsSinceEpoch}'; final method = endpoint['method'] as String; final url = endpoint['url'] as String; final startTime = DateTime.now(); // 准备请求头 final headers = { 'Content-Type': 'application/json', 'User-Agent': 'YX-Net-Inspector-Demo/1.0.0', 'Accept': 'application/json', 'X-Request-ID': requestId, }; // 记录请求开始 YxNetInspectorController.instance.logRequest( id: requestId, method: method, url: url, headers: headers, requestData: _getRequestData(method), queryParameters: method == 'GET' ? _getQueryParameters() : null, ); try { http.Response? response; switch (method.toUpperCase()) { case 'GET': response = await http.get(Uri.parse(url), headers: headers); break; case 'POST': final requestData = _getRequestData(method); response = await http.post( Uri.parse(url), headers: headers, body: requestData != null ? json.encode(requestData) : null, ); break; case 'PUT': final requestData = _getRequestData(method); response = await http.put( Uri.parse(url), headers: headers, body: requestData != null ? json.encode(requestData) : null, ); break; case 'DELETE': response = await http.delete(Uri.parse(url), headers: headers); break; default: response = await http.get(Uri.parse(url), headers: headers); } final duration = DateTime.now().difference(startTime); // 解析响应数据 dynamic responseData; try { responseData = json.decode(response.body); } catch (e) { responseData = response.body; } // 记录响应 YxNetInspectorController.instance.logResponse( id: requestId, statusCode: response.statusCode, responseData: responseData, duration: duration, ); } catch (error) { final duration = DateTime.now().difference(startTime); // 记录错误 YxNetInspectorController.instance.logError( id: requestId, error: error.toString(), duration: duration, ); } finally { setState(() { _isLoading = false; }); } } void _simulateMultipleRequests() { for (int i = 0; i < 5; i++) { Future.delayed(Duration(milliseconds: i * 300), () { _simulateRandomRequest(); }); } } void _simulateErrorRequest() { setState(() { _requestCounter++; _isLoading = true; }); // 使用HTTPBin的状态码端点来模拟错误 final errorCodes = [400, 401, 403, 404, 500, 502, 503]; final errorCode = errorCodes[_random.nextInt(errorCodes.length)]; final errorEndpoint = { 'method': 'GET', 'url': 'https://httpbin.org/status/$errorCode', 'name': '错误请求测试', 'description': '模拟HTTP错误状态码', }; _sendRealHttpRequest(errorEndpoint); } void _simulateBatchRequests(int count) { for (int i = 0; i < count; i++) { Future.delayed(Duration(milliseconds: i * 50), () { _simulateRandomRequest(); }); } ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('正在发送 $count 个请求...'), duration: const Duration(seconds: 2), ), ); } void _clearLogs() { YxNetInspectorController.instance.clearLogs(); setState(() { _requestCounter = 0; }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('日志已清空'), duration: Duration(seconds: 1), ), ); } Map? _getRequestData(String method) { if (method == 'GET') return null; switch (method) { case 'POST': return { 'name': '张三', 'email': 'zhangsan@example.com', 'age': _random.nextInt(50) + 18, 'department': '技术部', 'skills': ['Flutter', 'Dart', 'Mobile Development'], 'metadata': { 'created_at': DateTime.now().toIso8601String(), 'source': 'demo_app', 'version': '1.0.0', } }; case 'PUT': return { 'profile': { 'avatar': 'https://example.com/avatar.jpg', 'bio': '这是一个示例用户简介', 'preferences': { 'theme': 'dark', 'language': 'zh-CN', 'notifications': true, } } }; case 'PATCH': return { 'status': 'active', 'last_login': DateTime.now().toIso8601String(), }; default: return {'action': method.toLowerCase()}; } } Map? _getQueryParameters() { return { 'page': (_random.nextInt(10) + 1).toString(), 'limit': '20', 'sort': ['name', 'created_at', 'updated_at'][_random.nextInt(3)], 'order': ['asc', 'desc'][_random.nextInt(2)], 'filter': 'active', }; } }