在Flutter中监听方向状态:旋转前后。

13

OrientationBuilder 在全面的转换完成后才报告方向更改,然后重建发生在此之后。

有没有一种方法在方向启动之前就开始操作? 我不是要预先旋转,而是同时进行更改,而不是在之后。

目标:

  1. 设备已旋转。
  2. 检测到并重新构建UI以显示覆盖层。
  3. Flutter执行自己的转换,将UI旋转到新方向。
  4. 经过一段固定的时间后,简单地重建以隐藏覆盖层。

挑战在于如何在第三步发生之前实现第2点?


不,这是不可行的。你想要做什么? - Rémi Rousselet
我想在屏幕转换/旋转时加一个面纱,因为它看起来很糟糕。想象一下当您在 iPadOS 上调整应用程序大小时,它会因重新缩放而使内容模糊。这是一个简单的效果,但我不知道如何在变换(不是之后)同时应用此效果。 - Josh Kahane
尝试使用MediaQuery,不确定是否有效。 - Arsh Shaikh
我认为这仍然涉及到相同的问题。在方向更改之前无法获取该信息并重新构建。 - Josh Kahane
@RémiRousselet 你是如何确定这不可行的? - creativecreatorormaybenot
OrientationBuilder基于LayoutBuilder而不是MediaQuery。屏幕尺寸未变的情况下,方向理论上是可以改变的。 - Rémi Rousselet
3个回答

20

您可以通过覆盖 didChangeMetrics,使用 WidgetsBindingObserver 来提前获取屏幕方向变更。

如何使用 didChangeMetrics

您只需将 WidgetBindingObserver 混入有状态组件的 State 实现中即可:

class _FooState extends State with WidgetsBindingObserver {
  @override
  void didChangeMetrics() {
    // This will be triggered by changes in orientation.
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
}

确定方向

方向由可用尺寸的宽高比决定。这意味着你可以使用以下代码在didChangeMetrics中获取方向:

final orientation = WidgetsBinding.instance.window.physicalSize
    .aspectRatio > 1 ? Orientation.landscape : Orientation.portrait;

示例

我构建了一个示例 StatefulWidget,将 OrientationBuilder 回调与 didChangeMetrics 进行比较:

import 'package:flutter/material.dart';

void main() {
  runApp(OrientationListener());
}

class OrientationListener extends StatefulWidget {
  @override
  _OrientationListenerState createState() => _OrientationListenerState();
}

class _OrientationListenerState extends State<OrientationListener>
    with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    print('$WidgetsBindingObserver metrics changed ${DateTime.now()}: '
        '${WidgetsBinding.instance.window.physicalSize.aspectRatio > 1 ? Orientation.landscape : Orientation.portrait}');
  }

  @override
  Widget build(BuildContext context) {
    return MediaQuery(
      data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
      child: OrientationBuilder(
        builder: (context, orientation) {
          print('$OrientationBuilder rebuild ${DateTime.now()}: $orientation');

          return Container();
        },
      ),
    );
  }
}

结果

运行此示例会显示两个函数的以下时间:

WidgetsBindingObserver metrics changed 2020-08-22 14:47:01.690172: Orientation.portrait
WidgetsBindingObserver metrics changed 2020-08-22 14:47:01.706574: Orientation.landscape
OrientationBuilder rebuild 2020-08-22 14:47:01.760589: Orientation.landscape
WidgetsBindingObserver metrics changed 2020-08-22 14:47:06.537083: Orientation.landscape
WidgetsBindingObserver metrics changed 2020-08-22 14:47:06.549545: Orientation.portrait
OrientationBuilder rebuild 2020-08-22 14:47:06.603859: Orientation.portrait
WidgetsBindingObserver metrics changed 2020-08-22 14:47:10.423787: Orientation.portrait
WidgetsBindingObserver metrics changed 2020-08-22 14:47:10.442866: Orientation.landscape
OrientationBuilder rebuild 2020-08-22 14:47:10.501729: Orientation.landscape
WidgetsBindingObserver metrics changed 2020-08-22 14:47:13.639545: Orientation.landscape
WidgetsBindingObserver metrics changed 2020-08-22 14:47:13.658906: Orientation.landscape
WidgetsBindingObserver metrics changed 2020-08-22 14:47:13.672025: Orientation.portrait
OrientationBuilder rebuild 2020-08-22 14:47:13.730771: Orientation.portrait

针对我的情况,检测差异约为0.06秒。

观察结果

从上面可以看出来,这个差异是微不足道的(我认为)。所以我甚至不确定这是否对你有用。

此外,我观察到在设备旋转开始时实际上会调用OrientationBuilder回调 - 至少在我的 Android 模拟器上是如此。这意味着您可以在旋转发生之前重新构建您的 UI。


希望这对您有所帮助 :)


非常感谢您在这里提供了详细的分析,但是尽管在轮换开始时进行了注册,重建实际上直到轮换完成后才会发生/完成。例如,向您的“容器”添加颜色。在didChangeMetrics中,使用setState更改布尔值isRotating = true。为了测试,我只需在1-2秒后使用Timer将其设置回false。使用布尔值更改颜色,但颜色直到旋转后才会更改。 - Josh Kahane
@JoshKahane 是的,没错 :) 这是因为构建将与 OrientationBuilder 同时发生。看起来你可以在那个时候不进行绘制 (: - creativecreatorormaybenot
这对我的任务非常有帮助。非常感谢! - kaiv

4
如您所见,这个链接中没有“自然”的方法来实现您想要的:
https://github.com/flutter/flutter/issues/16322 尝试使用以下步骤:
  1. 锁定方向:https://dev.to/mightytechno/how-to-change-screen-orientation-in-flutter-32c1
  2. 使用此软件包监听设备旋转更改(因为方向不会更改):https://pub.dev/packages/native_device_orientation
  3. 进行自定义
  4. 使用步骤1中的链接设置新方向
如果您需要更好的效果,可以尝试使用AnimatedContainer,但是在设置新方向之前,您需要手动处理所有屏幕位置和旋转:
https://api.flutter.dev/flutter/widgets/AnimatedContainer-class.html

0

你可能想尝试的一件事是编写一个平台通道方法来获取加速度计的传感器数据,然后将其提供给类似 Transform.rotate() 的函数进行处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接