You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:10:38