Flutter笔记Flutter的WidgetsBinding.instance的window属性
1. 关于 WidgetsBinding
很多初学者是不会接触和用到 WidgetsBinding.instance的,本文介绍的知识点是 WidgetsBinding.instance 的一个小部分。因此,先对 WidgetsBinding.instance 进行大概的介绍。
WidgetsBinding.instance 是Flutter中的一个核心类的实例,用于管理应用程序的事件循环和处理各种事件,例如布局、绘制、手势和系统事件。它是 WidgetsBinding 类的一个单例实例,通过它可以访问应用程序的根 WidgetsBinding 对象。
WidgetsBinding 类是 Flutter 框架的一部分,它包含了以下常用的功能和应用:
- 事件循环管理:WidgetsBinding负责管理Flutter应用程序的事件循环,它处理了各种事件的分发和调度。这包括构建(build)事件、布局(layout)事件、绘制(paint)事件等。
- 处理系统事件:WidgetsBinding 可以处理系统级事件,例如按键事件、触摸事件、指针事件等。它允许应用程序响应用户的输入和交互。
- 定时器和帧回调:WidgetsBinding允许你注册定时器和帧回调,以便在未来的时间点执行代码或在下一帧绘制前执行代码。
- 状态管理:WidgetsBinding 管理应用程序的生命周期状态,包括
inactive
、paused
、resumed
和detached
状态。这有助于应用程序在不同状态下进行适当的处理。 - 媒体和屏幕分辨率信息:WidgetsBinding 提供了访问媒体查询(MediaQuery)和屏幕信息的方法,以便根据屏幕属性调整UI。
- 错误处理:WidgetsBinding 还处理了Flutter应用程序中的异常和错误,允许你注册全局错误处理程序。
- 根`BuildContext:WidgetsBinding 提供了根 BuildContext,可以用于构建全局部件。
WidgetsBinding 是整个 Flutter 应用程序事件和生命周期管理的关键。开发者可以通过 WidgetsBinding.instance 来访问它,并注册事件回调,以便在应用程序的各个生命周期阶段执行自定义操作。例如,你可以使用 WidgetsBinding 来注册全局的错误处理程序,管理应用程序的生命周期状态,或执行定时任务。
2. window 属性概述
WidgetsBinding.instance.window是一个全局单例对象,它提供了许多用于获取和控制Flutter应用程序窗口的属性。这些属性主要用于以下方面:
- 物理尺寸(Physical Size);
- 设备像素比(Device Pixel Ratio);
- 平台亮度(Platform Brightness);
- 语言环境(Locale);
- 可访问性特性(Accessibility Features);
- 文本缩放因子(Text Scale Factor);
3. 物理尺寸(Physical Size)
window.physicalSize属性返回一个Size对象,表示窗口的物理尺寸,单位是设备独立像素。
Size size = WidgetsBinding.instance.window.physicalSize;
这个尺寸是实际像素数量,不受设备像素比例影响。
Size size = WidgetsBinding.instance.window.physicalSize; <div id="3-1"></div> ## <a href="#3-1"><font color="#037781">3.1 全屏显示</font> 如果你需要在应用中创建一个全屏的元素或者背景,你可以使用window.physicalSize来获取屏幕的尺寸,然后设置元素或者背景的尺寸。例如: ```dart Container( width: window.physicalSize.width, height: window.physicalSize.height, color: Colors.red, )
3.2 与 MediaQuery 对比
需要指出 MediaQuery 和 window.physicalSize 都可以用来获取设备的尺寸信息,但它们的使用场景和方式有所不同。
MediaQuery
MediaQuery 是Flutter的一个 组件,它可以用来获取设备的媒体查询信息,如屏幕尺寸、设备像素比、文本缩放因子等。 MediaQuery 考虑了更多的因素,如设备方向、系统UI(如状态栏和导航栏)等。在大多数情况下,你应该使用 MediaQuery 来获取设备的尺寸信息。
例如,你可以使用 MediaQuery 来获取屏幕的宽度,并根据宽度来调整布局:
double screenWidth = MediaQuery.of(context).size.width; Container( width: screenWidth / 2, height: 100, color: Colors.red, )
window.physicalSize
window.physicalSize 是一个全局单例对象,它提供了设备的物理尺寸信息。这个尺寸是设备独立的,不受设备像素比的影响。然而,window.physicalSize 没有考虑设备方向和系统UI,所以它提供的尺寸信息可能不准确。
结论
- window.physicalSize 提供的是设备的物理尺寸,这是一个固定的值,不会因为设备的方向或者系统UI的变化而改变。因此, window.physicalSize 在一些需要获取设备固定尺寸的场景中可能会有用。例如,如果你正在开发一个游戏或者图形密集型的应用,你可能需要直接操作设备的物理像素。在这种情况下, window.physicalSize 可以提供你需要的尺寸信息。
- 另一个可能的使用场景是在处理设备方向变化时。当设备的方向改变时,MediaQuery 的尺寸信息也会改变,但 window.physicalSize 不会。因此,如果你需要在设备方向改变时保持一致的尺寸信息,你可以使用 window.physicalSize。
- 然而,这些场景在大多数应用中都比较少见。
因此在大多数情况下,应该避免直接使用 window.physicalSize,而是使用 MediaQuery 和 Flutter 的布局系统来创建自适应的布局。
4. 设备像素比(Device Pixel Ratio)
window.devicePixelRatio 属性返回的是设备的像素比例,这个比例是物理像素和逻辑像素之间的比例。在高分辨率设备上,这个比例通常大于1。
double ratio = WidgetsBinding.instance.window.devicePixelRatio;
这个属性可以用于在需要精确控制像素级别的渲染或者需要处理设备像素密度差异的场景中。
4.1 处理高分辨率图像
如果你的应用需要显示高分辨率的图像,你可以使用window.devicePixelRatio 来确定需要加载的图像的分辨率。例如,如果设备的像素比例是2.0,你可以加载2x的图像以获得最佳的显示效果。
★例如,假设你有一张图像它有两个版本:一个是 1x的版本,一个是 2x的版本。你可以使用 window.devicePixelRatio 来确定应该加载哪个版本的图像。代码实现就像下面这样写:
/// 获取设备的像素比率,以确定屏幕的像素密度。 double devicePixelRatio = WidgetsBinding.instance.window.devicePixelRatio; if (devicePixelRatio >= 2.0) { // 如果像素比率大于等于2.0,加载图像的2x版本 Image.asset('images/my_image@2x.png'); } else { // 如果像素比率小于2.0,加载图像的1x版本 Image.asset('images/my_image.png'); }
上面的代码中,我们首先获取设备的像素比例。然后,如果像素比例大于或等于2.0,我们加载2x版本的图像;否则,我们加载1x版本的图像。
这样,我们可以确保在高分辨率的设备上显示高分辨率的图像,而在低分辨率的设备上显示低分辨率的图像,从而获得最佳的显示效果。
4.2 自定义绘制
在使用 CustomPaint 进行自定义绘制时,你可能需要使用 window.devicePixelRatio 来确保你的绘制在所有设备上看起来都一样。比如可以使用 window.devicePixelRatio 来调整线条的宽度或者圆角的半径。
假设你正在绘制一个有圆角的矩形,你希望圆角的半径在所有设备上看起来都一样。你可以使用 window.devicePixelRatio 来调整圆角的半径。
/// 自定义绘制器类,用于绘制一个红色的圆角矩形。 class MyPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { // 计算设备像素比,以确保在不同设备上获得一致的渲染效果。 double devicePixelRatio = WidgetsBinding.instance.window.devicePixelRatio; double radius = 10 * devicePixelRatio; // 创建一个 Paint 对象,用于定义绘制属性。 var paint = Paint() ..color = Colors.red // 设置绘制颜色为红色。 ..style = PaintingStyle.fill; // 设置绘制样式为填充。 // 创建一个覆盖整个画布尺寸的矩形。 var rect = Rect.fromLTWH(0, 0, size.width, size.height); var rrect = RRect.fromRectAndRadius(rect, Radius.circular(radius)); // 使用定义的 Paint 对象在画布上绘制一个圆角矩形。 canvas.drawRRect(rrect, paint); } @override bool shouldRepaint(CustomPainter oldDelegate) { // 我们不需要重绘,因为绘制是静态的。 return false; } }
然后这样使用:
CustomPaint( painter: MyPainter(), )
5. 平台亮度(Platform Brightness)
5.1 描述
window.platformBrightness 属性返回当前平台的亮度模式,这个模式通常由用户在设备的设置中选择。这个属性可以用来判断当前是否使用 暗黑模式。
5.2 案例
下面的例子展示了如何使用 window.platformBrightness 来判断当前是否使用暗黑模式,并根据此来设置应用的主题:
// main.dart /// 获取当前设备窗口的亮度模式,用于确定应用程序的主题模式。 Brightness brightness = WidgetsBinding.instance.window.platformBrightness; ThemeData theme; if (brightness == Brightness.dark) { // 如果亮度模式为暗模式,使用暗色主题 theme = ThemeData.dark(); } else { // 如果亮度模式为亮模式,使用亮色主题 theme = ThemeData.light(); } MaterialApp( theme: theme, // 将主题应用于应用程序 home: MyHomePage(), // 设置应用程序的主页为 MyHomePage );
这段代码我们首先获取当前平台的亮度模式:
- 如果亮度模式是Brightness.dark,我们使用暗黑主题;
- 否则,我们使用明亮主题。
通过这样,我们可以确保我们的应用的主题与用户在设备设置中选择的亮度模式一致。
6.语言环境(Locale)
6.1 描述
window.locales 属性返回一个包含当前设备所有语言环境的列表。第一个语言环境是当前设备的主要语言环境。
6.2 案例
下面用一个小例子展示如何使用 window.locales 来获取当前设备的主要语言环境,并根据此来设置应用的本地化设置:
/// 获取应用程序窗口支持的所有语言区域信息。 List<Locale> locales = WidgetsBinding.instance.window.locales; /// 从支持的语言区域列表中选择主要的语言区域。 Locale primaryLocale = locales.first; MaterialApp( locale: primaryLocale, // 设置应用程序的主要语言区域 localizationsDelegates: [ // 添加您的本地化代理(localizations delegates)在这里 ], supportedLocales: [ // 添加您支持的语言区域列表在这里 ], home: MyHomePage(), // 设置应用程序的主页为 MyHomePage );
这段代码中,我们首先获取当前设备的所有语言环境,然后取出第一个语言环境作为主要语言环境。然后,我们在MaterialApp中设置locale属性为主要语言环境。
这样,我们可以确保我们的应用的本地化设置与用户在设备设置中选择的语言环境一致。
需要指出的是,在实际开发中,可能需要根据语言环境来调整更多的文本和布局。你还需要提供本地化委托 (localizationsDelegates )和支持的语言环境列表(supportedLocales),以便Flutter可以正确地加载和显示本地化的文本。就像这样:
import 'package:flutter_localizations/flutter_localizations.dart'; const MaterialApp( localizationsDelegates: <LocalizationsDelegate<Object>>[ // ... 应用程序特定的本地化代理(localizations delegates)在这里 GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: <Locale>[ Locale('en', 'US'), // 英语 Locale('he', 'IL'), // 希伯来语 // ... 应用程序支持的其他语言区域 ], // ... )
7.可访问性特性(Accessibility Features)
7.1 描述
window.accessibilityFeatures 属性返回一个表示当前设备的可访问性特性的位掩码。这个位掩码可以用来判断当前设备是否启用了某些可访问性特性,如屏幕阅读器、大字体等。
7.2 案例
下面的例子展示如何使用 window.accessibilityFeatures 来判断当前设备是否启用了屏幕阅读器:
/// 获取窗口的辅助功能特性,用于检查设备是否启用了辅助功能。 AccessibilityFeatures accessibilityFeatures = WidgetsBinding.instance.window.accessibilityFeatures; if (accessibilityFeatures.accessibleNavigation) { // 设备启用了屏幕阅读器 print('屏幕阅读器已启用'); } else { // 设备未启用屏幕阅读器 print('屏幕阅读器未启用'); }
这段代码中,我们首先获取当前设备的可访问性特性。然后,我们检查 accessibleNavigation 是否启用。如果启用,我们打印 Screen reader enabled
否则,我们打印Screen reader disabled
。
这样,我们可以根据用户的可访问性设置来调整我们的应用的行为。例如,如果用户启用了屏幕阅读器,我们可能需要提供更多的文本描述,或者调整我们的布局和导航。
需要指出的是,在实际开发中可能需要处理更多的可访问性特性,如大字体、高对比度等。你还需要确保你的应用遵循可访问性最佳实践,以便所有用户都可以轻松地使用应用。
7.文本缩放因子(Text Scale Factor)
8.1 描述
window.textScaleFactor属性返回用户设置的文本缩放因子。这个因子用于计算字体的实际显示大小。
double scale = WidgetsBinding.instance.window.textScaleFactor;
8.2 案例
下面的例子展示如何使用 window.textScaleFactor 来获取文本缩放因子,并根据此来设置文本的大小:
double textScaleFactor = WidgetsBinding.instance.window.textScaleFactor; Text( 'Hello, world!', style: TextStyle(fontSize: 20 * textScaleFactor), );
这段代码中,我们首先获取用户设置的文本缩放因子。然后,我们将文本缩放因子乘以我们想要的字体大小(在这个例子中是20),得到实际的字体大小。然后,我们使用这个字体大小来创建一个 Text 组件。这样,我们可以确保我们的文本的大小与用户在设备设置中选择的文本大小一致。
需要指出的是,Flutter 的 Text 组件默认已经考虑了文本缩放因子,所以你通常不需要手动获取和使用window.textScaleFactor。上面的示例主要是为了演示window.textScaleFactor 的用法。——在实际开发中,你应该让 Flutter 自动处理文本缩放,以确保最佳的可访问性和用户体验。
8.API 迁移信息
注:当前Flutter积极更新,成文时版本为3.13,虽然仍违背删除。但是从 Flutter 3.7.0-32.0.pre 版本之后,window 属性已标记为弃用。通过 View.of(context) 从上下文中查找当前 FlutterView,或者直接咨询 PlatformDispatcher。 以为即将到来的 多窗口支持 做准备。
例如之前的:
Size size = WidgetsBinding.instance.window.physicalSize;
目前可以改用:
double width = View.of(context).display.size.width;
这将会在我的另外一期博客专门介绍。