Flutter屏幕管理插件screen_manager的使用
Flutter屏幕管理插件screen_manager的使用
ScreenManager

为什么?
每次提到依赖注入、UI规则分离时,我们通常会直接使用第三方插件来简化这些任务。然而,我一直对使用第三方插件来实现这些功能持保留态度,因为我认为使用Flutter本身的功能就可以完成这些任务。因此,我创建了一个插件,利用Flutter本身的资源来简化依赖注入和规则分离。
注意事项
在使用这个插件之前,请注意它仍处于初期阶段,这意味着它目前并没有很多功能。基本上,我在该插件中开发的一切只是一个项目结构的标准模板。它主要是根据我在初始项目中的需求而设计的。因此,如果您正在寻找某些特定功能,可能当前版本的ScreenManager还没有这些功能。但这并不妨碍该插件在未来继续发展,以满足日常开发中的各种情况。
需要理解的要点
ScreenManager拥有一个由我自己创建的结构,用于构建应用程序的UI。让我们理解这个结构,需要注意的是,您不必严格遵循这种结构,您可以根据自己的需求选择最适合的方式。现在,让我们开始。
- lib
- home
- controller
- injection
- view
- widgets || components
- controller: 这里将包含您的视图控制器,业务逻辑,状态更新等。
- injection: 这里将包含您希望屏幕能够访问的依赖项,如数据源、仓库、用例等。
- view: 这里将包含您的视图,您在这里创建依赖项与视图之间的桥梁,也包含了您的小部件/组件的组合。
- widgets || components: 如果您喜欢,当AppBar、FloatAction出现在您的视图中时,我通常会将这些小部件或组件分离到单独的文件中,以便使它们更解耦。这里将包含您的UI的所有这些部分。
控制器
现在让我们了解控制器。在您提问之前,是的,我在创建控制器时参考了GetX插件。创建控制器非常简单,只需遵循以下代码:
class HomeController extends ScreenController {}
这只是您需要创建控制器的内容。它还具有一些可以重载的方法,示例如下:
class HomeController extends ScreenController {
@override
void onInit() {
super.onInit();
}
@override
void onReady() {
super.onReady();
}
@override
void onClose() {
super.onClose();
}
@override
void onDependencies() {
super.onDependencies();
}
}
- onInit: 此方法在StatefulWidget的
initState
中执行。 - onReady: 此方法在StatefulWidget的
build
后执行。 - onClose: 此方法在StatefulWidget的
dispose
时执行。 - onDependencies: 此方法在StatefulWidget的
didChangeDependencies
中执行。 - refresh: 此方法调用StatefulWidget的
setState
。
注入
注入是您创建对象实例的部分,您可以在之后的UI中访问这些实例。创建注入的示例如下:
class HomeInjection extends ScreenInjection<HomeController> {
HomeInjection({
Key? key,
required ScreenBridge child
}) : super(
key: key,
child: child,
controller: HomeController()
);
}
正如您所见,在注入中我们也指定了哪个控制器负责,这样ScreenBridge(即我们的依赖注入和UI之间的桥梁)就可以完成它的职责,实现控制器在UI中的注入。
在注入中,还可以在构造函数的super中定义一个名为receiveArgs
的参数,示例如下,这将在后面解释。
receiveArgs: const ScreenReceiveArgs(receive: true, identity: "homeview")
全局依赖
经过重新思考一些点,我想到了一个有趣的地方,开发者可以在应用程序中注入全局依赖项。
假设您有一个包含与sqlite连接的对象实例,在这种情况下,您需要在应用的多个地方创建依赖于该连接的数据源的新实例。在这种情况下,创建多个实例并不理想,因此在这些注入中现在有一个名为dependencies
的方法,您可以通过覆盖此方法并实例化变量来创建依赖项。
现在来看例子。
首先,创建一个包含全局依赖项的InheritedWidget。在这个例子中,我将创建一个虚构的类,代码如下:
class GlobalDependencies extends InheritedWidget {
final ConnectionSqlite connection = ConnectionSqlite();
const GlobalDependencies({
super.key,
required super.child
});
static GlobalDependencies of(BuildContext context) {
final result = context.dependOnInheritedWidgetOfExactType<GlobalDependencies>();
assert(result != null, "No injection found on context");
return result!;
}
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;
}
现在在您的main.dart
中,将这个InheritedWidget作为所有内容的父级:
runApp(
GlobalDependencies(
child: MaterialApp(
initialRoute: "/",
routes: {
FirstPage.firstPageRoute: (context) => const FirstPage(),
SecondPage.secondPageRoute:(context) => const SecondPage()
},
)
)
);
现在以HomeInjection为基础,在这个类中覆盖dependencies
方法,您会注意到这个方法需要一个上下文。该方法将在Injection被实例化时调用。
通过这个上下文,您可以调用GlobalDependencies并查找需要实例化新对象的依赖项。假设在这个我的注入中,我将实例化一个依赖于连接的新DataSource。
@override
void dependencies(BuildContext? context) {
homeDataSource = HomeDataSource(
connection: GlobalDependencies.of(context!).connection
);
}
这样,您就可以在任何依赖于例如连接的注入中创建新的实例。
最后,请记住,如果您打算使用这种方法,应该在变量前加上late
关键字,并且在创建Injection实例时应传递上下文。示例如下:
@override
FirstPageInjection build(BuildContext context) {
return FirstPageInjection(
context: context,
child: const ScreenBridge<FirstPageController, FirstPageInjection>(
child: FirstPageView(),
)
);
}
如何获取依赖项
获取Injection中的依赖项非常简单。以之前的例子为基础,我们将尝试在Controller中获取依赖项。请注意,这个依赖项也可以在Widget或View中获取。
class HomeInjection extends ScreenInjection<HomeController> {
final user = Usuario();
HomeInjection({
Key? key,
required ScreenBridge child
}) : super(
key: key,
child: child,
controller: HomeController()
);
}
正如您可以看到的,我创建了一个新的Usuario实例。假设您希望在Controller中获取这个实例,您可以通过ScreenInjection类来实现。请注意,在Controller中,这些依赖项应在onInit
、onReady
或onDependencies
方法中获取,此时上下文实际上已经被创建。
class HomeController extends ScreenController {
late final Usuario user;
@override
void onInit() {
super.onInit();
user = ScreenInjection.of<HomeInjection>(context).user;
}
}
基本上,您需要在of
方法中指定应该从哪个Injection中获取信息,这样您就可以在Controller中获得Usuario的实例了。
您还可能注意到,在Injection中包含以下方法:
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;
这个方法来自InheritedWidget。如果您希望由于Injection属性的变化而更新您的View,您可以实现这个方法以达到目的。为了更好地理解,请查阅有关InheritedWidget的文档,看看如何实现这一点。
视图
现在是时候创建您的视图了。在此部分将包含ScreenBridge、Injection和View,示例如下:
class HomeBridge extends Screen {
const HomeBridge({Key? key}) : super(key: key);
@override
HomeInjection build(BuildContext context) {
return HomeInjection(
child: const ScreenBridge<HomeController, HomeInjection>(
child: HomeView(),
)
);
}
}
class HomeView extends ScreenView<HomeController> {
const HomeView({Key? key}) : super(key: key);
@override
Scaffold build(BuildContext context) {
return const Scaffold();
}
}
正如您所看到的,在Bridge部分,您首先返回您的Injection,然后返回ScreenBridge,指示它应该使用哪个Controller和Injection来执行必要的注入和创建。然后,Bridge的子元素实际上是您的View。这个桥接是必不可少的,必须正确配置,否则无法创建View,可能会出现错误。
在View部分,只需要重载build
方法并返回一个Scaffold。每个View都需要有一个Scaffold才能执行操作,如BottomSheet等需要Scaffold的其他小部件。
一个重要点,所有的View在这种情况下都是StatefulWidget。
小部件
如果您想为您的屏幕创建小部件,并且这些小部件也需要访问UI的控制器,请注意,这些小部件是专门为UI创建的,因为它们共享同一个控制器。
class FabWidget extends ScreenWidget<HomeController> {
@override
void onInit() {
super.onInit();
print("INIT");
}
@override
void onReady() {
print("READY");
}
@override
Widget build(BuildContext context) {
super.build(context);
return FloatingActionButton(
onPressed: controller.add,
child: const Icon(Icons.add)
);
}
}
还有一些可重载的方法:
- onInit: 在返回小部件本身之前执行此方法。
- onReady: 在小部件的
build
之后调用此方法。
重要的一点是,所有继承自ScreenWidget的小部件都是无状态小部件。
在视图之间触发消息(ScreenReceiveArgs)
还记得我提到的那个您可以添加到Injection构造函数中的参数吗?就是这里,那个参数用于识别您的视图是否可见以接收调用。再次展示一个示例。
首先,在您的Injection中:
class HomeInjection extends ScreenInjection<HomeController> {
HomeInjection({
Key? key,
required ScreenBridge child
}) : super(
key: key,
child: child,
controller: HomeController(),
receiveArgs: const ScreenReceiveArgs(receive: true, identity: "homeview")
);
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;
}
正如您所见,在Home的Injection中,我们定义了它将接收调用,通过在参数receive
中传递true
,并使用一个标识符,该标识符将用于调用此视图。
之后,在我们的Controller中,我们将实现一个名为receive
的方法,或者您可以选择一个更好的名称,示例如下:
void receive(String message, dynamic value, {ScreenReceive? screen}) {
switch (message) {
case "new_people":
peoples.add(value);
break;
case "update_people":
int position = peoples.indexWhere((people) => people.id == value.id);
peoples[position] = value;
}
refresh();
}
并且在您的View中,将覆盖receive
方法:
@override
void receive(String message, value, {ScreenReceive? screen}) {
controller.receive(message, value);
}
receive
方法有三个参数:
- message: 调用的消息,例如,在我们的例子中,消息为
new_people
,update_people
,基于这条消息,您将触发不同的功能。 - value: 如果您希望向视图发送值,您将通过这个参数传递。在我们的例子中,我传递了一个类型为People的实体。
- screen: 如果您希望传递执行调用的View的实例,并创建一种逻辑,例如,如果该屏幕正在更新,则不会接受来自视图x的任何调用,您也可以以这种方式使用它。
要执行调用,非常简单,您将使用ScreenMediator
类,示例如下:
ScreenMediator.callScreen("homeview", "new_people", people);
更多关于Flutter屏幕管理插件screen_manager的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter屏幕管理插件screen_manager的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
screen_manager
是一个用于在 Flutter 应用中管理屏幕(如锁定屏幕、解锁屏幕、更改屏幕亮度等)的插件。它提供了与设备屏幕相关的各种功能,允许开发者以编程方式控制屏幕的行为。
安装
首先,你需要在 pubspec.yaml
文件中添加 screen_manager
插件的依赖:
dependencies:
flutter:
sdk: flutter
screen_manager: ^0.0.1 # 请使用最新的版本号
然后运行 flutter pub get
来安装依赖。
使用
1. 导入包
在你的 Dart 文件中导入 screen_manager
包:
import 'package:screen_manager/screen_manager.dart';
2. 初始化
在使用 screen_manager
之前,你需要初始化它:
ScreenManager screenManager = ScreenManager();
3. 屏幕锁定与解锁
你可以使用 screenManager.lock()
方法来锁定屏幕,使用 screenManager.unlock()
方法来解锁屏幕:
// 锁定屏幕
screenManager.lock();
// 解锁屏幕
screenManager.unlock();
4. 设置屏幕亮度
你可以使用 screenManager.setBrightness()
方法来设置屏幕亮度。亮度值范围是 0.0
到 1.0
,其中 0.0
表示最暗,1.0
表示最亮:
// 设置屏幕亮度为 50%
screenManager.setBrightness(0.5);
5. 获取当前屏幕亮度
你可以使用 screenManager.getBrightness()
方法来获取当前屏幕亮度:
double brightness = await screenManager.getBrightness();
print('当前屏幕亮度: $brightness');
6. 保持屏幕常亮
你可以使用 screenManager.keepOn()
方法来保持屏幕常亮:
// 保持屏幕常亮
screenManager.keepOn(true);
// 取消保持屏幕常亮
screenManager.keepOn(false);
7. 监听屏幕状态
你可以监听屏幕状态的变化,例如屏幕是否被锁定或解锁:
screenManager.onScreenStateChanged.listen((ScreenState state) {
if (state == ScreenState.locked) {
print('屏幕已锁定');
} else if (state == ScreenState.unlocked) {
print('屏幕已解锁');
}
});
示例代码
以下是一个完整的示例代码,展示了如何使用 screen_manager
插件来管理屏幕:
import 'package:flutter/material.dart';
import 'package:screen_manager/screen_manager.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: ScreenManagerExample(),
);
}
}
class ScreenManagerExample extends StatefulWidget {
[@override](/user/override)
_ScreenManagerExampleState createState() => _ScreenManagerExampleState();
}
class _ScreenManagerExampleState extends State<ScreenManagerExample> {
ScreenManager screenManager = ScreenManager();
[@override](/user/override)
void initState() {
super.initState();
screenManager.onScreenStateChanged.listen((ScreenState state) {
if (state == ScreenState.locked) {
print('屏幕已锁定');
} else if (state == ScreenState.unlocked) {
print('屏幕已解锁');
}
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen Manager Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
screenManager.lock();
},
child: Text('锁定屏幕'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
screenManager.unlock();
},
child: Text('解锁屏幕'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
screenManager.setBrightness(0.5);
},
child: Text('设置屏幕亮度为 50%'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
double brightness = await screenManager.getBrightness();
print('当前屏幕亮度: $brightness');
},
child: Text('获取当前屏幕亮度'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
screenManager.keepOn(true);
},
child: Text('保持屏幕常亮'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
screenManager.keepOn(false);
},
child: Text('取消保持屏幕常亮'),
),
],
),
),
);
}
}