Flutter容器管理插件dart_container的使用
Flutter容器管理插件dart_container的使用
描述
该插件为Dart语言提供了依赖注入解决方案,并且包含一个基于[dart_router_extended]的应用服务器。
特性
- 简单注入:注册对象实例。这基本上会被视为单例对象。每次注入调用都会返回相同的对象。
- 懒惰注入:注册生成对象的构建器函数。该函数将在第一次执行注入时被调用,然后在后续的注入中返回相同对象。
- 工厂注入:注册生产对象的工厂函数。
- 有资格名称的注入:容器支持有资格的注入,因此你可以为你的依赖提供名称。
- 注入配置文件:你可以为多个配置文件注册某个对象,然后根据选择的配置文件来注入或不注入值。这个功能对于想要以不同的注入配置运行应用程序非常有用。
- 值注入:将简单的命名值注入到容器中。
- Web服务器配置
- Web路由和控制器支持
- 使用路由保护进行Web路由安全
- CORS配置支持
- 计划任务支持
- 事件支持
使用方法
简单注入
var myObject = MyClass();
var myProperty = "Prop value";
// 注册一个对象到容器
$().generic(object: myObject)
// 然后注册一个值
.value("myProperty", myProperty)
// 然后注册一个有别名的对象。这也可以与构建器和工厂一起使用
.generic(object: myObject, name: "alias");
// 获取对象
MyClass injectedObject = $().get();
// 获取有别名的对象
MyClass injectedObjectAlias = $().get(name: "alias");
// 如果存在,则获取对象
MyClass? injectedObjectIfPresent = $().getIfPresent();
// 获取有别名的对象
MyClass? injectedAliasObjectIfPresent = $().getIfPresent(name: "alias");
// 也可以使用快捷方法
MyClass injectedObject = $get();
MyClass injectedObjectAlias = $get(name: "alias");
MyClass? injectedObjectIfPresent = $$get();
// 获取值
String property = $().getValue("myProperty");
String? propertyIfPresent = $().getValueIfPresent("myProperty");
// 或者使用快捷方法
String property = $val("myProperty");
String? propertyIfPresent = $$val("myProperty");
构建器和工厂注入
构建器和工厂本质上都是用于构建容器对象的方法,区别在于工厂方法每次从容器中检索对象时都会被调用,而构建器方法只在首次创建容器对象时调用一次,之后每次都返回相同的实例,使其基本上成为一个延迟构建的单例。
class SimpleObj {
final String timestamp;
SimpleObj(this.timestamp);
}
// 注册到容器
$()
// 注册一个只会被调用一次以创建容器对象的构建器函数
.generic(builder: () => MyClass())
.generic(factory: () => SimpleObj(DateTime.now().microsecondsSinceEpoch.toString()));
// 获取对象
MyClass injectedObject = $().get();
// 使用注入的工厂生成对象
SimpleObj injectedObjectIfPresent = $().getIfPresent();
// 也可以使用快捷方法
MyClass injectedObject = $get();
SimpleObj injectedObjectIfPresent = $$get();
条件回调
class SimpleObj {
final String timestamp;
SimpleObj(this.timestamp);
}
// 注册到容器
$()
// 注册一个只会被调用一次以创建容器对象的构建器函数
.generic(builder: () => MyClass())
.generic(factory: () => SimpleObj(DateTime.now().microsecondsSinceEpoch.toString()));
// 获取对象
MyClass injectedObject = $().get();
// 使用注入的工厂生成对象
SimpleObj? injectedObjectIfPresent = $().getIfPresent();
// 也可以使用快捷方法
MyClass injectedObject = $get();
SimpleObj? injectedObjectIfPresent = $$get();
// 条件回调,仅当容器中存在对象时才调用某些代码
Container().ifPresentThen<MyClass>((MyClass obj) {
print(obj);
});
// 或者使用快捷方法
$then<MyClass>((MyClass obj) {
print(obj);
});
// 条件回调。仅当容器中存在值时才调用某些代码
$().ifValuePresentThen("valueKey", (value) {
print(value);
});
// 或者使用快捷方法
$valThen("valueKey", (value) {
print(value);
});
// 条件回调,具有多个依赖项。只有当所有依赖项都找到时,容器才会调用回调
$().ifAllPresentThen([
Lookup.object(MyClass),
Lookup.object(SimpleObject),
Lookup.value("valueKey")
], (list) {
MyClass? myClass;
SimpleObject? simpleObject;
String? value;
[myClass, simpleObject, value] = list;
});
// 或者调用快捷方法
$allThen([
$look(MyClass),
$look(SimpleObject),
$lookVal("valueKey")
], (list) {
MyClass? myClass;
SimpleObject? simpleObject;
String? value;
[myClass, simpleObject, value] = list;
});
使用配置文件
var myObject = MyClass();
var myProperty = "Prop value";
// 注册到容器
$()
.generic(object: myObject, profiles: ["test", "run"])
.value("myProperty", myProperty, profiles: ["test", "run"])
// 设置活动配置文件
.profile("run");
// 获取对象。注入总是使用活动配置文件来注入任何已注册的对象或提供的值
// 如果对于活动配置文件对象不在容器中,此方法将抛出异常
MyClass injectedObject = $().get();
// 获取对象(如果存在)
// 如果对于活动配置文件对象不在容器中,此方法将返回null
MyClass? injectedObjectIfPresent = $().getIfPresent();
// 获取值。如果在活动配置文件中不存在值,此方法将抛出异常
String property = $().getValue("myProperty");
// 如果在活动配置文件中不存在值,此方法将返回null
String? propertyIfPresent = $().getValueIfPresent("myProperty");
注入接口的对象
class MyInterface {
void doSomething() {}
}
class MyClass implements MyInterface {
@override
void doSomething() {
print("Something");
}
}
var myObject = MyClass();
// 为接口而不是类型向容器注册对象
$().typed(MyInterface, object: myObject);
// 如果对于活动配置文件对象不在容器中,此方法将抛出异常
MyInterface injectedObject = $().get();
// 获取对象(如果存在)
// 如果对于活动配置文件对象不在容器中,此方法将返回null
MyInterface? injectedObjectIfPresent = $().getIfPresent();
自启动对象
class AutoStartMock implements AutoStart {
@override
void init() {
print("Init called");
}
@override
void run() {
print("Run called");
}
}
$().generic(builder: () => AutoStartMock(), autoStart: true)
// 一旦调用自启动,AutoStart对象将首先调用init方法,
// 然后异步调用run方法,以避免阻塞容器和其他功能
.autoStart();
计划任务
配置调度程序
// 将定时器轮询间隔设置为指定的持续时间
// 轮询间隔对于长时间运行的计划任务很有用。较长的时间间隔会减少CPU负载,但也会降低触发时间的准确性
// 如果未指定,默认值为10秒
$().schedulerPollingInterval(Duration(seconds: 1));
// 将所有任务的开始延迟设置为指定的持续时间
$().schedulerInitialDelay(Duration(seconds: 10));
一次性计划任务
class OneTimeScheduledJob implements ScheduledJob {
bool hasRun = false;
@override
Duration? getDuration() => Duration(seconds: 1);
@override
ScheduledJobType getType() => ScheduledJobType.oneTime;
@override
void run() {
hasRun = true;
}
@override
DateTime? getStartTime() => null;
}
// 任务将在1秒后运行,如getDuration实现所提供的
$().schedule(oneTime).autoStart();
周期性计划任务
class PeriodicScheduledJob implements ScheduledJob {
int runTimes = 0;
@override
Duration? getDuration() => Duration(seconds: 1);
@override
ScheduledJobType getType() => ScheduledJobType.periodic;
@override
DateTime? getStartTime() => null;
@override
void run() {
runTimes++;
}
}
// 立即运行,然后每秒运行一次,如getDuration实现所提供的
PeriodicScheduledJob periodic = PeriodicScheduledJob();
$().schedule(periodic).autoStart();
指定时间的一次性计划任务
class AtExactTimeScheduledJob implements ScheduledJob {
bool ran = false;
@override
Duration? getDuration() => null;
@override
DateTime? getStartTime() => DateTime.now().add(Duration(seconds: 3));
@override
ScheduledJobType getType() => ScheduledJobType.atExactTime;
@override
void run() {
ran = true;
}
}
AtExactTimeScheduledJob atTime = AtExactTimeScheduledJob();
// 任务将在3秒后运行
$().schedulerPollingInterval(Duration(seconds: 1))
.schedule(atTime)
.autoStart();
指定时间重复计划任务
class AtExactTimeRepeatingScheduledJob extends ScheduledJob {
bool ran = false;
@override
Duration? getDuration() => Duration(seconds: 2);
@override
DateTime? getStartTime() => DateTime.now().add(Duration(seconds: 3));
@override
ScheduledJobType getType() => ScheduledJobType.atExactTime;
@override
void run() {
ran = true;
}
}
AtExactTimeScheduledJob atTime = AtExactTimeScheduledJob();
// 任务将在3秒后运行,然后每隔2秒运行一次
$().schedulerPollingInterval(Duration(seconds: 1))
.schedule(atTime)
.autoStart();
Web服务器
class StausController extends Controller {
StatusController()
: super(
// 控制器下的所有路由都挂载在控制器前缀下
pathPrefix: "/status",
routes: [
GetStatusRoute(),
PostStatusRoute(),
],
// 守卫允许对路由进行安全访问。如果你不想任何人访问路由或控制器,可以实现一个守卫
guard: StatusGuard(),
);
}
class StatusGuard extends RouteGuard {
@override
bool isSecure(Request request) {
return true;
}
}
class GetStatusRoute extends AbstractRoute {
GetStatusRoute()
: super(
// 所有的路由解析完全兼容shelf_router,因为实际使用的库就是它
["/<key>"],
Method.get,
);
@override
Function buildHandler() {
return _respond;
}
Response _respond(Request req, String key) {
return JsonResponse.okJson({key: "requested"});
}
}
class PostStatusRoute extends AbstractRoute {
PostStatusRoute()
: super(
["/<key>"],
Method.post,
);
@override
Function buildHandler() {
return _respond;
}
Response _respond(Request req, String key) async {
return JsonResponse.ok({key: "posted"});
}
}
$().webServerConfig(
// 首先我们需要一个未找到处理器
(req) => Response.notFound("Route not found for ${req.method}:${req.requestedUri}"),
// 主机
'localhost',
// 端口
env.httpPort,
shared: true,
profiles: ["test", "run"],
)
// 提供控制器列表
.controllers([
StatusController();
])
// 和/或提供路由列表
.routes([
GetStatusRoute(),
PostStatusRoute(),
])
// 设置活动配置文件
.profile("run")
// 调用自启动以启动Web服务器
.autoStart();
// 如果你不想使用自启动功能但仍想启动Web服务器,你可以
Future.delayed(Duration.zero, () async => $get<WebServer>().run());
.
事件
使用dart-container
可以构建一个简单的发布/订阅框架,用于传递消息并构建反应式应用。目前支持的是精确匹配主题。
简单主题
// 首先订阅主题
$().subscribe("topic", (topic, event) {
print("Got message on topic $topic with event value $event");
});
// 然后发布到主题
$().publishEvent(["topic"], "newValue");
// 也可以订阅和发布到多个主题
// 发布给多个订阅者
$().subscribe("topic1", (topic, event) {
print("Subscriber 1 : Got message on topic $topic with event value $event");
});
$().subscribe("topic2", (topic, event) {
print("Subscriber 2 : Got message on topic $topic with event value $event");
});
// 或者使用快捷方法
// $sub("topic2", (topic, event) {
// print("Subscriber 2 : Got message on topic $topic with event value $event");
//});
// 向多个主题发布消息
// 在这种情况下,两个订阅者都将收到新消息
$().publishEvent(["topic1", "topic2"], "newValue");
// 或者使用快捷方法
// $pub(["topic1", "topic2"], "newValue");
通配符和子主题
$().subscribe("topic/*", (topic, event) {
// 将接收来自两个子主题的消息
});
// 发布到第一个子主题
$().publishEvent(["topic/subtopic1"], "newValue1");
// 发布到第二个子主题
$().publishEvent(["topic/subtopic2"], "newValue2");
$().subscribe("topic/*", (topic, event) {
// 将接收来自两个子主题的消息
});
// 发布到第一个子主题
$().publishEvent(["topic/subtopic1", "topic/subtopic1"], "newValue1");
// 重要提示:当向多个子主题发布值时,订阅者只会被调用一次,参数的主题值将是发布列表中匹配的第一个主题的值
更多关于Flutter容器管理插件dart_container的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter容器管理插件dart_container的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,dart_container
是一个假设的 Flutter 插件名称,因为实际上 Flutter 并没有一个直接名为 dart_container
的官方或广泛认可的插件。不过,我可以基于 Flutter 中容器管理的一般概念,提供一个示例代码,展示如何在 Flutter 中管理容器(如页面、对话框等)。
在 Flutter 中,容器通常指的是能够包含其他小部件(widgets)的 widget,例如 Scaffold
、Stack
、Column
、Row
等。为了模拟一个容器管理插件的功能,我们可以创建一个自定义的 Flutter 插件或者功能,用于管理这些容器。
以下是一个简化的示例,展示如何在 Flutter 中使用基本的容器管理概念。在这个例子中,我们将创建一个简单的页面导航管理,使用 Flutter 的 Navigator
来管理不同的页面(容器)。
示例代码
1. 定义主页面(Main Page)
import 'package:flutter/material.dart';
import 'second_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Container Management',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Page'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
},
child: Text('Go to Second Page'),
),
),
);
}
}
2. 定义第二个页面(Second Page)
import 'package:flutter/material.dart';
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second Page'),
),
body: Center(
child: Text('This is the second page!'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.pop(context);
},
tooltip: 'Back to Home',
child: Icon(Icons.arrow_back),
),
);
}
}
解释
- MyApp 类是应用的入口点,它创建了一个
MaterialApp
,并设置了应用的主题和主页。 - MyHomePage 类是主页面的实现,它包含一个
Scaffold
,在appBar
中显示标题,在body
中包含一个按钮。点击按钮时,使用Navigator.push
方法将第二个页面推送到导航堆栈上。 - SecondPage 类是第二个页面的实现,它同样包含一个
Scaffold
,并在appBar
中显示标题,在body
中显示文本。它还包含一个浮动操作按钮(FAB),点击该按钮时,使用Navigator.pop
方法将当前页面从导航堆栈中弹出,返回到主页面。
这个示例展示了如何在 Flutter 中使用基本的导航功能来管理不同的页面(容器)。虽然这不是一个实际的 dart_container
插件的示例,但它展示了如何在 Flutter 应用中实现类似的容器管理功能。如果你正在寻找一个特定的容器管理插件,你可能需要查看 Flutter 的 Pub.dev 网站,以找到符合你需求的第三方插件。