鸿蒙Flutter 依赖注入模式:使用Provider管理服务类
一、依赖注入概述依赖注入Dependency Injection简称DI是一种软件设计模式用于实现控制反转Inversion of Control简称IoC。在Flutter开发中依赖注入可以帮助我们解耦组件组件不直接创建依赖降低耦合度提高可测试性方便替换为mock对象进行测试简化维护集中管理依赖易于维护和替换实现复用同一个服务可以在多个组件中复用Provider不仅可以用于状态管理还可以作为依赖注入容器管理服务类的生命周期。二、创建服务类在使用Provider进行依赖注入之前我们需要先创建服务类。服务类通常是无状态的工具类提供特定的功能。2.1 日志服务classLogService{voidinfo(Stringmessage){debugPrint([INFO]$message);}voiderror(Stringmessage,[Object?error]){debugPrint([ERROR]$message:$error);}}2.2 存储服务classStorageService{String_data;FuturevoidsaveData(Stringdata)async{awaitFuture.delayed(constDuration(milliseconds:100));_datadata;}FutureStringloadData()async{awaitFuture.delayed(constDuration(milliseconds:100));return_data;}}2.3 API服务classApiService{FutureMapString,dynamicfetchProduct(Stringid)async{awaitFuture.delayed(constDuration(seconds:1));return{id:id,name:商品$id,price:99.9,};}}三、使用Provider注入服务使用Provider注入服务非常简单只需要在MultiProvider中声明即可MultiProvider(providers:[ProviderLogService(create:(_)LogService()),ProviderStorageService(create:(_)StorageService()),ProviderApiService(create:(_)ApiService()),ChangeNotifierProvider(create:(context)DataProvider(logService:context.readLogService(),storageService:context.readStorageService(),),),],child:constMyApp(),)3.1 注入不可变服务对于无状态的服务类使用Provider即可ProviderLogService(create:(_)LogService())3.2 注入可变状态对于需要管理状态的类使用ChangeNotifierProviderChangeNotifierProvider(create:(context)DataProvider(logService:context.readLogService(),storageService:context.readStorageService(),),)四、在Provider中使用依赖在ChangeNotifier中使用依赖注入的服务classDataProviderextendsChangeNotifier{finalLogService_logService;finalStorageService_storageService;String_data初始数据;DataProvider({requiredLogServicelogService,requiredStorageServicestorageService,}):_logServicelogService,_storageServicestorageService;Stringgetdata_data;FuturevoidupdateData(StringnewData)async{_logService.info(更新数据:$newData);_datanewData;await_storageService.saveData(newData);notifyListeners();}FuturevoidloadData()async{_logService.info(加载数据);_dataawait_storageService.loadData();notifyListeners();}}五、在Widget中使用服务在Widget中使用依赖注入的服务5.1 获取服务classProductDetailPageextendsStatelessWidget{constProductDetailPage({super.key});overrideWidgetbuild(BuildContextcontext){finallogServicecontext.readLogService();returnScaffold(appBar:AppBar(title:constText(商品详情)),body:Center(child:ElevatedButton(onPressed:(){logService.info(用户点击加入购物车);context.readCartProvider().addItem(1,商品,19.9);},child:constText(加入购物车),),),);}}5.2 注意事项使用context.readT()获取不需要监听的服务使用context.watchT()获取需要监听变化的状态服务必须在当前Provider之前声明依赖注入的服务默认是单例六、单例服务与工厂模式6.1 Provider默认创建单例Provider默认创建单例实例整个应用中只创建一次MultiProvider(providers:[Provider(create:(_)ApiService()),],child:constMyApp(),)6.2 使用Provider.value如果需要传入已存在的实例可以使用Provider.valuefinalapiServiceApiService();MultiProvider(providers:[Provider.value(value:apiService),],child:constMyApp(),)6.3 延迟初始化使用ProxyProvider进行延迟初始化只有在需要时才创建ProviderMultiProvider(providers:[Provider(create:(_)ApiService()),ChangeNotifierProxyProviderApiService,CartProvider(create:(context)CartProvider(apiService:context.read()),update:(context,apiService,cartProvider){returncartProvider!..updateApiService(apiService);},),],child:constMyApp(),)七、依赖注入的优势优势说明解耦组件不直接创建依赖降低耦合可测试方便替换为mock对象进行测试可维护集中管理依赖易于维护可复用同一个服务可以在多个组件中复用生命周期管理Provider自动管理服务生命周期八、最佳实践8.1 将服务类与状态类分离服务类应该是无状态的工具类状态类应该只负责管理状态// 服务类 - 无状态classApiService{FutureUserfetchUser(Stringid)async{...}}// 状态类 - 管理状态classUserProviderextendsChangeNotifier{finalApiService_apiService;User?_user;UserProvider({requiredApiServiceapiService}):_apiServiceapiService;}8.2 使用Provider注入不可变服务对于无状态的服务使用Provider而不是ChangeNotifierProviderProviderLogService(create:(_)LogService())8.3 使用ChangeNotifierProvider管理可变状态对于需要管理状态的类使用ChangeNotifierProviderChangeNotifierProvider(create:(_)CartProvider())8.4 按依赖顺序声明Provider依赖其他Provider的Provider必须在被依赖的Provider之后声明MultiProvider(providers:[ProviderApiService(create:(_)ApiService()),// CartProvider依赖ApiService必须在ApiService之后ChangeNotifierProvider(create:(context)CartProvider(apiService:context.read()),),],child:constMyApp(),)8.5 在构造函数中注入依赖不要在方法中直接使用context.read而是在构造函数中注入依赖classCartProviderextendsChangeNotifier{finalApiService_apiService;// 正确在构造函数中注入CartProvider({requiredApiServiceapiService}):_apiServiceapiService;voidfetchProducts(){// 使用注入的依赖_apiService.fetchProducts();}}九、代码示例9.1 创建一个依赖注入演示页面classDependencyInjectionPageextendsStatelessWidget{constDependencyInjectionPage({super.key});overrideWidgetbuild(BuildContextcontext){returnMultiProvider(providers:[ProviderLogService(create:(_)LogService()),ProviderStorageService(create:(_)StorageService()),ChangeNotifierProvider(create:(context)DataProvider(logService:context.readLogService(),storageService:context.readStorageService(),),),],child:Scaffold(appBar:AppBar(title:constText(依赖注入模式演示)),body:ListView(padding:constEdgeInsets.all(16),children:[_buildSection(服务类定义,_serviceDefinition()),_buildSection(依赖注入演示,_diDemo()),_buildSection(在Provider中使用依赖,_providerWithDependencies()),_buildSection(依赖注入优势,_diAdvantages()),],),),);}Widget_buildSection(Stringtitle,Widgetcontent){returnCard(margin:constEdgeInsets.only(bottom:16),child:Padding(padding:constEdgeInsets.all(16),child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[Text(title,style:constTextStyle(fontSize:18,fontWeight:FontWeight.bold)),constSizedBox(height:12),content,],),),);}Widget_diDemo(){returnColumn(children:[ConsumerDataProvider(builder:(context,provider,child){returnText(当前数据:${provider.data},style:constTextStyle(fontSize:18),);},),constSizedBox(height:16),TextField(decoration:constInputDecoration(labelText:输入新数据),onSubmitted:(value){context.readDataProvider().updateData(value);},),constSizedBox(height:16),ElevatedButton(onPressed:()context.readDataProvider().updateData(Hello DI!),child:constText(设置Hello),),],);}}9.2 代码解析在这个演示页面中服务类定义展示了LogService、StorageService等服务类的定义方式依赖注入演示展示了如何使用MultiProvider注入多个服务在Provider中使用依赖展示了如何在DataProvider中使用注入的服务依赖注入优势总结了依赖注入的优势十、关键要点10.1 Provider可作为依赖注入容器使用Provider不仅可以管理状态还可以管理服务类的生命周期。10.2 使用Provider注入不可变服务对于无状态的服务类使用Provider即可不需要使用ChangeNotifierProvider。10.3 Provider默认创建单例实例Provider默认创建单例实例整个应用中只创建一次。10.4 服务之间可以有依赖关系一个Provider可以依赖其他Provider但必须按依赖顺序声明。10.5 便于测试和维护使用依赖注入可以方便地替换为mock对象进行测试同时也便于维护和扩展。十一、总结依赖注入是一种重要的软件设计模式可以帮助我们解耦组件、提高可测试性和可维护性。在Flutter中Provider不仅可以用于状态管理还可以作为依赖注入容器使用。在实际开发中建议遵循以下原则将服务类与状态类分离使用Provider注入不可变服务使用ChangeNotifierProvider管理可变状态按依赖顺序声明Provider在构造函数中注入依赖不要直接使用context.read希望本文能帮助你掌握依赖注入模式的使用并在实际项目中正确应用