优化Flutter服务代码质量:代码重构与最佳实践改进
Dart/Flutter代码审查与改进方案
一、CustomerService(服务类)审查与改进
反例代码(常见问题)
class CustomerService { final HttpClient client = HttpClient(); Future getCustomers() async { var url = Uri.parse('https://api.example.com/customers'); var request = await client.getUrl(url); var response = await request.close(); var responseBody = await response.transform(utf8.decoder).join(); return jsonDecode(responseBody); } }
改进点
1. 命名与类型安全
- 明确方法返回类型:将
Future getCustomers()改为Future<List<Customer>> getCustomers(),避免动态类型带来的运行时风险。 - 私有成员命名:将
client改为_httpClient,标记为私有,隐藏内部实现细节。
2. 不可变性与依赖注入
- 构造函数注入HttpClient:允许外部传入自定义实现(如测试用Mock),提升可测试性:
class CustomerService { final HttpClient _httpClient; CustomerService({HttpClient? httpClient}) : _httpClient = httpClient ?? HttpClient(); } - 定义不可变数据类
Customer:使用final字段和const构造函数,确保数据不可篡改:class Customer { final String id; final String name; final String email; const Customer({ required this.id, required this.name, required this.email, }); factory Customer.fromJson(Map<String, dynamic> json) => Customer( id: json['id'] as String, name: json['name'] as String, email: json['email'] as String, ); }
3. 错误处理增强
- 捕获并分类处理异常:自定义
CustomerFetchException,清晰区分网络错误、状态码错误、解析错误,便于UI层针对性处理:class CustomerFetchException implements Exception { final String message; final int? statusCode; CustomerFetchException(this.message, {this.statusCode}); @override String toString() => 'CustomerFetchException: $message (状态码: $statusCode)'; } Future<List<Customer>> getCustomers() async { try { const url = Uri.parse('https://api.example.com/customers'); final request = await _httpClient.getUrl(url); final response = await request.close(); if (response.statusCode != HttpStatus.ok) { throw CustomerFetchException( '获取客户数据失败', statusCode: response.statusCode, ); } final responseBody = await response.transform(utf8.decoder).join(); final List<dynamic> jsonList = jsonDecode(responseBody); return jsonList.map((json) => Customer.fromJson(json)).toList(); } on SocketException { throw CustomerFetchException('无网络连接'); } on FormatException { throw CustomerFetchException('响应格式无效'); } catch (e) { throw CustomerFetchException('意外错误: ${e.toString()}'); } }
4. 代码整洁性
- 提取API地址常量:避免硬编码,便于统一维护:
class CustomerService { static const _baseUrl = 'https://api.example.com'; static const _customersEndpoint = '$_baseUrl/customers'; // ... 其他代码 }
二、CustomerListPage(客户列表页面)审查与改进
反例代码(常见问题)
class CustomerListPage extends StatefulWidget { @override _CustomerListPageState createState() => _CustomerListPageState(); } class _CustomerListPageState extends State<CustomerListPage> { List customers = []; bool isLoading = false; @override void initState() { super.initState(); fetchCustomers(); } void fetchCustomers() async { setState(() { isLoading = true; }); var service = CustomerService(); var result = await service.getCustomers(); setState(() { customers = result; isLoading = false; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Customers')), body: isLoading ? CircularProgressIndicator() : ListView.builder( itemCount: customers.length, itemBuilder: (context, index) { return ListTile( title: Text(customers[index]['name']), subtitle: Text(customers[index]['email']), ); }, ), ); } }
改进点
1. 状态管理与依赖注入
- 类型化状态变量:将
List customers = []改为List<Customer> _customers = const [],使用不可变列表,更新时创建新列表。 - 注入服务实例:通过构造函数传入
CustomerService,避免在状态类中硬编码实例化,提升可测试性:class CustomerListPage extends StatefulWidget { final CustomerService customerService; const CustomerListPage({super.key, required this.customerService}); @override State<CustomerListPage> createState() => _CustomerListPageState(); }
2. 错误处理与用户反馈
- 添加错误状态:新增
String? _errorMessage,在UI中展示错误提示和重试按钮:class _CustomerListPageState extends State<CustomerListPage> { List<Customer> _customers = const []; bool _isLoading = false; String? _errorMessage; Future<void> fetchCustomers() async { setState(() { _isLoading = true; _errorMessage = null; }); try { final customers = await widget.customerService.getCustomers(); setState(() => _customers = customers); } on CustomerFetchException catch (e) { setState(() => _errorMessage = e.message); } catch (e) { setState(() => _errorMessage = '加载客户数据失败'); } finally { setState(() => _isLoading = false); } } }
3. UI逻辑优化
- 拆分UI构建方法:提取
_buildBody(),避免build方法过于冗长,清晰区分加载中、错误、空列表、正常列表状态:@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('客户列表')), body: _buildBody(), floatingActionButton: _isLoading ? null : FloatingActionButton( onPressed: fetchCustomers, child: const Icon(Icons.refresh), ), ); } Widget _buildBody() { if (_isLoading) { return const Center(child: CircularProgressIndicator()); } if (_errorMessage != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(_errorMessage!), const SizedBox(height: 16), ElevatedButton(onPressed: fetchCustomers, child: const Text('重试')), ], ), ); } if (_customers.isEmpty) { return const Center(child: Text('暂无客户数据')); } return ListView.builder( itemCount: _customers.length, itemBuilder: (context, index) { final customer = _customers[index]; return ListTile( key: Key(customer.id), title: Text(customer.name), subtitle: Text(customer.email), onTap: () => _navigateToDetail(context, customer), ); }, ); } void _navigateToDetail(BuildContext context, Customer customer) { Navigator.push( context, MaterialPageRoute( builder: (context) => CustomerDetailPage(customer: customer), ), ); }
4. 性能与规范优化
- 使用
const构造函数:对无状态Widget(如Text、CircularProgressIndicator)添加const,减少不必要的重建。 - 私有状态变量:所有状态变量添加下划线前缀(
_customers、_isLoading),明确私有属性。
5. 可测试性
- 测试时传入Mock服务:模拟不同场景(成功加载、加载失败、空列表),验证UI状态是否正确:
void main() { testWidgets('加载中显示进度指示器', (tester) async { final mockService = MockCustomerService(); when(mockService.getCustomers()).thenAnswer((_) => Future.delayed(const Duration(milliseconds: 100), () => [])); await tester.pumpWidget(MaterialApp(home: CustomerListPage(customerService: mockService))); expect(find.byType(CircularProgressIndicator), findsOneWidget); }); }
内容的提问来源于stack exchange,提问作者Henrique Polizer
相关产品推荐
相关产品推荐

