手把手带你写flutter:1、网络框架

93 阅读4分钟

1. 架构概述

本网络框架基于 GetX 和 Dio 实现,采用分层架构设计,实现了请求拦截、统一错误处理、国际化支持等功能。

1.1 架构图


graph TD
    A[UI Layer] --> B[Controller Layer]
    B --> C[Service Layer]
    C --> D[Network Layer]
    D --> E[HTTP Client/Dio]
    
    F[Interceptors] --> D
    G[Error Handler] --> D
    H[Config] --> D

1.2 数据流图

sequenceDiagram
    participant UI
    participant Controller
    participant ApiService
    participant HttpUtil
    participant Server

    UI->>Controller: 触发请求
    Controller->>ApiService: 调用API方法
    ApiService->>HttpUtil: 执行HTTP请求
    Note over HttpUtil: 添加请求头和签名
    HttpUtil->>Server: 发送请求
    Server-->>HttpUtil: 返回响应
    Note over HttpUtil: 错误处理/响应拦截
    HttpUtil-->>ApiService: 返回处理后的数据
    ApiService-->>Controller: 返回业务模型
    Controller-->>UI: 更新界面

2. 核心组件

2.1 配置层 (Configuration Layer)

2.1.1 基础配置

  • 环境配置
  enum Environment { dev, test, prod }
  
  class HttpConfig {
    static Environment environment = Environment.dev;
    
    static String get baseUrl {
      switch (environment) {
        case Environment.dev:
          return 'https://dev-api.example.com';
        case Environment.test:
          return 'https://test-api.example.com';
        case Environment.prod:
          return 'https://api.example.com';
      }
    }
  }

2.1.2 安全配置

  • 证书配置
  class SecurityConfig {
    static String? certificatePath;
    static bool validateCertificate = true;
    
    static SecurityContext? getSecurityContext() {
      if (certificatePath != null) {
        final context = SecurityContext.defaultContext;
        context.setTrustedCertificates(certificatePath!);
        return context;
      }
      return null;
    }
  }

2.1.3 缓存配置

class CacheConfig {
  static const int maxAge = 7200; // 缓存有效期(秒)
  static const int maxCount = 100; // 最大缓存数量
  
  static bool shouldCache(String path) {
    // 配置需要缓存的接口
    return path.startsWith('/api/config') || 
           path.startsWith('/api/static');
  }
}

2.2 网络层 (Network Layer)

2.2.1 请求重试机制

class RetryInterceptor extends Interceptor {
  final int maxRetries;
  final Duration retryDelay;
  
  RetryInterceptor({
    this.maxRetries = 3,
    this.retryDelay = const Duration(seconds: 1),
  });
  
  @override
  Future onError(DioError err, ErrorInterceptorHandler handler) async {
    var extra = err.requestOptions.extra;
    var retryCount = extra['retryCount'] ?? 0;
    
    if (_shouldRetry(err) && retryCount < maxRetries) {
      await Future.delayed(retryDelay);
      var options = err.requestOptions;
      options.extra['retryCount'] = retryCount + 1;
      
      try {
        final response = await dio.fetch(options);
        return handler.resolve(response);
      } catch (e) {
        return handler.next(err);
      }
    }
    return handler.next(err);
  }
  
  bool _shouldRetry(DioError error) {
    return error.type == DioErrorType.connectionTimeout ||
           error.type == DioErrorType.receiveTimeout;
  }
}

2.2.2 缓存实现

class CacheInterceptor extends Interceptor {
  final Cache cache;
  
  CacheInterceptor(this.cache);
  
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (options.method == 'GET' && CacheConfig.shouldCache(options.path)) {
      final cacheData = cache.get(options.uri.toString());
      if (cacheData != null) {
        return handler.resolve(
          Response(
            requestOptions: options,
            data: cacheData,
            statusCode: 200,
          ),
        );
      }
    }
    handler.next(options);
  }
  
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    if (response.requestOptions.method == 'GET' && 
        CacheConfig.shouldCache(response.requestOptions.path)) {
      cache.set(
        response.requestOptions.uri.toString(),
        response.data,
        CacheConfig.maxAge,
      );
    }
    handler.next(response);
  }
}

2.3 服务层 (Service Layer)

2.3.1 响应数据处理

class ApiResponse<T> {
  final int code;
  final String message;
  final T? data;
  final bool success;
  
  ApiResponse({
    required this.code,
    required this.message,
    this.data,
    this.success = false,
  });
  
  factory ApiResponse.fromJson(Map<String, dynamic> json, T Function(Map<String, dynamic>) fromJson) {
    return ApiResponse(
      code: json['code'] ?? -1,
      message: json['message'] ?? '',
      success: json['code'] == 200,
      data: json['data'] == null ? null : fromJson(json['data']),
    );
  }
}

2.3.2 业务异常处理

class BusinessException implements Exception {
  final String message;
  final int code;
  
  BusinessException({
    required this.message,
    required this.code,
  });
  
  @override
  String toString() => 'BusinessException: $message (code: $code)';
}

class ApiService extends GetxService {
  Future<T> handleResponse<T>(Future<Response> Function() request) async {
    try {
      final response = await request();
      final apiResponse = ApiResponse.fromJson(
        response.data,
        (json) => json as T,
      );
      
      if (!apiResponse.success) {
        throw BusinessException(
          message: apiResponse.message,
          code: apiResponse.code,
        );
      }
      
      return apiResponse.data as T;
    } on DioError catch (e) {
      throw _handleDioError(e);
    } catch (e) {
      rethrow;
    }
  }
}

3. 关键特性

3.1 安全性

3.1.1 请求防重放

class NonceManager {
  static final _instance = NonceManager._internal();
  factory NonceManager() => _instance;
  NonceManager._internal();
  
  final _usedNonces = <String>{};
  final _lock = Lock();
  
  Future<bool> validateNonce(String nonce, String timestamp) async {
    return await _lock.synchronized(() async {
      // 检查时间戳是否在有效期内
      final requestTime = int.parse(timestamp);
      final now = DateTime.now().millisecondsSinceEpoch;
      if ((now - requestTime).abs() > 300000) { // 5分钟有效期
        return false;
      }
      
      // 检查nonce是否已使用
      if (_usedNonces.contains(nonce)) {
        return false;
      }
      
      _usedNonces.add(nonce);
      // 清理过期的nonce
      _cleanExpiredNonces();
      return true;
    });
  }
  
  void _cleanExpiredNonces() {
    // 清理逻辑
  }
}

3.2 可维护性

  • 统一的 API 管理

  • 规范的错误处理

  • 完善的日志系统

3.3 扩展性

  • 支持自定义拦截器

  • 灵活的配置项

  • 易于添加新接口

3.3.1 认证拦截器

class AuthInterceptor extends Interceptor {
  final AuthService _authService = Get.find<AuthService>();
  final NavigationService _navigationService = Get.find<NavigationService>();

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // 添加token到请求头
    final token = _authService.token;
    if (token != null) {
      options.headers['Authorization'] = 'Bearer $token';
    }
    handler.next(options);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    if (err.response?.statusCode == 401) {
      // 清除本地token
      _authService.clearToken();
      
      // 取消所有请求
      CancelTokenManager().cancelAll();
      
      // 保存当前路由
      final currentRoute = Get.currentRoute;
      
      // 跳转到登录页
      _navigationService.toLogin().then((_) {
        // 登录成功后返回原页面
        if (_authService.isLoggedIn) {
          Get.offAllNamed(currentRoute);
        }
      });
      
      return handler.reject(err);
    }
    handler.next(err);
  }
}

3.3.2 认证服务

class AuthService extends GetxService {
  final _storage = Get.find<StorageService>();
  final _isLoggedIn = false.obs;
  String? _token;

  bool get isLoggedIn => _isLoggedIn.value;
  String? get token => _token;

  @override
  void onInit() {
    super.onInit();
    _loadToken();
  }

  Future<void> _loadToken() async {
    _token = await _storage.getToken();
    _isLoggedIn.value = _token != null;
  }

  Future<void> setToken(String token) async {
    _token = token;
    await _storage.saveToken(token);
    _isLoggedIn.value = true;
  }

  Future<void> clearToken() async {
    _token = null;
    await _storage.removeToken();
    _isLoggedIn.value = false;
  }
}

3.3.3 导航服务

class NavigationService extends GetxService {
  // 存储需要登录的路由
  static final Set<String> _authRequiredRoutes = {
    Routes.PROFILE,
    Routes.SETTINGS,
    // ... 其他需要登录的路由
  };

  // 路由守卫
  Future<bool> canNavigate(String route) async {
    final authService = Get.find<AuthService>();
    if (_authRequiredRoutes.contains(route) && !authService.isLoggedIn) {
      await toLogin();
      return false;
    }
    return true;
  }

  // 跳转到登录页
  Future<void> toLogin() async {
    await Get.toNamed(Routes.LOGIN);
  }
}

3.3.3 路由配置

class AppPages {
  static final routes = [
    GetPage(
      name: Routes.HOME,
      page: () => HomePage(),
      middlewares: [AuthGuard()],
    ),
    GetPage(
      name: Routes.PROFILE,
      page: () => ProfilePage(),
      middlewares: [AuthGuard()],
    ),
    GetPage(
      name: Routes.LOGIN,
      page: () => LoginPage(),
    ),
  ];
}

class AuthGuard extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    final authService = Get.find<AuthService>();
    return !authService.isLoggedIn && route != Routes.LOGIN
        ? RouteSettings(name: Routes.LOGIN)
        : null;
  }
}

3.4 易用性

  • 简单的调用方式

  • 统一的错误提示

  • 支持进度回调

3.4.1 请求取消管理

class CancelTokenManager {
  static final CancelTokenManager _instance = CancelTokenManager._internal();
  factory CancelTokenManager() => _instance;
  CancelTokenManager._internal();

  // 存储所有请求的CancelToken
  final Map<String, CancelToken> _tokens = {};

  // 创建新的CancelToken
  CancelToken createToken(String tag) {
    cancelToken(tag); // 如果已存在,先取消旧的
    final token = CancelToken();
    _tokens[tag] = token;
    return token;
  }

  // 取消指定请求
  void cancelToken(String tag) {
    if (_tokens.containsKey(tag)) {
      if (!_tokens[tag]!.isCancelled) {
        _tokens[tag]!.cancel('Request cancelled');
      }
      _tokens.remove(tag);
    }
  }

  // 取消所有请求
  void cancelAll() {
    _tokens.forEach((key, token) {
      if (!token.isCancelled) {
        token.cancel('All requests cancelled');
      }
    });
    _tokens.clear();
  }
}

3.4.2 使用示例

class HomeController extends GetxController {
  final _apiService = Get.find<ApiService>();
  final _cancelManager = CancelTokenManager();

  Future<void> fetchData() async {
    try {
      final token = _cancelManager.createToken('fetchData');
      final result = await _apiService.getData(cancelToken: token);
      // 处理数据
    } catch (e) {
      if (e is DioException && CancelToken.isCancel(e)) {
        print('Request was cancelled');
      }
    }
  }

  void cancelFetch() {
    _cancelManager.cancelToken('fetchData');
  }

  @override
  void onClose() {
    _cancelManager.cancelAll();
    super.onClose();
  }
}

4. 详细设计

4.1 目录结构

lib/
├── core/                           
│   ├── network/                    # 网络相关
│   │   ├── config/                
│   │   │   ├── http_config.dart    # HTTP配置
│   │   │   └── api_endpoints.dart  # API端点
│   │   │
│   │   ├── interceptors/          
│   │   │   ├── auth_interceptor.dart      # 认证拦截器
│   │   │   └── error_interceptor.dart     # 错误拦截器
│   │   │
│   │   └── http_util.dart         # 网络工具类
│   │
│   └── utils/                     
│       └── token_manager.dart     # Token管理
│
├── services/                      
│   ├── api_service.dart           # API服务
│   ├── auth_service.dart          # 认证服务
│   └── storage_service.dart       # 存储服务
│
├── i18n/                          # 国际化
│   └── translations.dart          
│
├── routes/                        # 路由
│   ├── app_pages.dart            
│   └── app_routes.dart           
│
├── models/                        # 数据模型
│   └── response/
│       └── api_response.dart
│
└── main.dart                      # 入口文件

注意事项:1. 所有网络请求必须通过 ApiService 进行

4.2 核心组件

4.2.1 配置层 (HttpConfig)

  • 基础URL配置

  • 超时设置

  • 请求头管理

  • 安全签名生成

4.2.2 网络层 (HttpUtil)

  • 请求方法封装

  • 拦截器管理

  • 错误处理

  • 文件下载

4.2.3 服务层 (ApiService)

  • 业务接口封装

  • 数据模型转换

  • 统一调用入口

5. 代码示例

5.1 网络配置

import 'dart:convert';
import 'package:crypto/crypto.dart';

class HttpConfig {
  // 基础配置
  static const String baseUrl = 'https://api.example.com';
  static const int connectTimeout = 15000;
  static const int receiveTimeout = 15000;
  static const String apiVersion = '/v1';
  
  // 基础请求头
  static Map<String, dynamic> headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  // 密钥配置
  static const String secretKey = 'YOUR_SECRET_KEY'; // 实际项目中应从安全位置获取
  
  // 获取加密请求头
  static Map<String, dynamic> getEncryptHeaders(String method, dynamic body) {
    final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
    final nonce = _generateNonce();
    final sign = generateSign(method, body, timestamp, nonce);
    
    return {
      ...headers,
      'X-Timestamp': timestamp,
      'X-Nonce': nonce,
      'X-Sign': sign,
    };
  }

  // 生成随机字符串
  static String _generateNonce() {
    const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
    final random = Random();
    return List.generate(16, (index) => chars[random.nextInt(chars.length)]).join();
  }

  // 生成签名
  static String generateSign(String method, dynamic body, String timestamp, String nonce) {
    String content = '';
    if (body != null) {
      if (body is Map) {
        content = json.encode(body);
      } else if (body is String) {
        content = body;
      }
    }
    
    final String rawStr = method + content + timestamp + nonce + secretKey;
    return md5.convert(utf8.encode(rawStr)).toString();
  }
}

5.2 网络工具类

import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'http_config.dart';

class HttpUtil {
  static HttpUtil get instance => _instance;
  static final HttpUtil _instance = HttpUtil._internal();
  late Dio dio;
  
  HttpUtil._internal() {
    _initDio();
  }
  
  void _initDio() {
    dio = Dio(BaseOptions(
      baseUrl: HttpConfig.baseUrl,
      connectTimeout: Duration(milliseconds: HttpConfig.connectTimeout),
      receiveTimeout: Duration(milliseconds: HttpConfig.receiveTimeout),
      headers: HttpConfig.headers,
    ));
    
    _addInterceptors();
  }
  
  void _addInterceptors() {
    dio.interceptors.add(InterceptorsWrapper(
      onRequest: _requestInterceptor,
      onResponse: _responseInterceptor,
      onError: _errorInterceptor,
    ));
    
    // 添加日志拦截器
    if (kDebugMode) {
      dio.interceptors.add(LogInterceptor(
        requestBody: true,
        responseBody: true,
      ));
    }
  }
  
  void _requestInterceptor(RequestOptions options, RequestInterceptorHandler handler) {
    // 添加加密请求头
    options.headers.addAll(
      HttpConfig.getEncryptHeaders(options.method, options.data),
    );
    
    // 添加token
    final token = Get.find<StorageService>().getToken();
    if (token != null) {
      options.headers['Authorization'] = 'Bearer $token';
    }
    
    handler.next(options);
  }
  
  void _responseInterceptor(Response response, ResponseInterceptorHandler handler) {
    // 统一响应处理
    if (response.data['code'] == 200) {
      handler.next(response);
    } else {
      handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          response: response,
          error: response.data['message'] ?? 'error.unknown'.tr,
        ),
      );
    }
  }
  
  void _errorInterceptor(DioException error, ErrorInterceptorHandler handler) {
    // 错误处理
    final errorMessage = _getLocalizedErrorMessage(error);
    Get.snackbar('error.title'.tr, errorMessage);
    handler.next(error);
  }
  
  String _getLocalizedErrorMessage(DioException error) {
    String key = 'error.unknown';
    
    switch (error.type) {
      case DioExceptionType.connectionTimeout:
        key = 'error.connectiontimeout';
        break;
      case DioExceptionType.receiveTimeout:
        key = 'error.receivetimeout';
        break;
      case DioExceptionType.badResponse:
        key = 'error.badresponse${error.response?.statusCode ?? ""}';
        break;
      // ... 其他错误类型
    }
    
    return key.tr;
  }
  
  // GET请求
  Future<T?> get<T>(
    String path, {
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await dio.get(
        path,
        queryParameters: params,
        options: options,
        cancelToken: cancelToken,
      );
      return response.data;
    } catch (e) {
      rethrow;
    }
  }
  
  // POST请求
  Future<T?> post<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await dio.post(
        path,
        data: data,
        queryParameters: params,
        options: options,
        cancelToken: cancelToken,
      );
      return response.data;
    } catch (e) {
      rethrow;
    }
  }
  
  // PUT请求
  Future<T?> put<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await dio.put(
        path,
        data: data,
        queryParameters: params,
        options: options,
        cancelToken: cancelToken,
      );
      return response.data;
    } catch (e) {
      rethrow;
    }
  }
  
  // DELETE请求
  Future<T?> delete<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await dio.delete(
        path,
        data: data,
        queryParameters: params,
        options: options,
        cancelToken: cancelToken,
      );
      return response.data;
    } catch (e) {
      rethrow;
    }
  }
  
  // 下载文件
  Future<String?> download(
    String url,
    String savePath, {
    ProgressCallback? onReceiveProgress,
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      await dio.download(
        url,
        savePath,
        onReceiveProgress: onReceiveProgress,
        queryParameters: params,
        options: options,
        cancelToken: cancelToken,
      );
      return savePath;
    } catch (e) {
      rethrow;
    }
  }
}

5.3 API管理

class ApiEndpoints {
  // Auth
  static const String auth = '/auth';
  static const String login = '$auth/login';
  static const String register = '$auth/register';
  static const String logout = '$auth/logout';
  
  // User
  static const String user = '/user';
  static const String profile = '$user/profile';
  static const String updateProfile = '$user/update';
  
  // File
  static const String file = '/file';
  static const String upload = '$file/upload';
  static const String download = '$file/download';
}

5.4 API服务类

class ApiService extends GetxService {
  final _http = HttpUtil.instance;
  
  // Auth APIs
  Future<UserModel?> login(String username, String password) async {
    try {
      final response = await _http.post(
        ApiEndpoints.login,
        data: {
          'username': username,
          'password': password,
        },
      );
      return response != null ? UserModel.fromJson(response) : null;
    } catch (e) {
      rethrow;
    }
  }
  
  // User APIs
  Future<UserModel?> getUserProfile() async {
    try {
      final response = await _http.get(ApiEndpoints.profile);
      return response != null ? UserModel.fromJson(response) : null;
    } catch (e) {
      rethrow;
    }
  }
  
  Future<UserModel?> updateProfile(Map<String, dynamic> data) async {
    try {
      final response = await _http.put(
        ApiEndpoints.updateProfile,
        data: data,
      );
      return response != null ? UserModel.fromJson(response) : null;
    } catch (e) {
      rethrow;
    }
  }
  
  // File APIs
  Future<String?> downloadFile(
    String fileId,
    String savePath, {
    ProgressCallback? onReceiveProgress,
  }) async {
    try {
      return await _http.download(
        '${ApiEndpoints.download}/$fileId',
        savePath,
        onReceiveProgress: onReceiveProgress,
      );
    } catch (e) {
      rethrow;
    }
  }
}

5.5 国际化支持

import 'package:get/get.dart';

class AppTranslations extends Translations {
  @override
  Map<String, Map<String, String>> get keys => {
    'en_US': {
      'error.title': 'Error',
      'error.unknown': 'Unknown error',
      'error.connectiontimeout': 'Connection timeout',
      'error.receivetimeout': 'Receive timeout',
      'error.badresponse401': 'Unauthorized, please login again',
      'error.badresponse403': 'Access denied',
      'error.badresponse404': 'Resource not found',
      'error.badresponse500': 'Internal server error',
    },
    'zh_CN': {
      'error.title': '错误',
      'error.unknown': '未知错误',
      'error.connectiontimeout': '连接超时',
      'error.receivetimeout': '接收超时',
      'error.badresponse401': '未授权,请重新登录',
      'error.badresponse403': '拒绝访问',
      'error.badresponse404': '请求错误,未找到该资源',
      'error.badresponse500': '服务器内部错误',
    },
  };
}

5.6 使用示例

class LoginController extends GetxController {
  final _apiService = Get.find<ApiService>();
  final username = ''.obs;
  final password = ''.obs;
  final isLoading = false.obs;
  
  Future<void> login() async {
    if (username.isEmpty || password.isEmpty) {
      Get.snackbar('提示', '请输入用户名和密码');
      return;
    }
    
    try {
      isLoading(true);
      final user = await _apiService.login(
        username.value,
        password.value,
      );
      
      if (user != null) {
        Get.offAllNamed(Routes.HOME);
      }
    } finally {
      isLoading(false);
    }
  }
}

5.7 文件下载示例

class FileController extends GetxController {
  final _apiService = Get.find<ApiService>();
  final downloadProgress = 0.0.obs;
  
  Future<void> downloadFile(String fileId) async {
    try {
      final savePath = await getApplicationDocumentsDirectory();
      final filePath = '${savePath.path}/downloaded_file.pdf';
      
      await _apiService.downloadFile(
        fileId,
        filePath,
        onReceiveProgress: (received, total) {
          if (total != -1) {
            downloadProgress(received / total);
          }
        },
      );
      
      Get.snackbar('Success', 'File downloaded successfully');
    } catch (e) {
      // 错误已在 HttpUtil 中统一处理
    }
  }
}

5.8 初始化配置

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await initServices();
  await initConfigs();
  
  runApp(MyApp());
}

/// 初始化服务
Future<void> initServices() async {
  try {
    // 按依赖顺序初始化
    await Get.putAsync(() => StorageService().init());
    await Get.putAsync(() => AuthService().init());
    await Get.putAsync(() => NavigationService().init());
    await Get.putAsync(() => ApiService().init());
    
    debugPrint('所有服务初始化完成');
  } catch (e) {
    debugPrint('服务初始化失败: $e');
    rethrow;
  }
}

/// 初始化配置
Future<void> initConfigs() async {
  try {
    // 设置环境
    HttpConfig.environment = Environment.dev;
    
    // 加载本地存储的配置
    final storage = Get.find<StorageService>();
    final savedLocale = await storage.getLocale();
    if (savedLocale != null) {
      Get.updateLocale(savedLocale);
    }
    
    debugPrint('所有配置初始化完成');
  } catch (e) {
    debugPrint('配置初始化失败: $e');
    rethrow;
  }
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'App Name',
      // 国际化配置
      translations: AppTranslations(),
      locale: Get.deviceLocale,
      fallbackLocale: const Locale('zh', 'CN'),
      
      // 路由配置
      initialRoute: Routes.INITIAL,
      getPages: AppPages.routes,
      
      // 主题配置
      theme: AppTheme.light,
      darkTheme: AppTheme.dark,
      
      // 全局绑定
      initialBinding: AppBinding(),
      
      // 路由观察者
      navigatorObservers: [
        GetObserver((route) async {
          if (route is GetPageRoute) {
            final canNavigate = await Get.find<NavigationService>()
                .canNavigate(route.routeName!);
            if (!canNavigate) {
              return const RouteSettings(name: Routes.LOGIN);
            }
          }
          return null;
        }),
      ],
      
      // 错误处理
      onUnknownRoute: (settings) {
        return GetPageRoute(
          page: () => const NotFoundPage(),
        );
      },
      
      // 调试配置
      debugShowCheckedModeBanner: false,
    );
  }
}

/// 全局依赖绑定
class AppBinding extends Bindings {
  @override
  void dependencies() {
    // 注入全局控制器
    Get.put(GlobalController());
    Get.put(ThemeController());
    // ... 其他全局控制器
  }
}

/// 全局控制器示例
class GlobalController extends GetxController {
  final _authService = Get.find<AuthService>();
  
  @override
  void onInit() {
    super.onInit();
    // 监听登录状态
    ever(_authService.isLoggedIn, _handleAuthChanged);
  }
  
  void _handleAuthChanged(bool isLoggedIn) {
    if (!isLoggedIn) {
      Get.offAllNamed(Routes.LOGIN);
    }
  }
}

/// 主题控制器示例
class ThemeController extends GetxController {
  final _storage = Get.find<StorageService>();
  final isDarkMode = false.obs;
  
  @override
  void onInit() {
    super.onInit();
    _loadThemeMode();
  }
  
  Future<void> _loadThemeMode() async {
    final isDark = await _storage.getDarkMode();
    isDarkMode.value = isDark ?? false;
  }
  
  Future<void> toggleTheme() async {
    isDarkMode.value = !isDarkMode.value;
    await _storage.saveDarkMode(isDarkMode.value);
    Get.changeThemeMode(isDarkMode.value ? ThemeMode.dark : ThemeMode.light);
  }
}

6. END