Pine
为您的Flutter项目提供轻量级架构助手
如果您想支持此项目,
入门
Pine依赖以下依赖项
有了这个工具,您将使用Provider轻松定义Flutter应用程序的架构,将元素注入到widget树中,并使用BLoC作为状态管理器。
安装
此包旨在支持Flutter项目的开发。通常,将其放在dependencies下,在您的pubspec.yaml文件中
dev_dependencies:
pine: ^1.0.0
您可以从命令行安装包
flutter pub get
或者只需通过命令行添加
flutter pub add pine
工作原理
架构
元素从上到下注入。
- 添加到widget树的第一个元素是mapper,它在将来自数据层的数据转换为表示层应使用的内容时特别有用。
- 第二个元素是provider:在这里,您可以注入操作数据或访问数据的服务,如REST客户端或DAO接口。
- 第三层用于注入存储库,这些存储库通过抽象层访问数据层。
- 最后一层用于注入逻辑:Pine依赖BLoC作为状态管理器,因此我们将注入全局范围的BLoC。
每个元素可能依赖于顶层元素,并且通常从底层元素访问:例如,存储库可能需要访问REST客户端服务来收集数据,将其保存到数据库,然后将其返回给BLoC。要访问顶层项,您可以使用Provider公开的read和watch函数。
交互
用法
可以通过使用DependencyInjectorHelper widget来实现Pine架构,该widget帮助您将不同类型的元素注入到widget树中。如果您正在处理一个简单的项目,您应该将DependencyInjectorHelper直接放入您的主应用程序widget中。
示例
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => DependencyInjectorHelper(
blocs: [
BlocProvider<NewsBloc>(
create: (context) => NewsBloc(
newsRepository: context.read(),
)..fetchNews(),
),
],
mappers: [
Provider<DTOMapper<ArticleDTO, Article>>(
create: (_) => ArticleMapper(),
),
],
providers: [
Provider<Dio>(
create: (_) => Dio(),
),
Provider<NewsService>(
create: (context) => NewsService(
context.read(),
baseUrl: 'https://newsapi.org/v2/',
),
),
],
repositories: [
RepositoryProvider<NewsRepository>(
create: (context) => NewsRepositoryImpl(
newsService: context.read(),
mapper: context.read(),
),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'News App',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const HomePage(),
),
);
}
随着项目的增长,最好创建一个新的widget来将所有这些项目包装在不同的文件中。我们可以将此widget命名为DependencyInjector。 dependency_injector.dart
part 'blocs.dart';
part 'mappers.dart';
part 'providers.dart';
part 'repositories.dart';
class DependencyInjector extends StatelessWidget {
final Widget child;
const DependencyInjector({
Key? key,
required this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) => DependencyInjectorHelper(
blocs: _blocs,
providers: _providers,
mappers: _mappers,
repositories: _repositories,
child: child,
);
}
在此widget中,我们需要定义项目中所需的所有依赖项。我倾向于根据类型将这些元素分成不同的文件。在我们的示例中,我们将创建四个不同的文件,因为我们注入了blocs、mappers、providers和repositories。
blocs.dart
part of 'dependency_injector.dart';
final List<BlocProvider> _blocs = [
BlocProvider<NewsBloc>(
create: (context) => NewsBloc(
newsRepository: context.read(),
)..fetchNews(),
),
];
mappers.dart
part of 'dependency_injector.dart';
final List<SingleChildWidget> _mappers = [
Provider<DTOMapper<ArticleDTO, Article>>(
create: (_) => ArticleMapper(),
),
];
providers.dart
part of 'dependency_injector.dart';
final List<SingleChildWidget> _providers = [
Provider<Dio>(
create: (_) => Dio(),
),
Provider<NewsService>(
create: (context) => NewsService(
context.read(),
baseUrl: 'https://newsapi.org/v2/',
),
),
];
repositories.dart
part of 'dependency_injector.dart';
final List<RepositoryProvider> _repositories = [
RepositoryProvider<NewsRepository>(
create: (context) => NewsRepositoryImpl(
newsService: context.read(),
mapper: context.read(),
),
),
];
一旦我们完成了定义要注入到widget树中的全局依赖项,我们就需要如下包装我们的MaterialApp/CupertinoApp与DependencyInjector widget:
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => DependencyInjector(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'News App',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const HomePage(),
),
);
}
测试
使用DependencyInjectorHelper,可以轻松地将依赖项注入到widget树中。只需用DependencyInjectorHelper类包装您需要测试的widget,并注入您需要的依赖项。
在下面的示例中,我们将测试 HomePage widget,它依赖于 NewsBloc。在执行包含 HomePage 的 MaterialApp 之前,我们将如下包装它:
await tester.pumpWidget(
DependencyInjectorHelper(
blocs: [
BlocProvider<NewsBloc>.value(value: newsBloc),
],
child: const MaterialApp(
home: HomePage(),
),
),
);
当然,由于我们正在测试 HomePage,因此我们注入了一个模拟的 newsBloc。
许可证
Pine在MIT许可证下可用。有关更多信息,请参阅LICENSE文件。



