Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listen to Orientation State in Flutter; before & after rotation

Tags:

ios

flutter

dart

OrientationBuilder reports the orientation change after the full transformation has taken place, then the rebuild occurs after that.

Is there a way to act before the orientation initiates? I am not trying to pre-empt the rotation, but make changes simutaneously, not after.

The goal:

  1. Device is rotated.
  2. Detect this and rebuild UI to show overlay.
  3. Flutter performs its own tranformation, rotating UI to new orientation.
  4. After fixed time period simply rebuild to hide overlay.

The challenge, how to fulfil point 2 before 3 occurs?

like image 348
Josh Kahane Avatar asked Aug 18 '20 11:08

Josh Kahane


2 Answers

Yes, you can get the orientation change earlier using WidgetsBindingObserver by overriding didChangeMetrics.

How to use didChangeMetrics

You can simply mixin WidgetBindingObserver in a State implementation of a stateful widget:

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();
  }
}

Determining the orientation

Orientation is determined by the aspect ratio of the available size. This means that you can get the orientation in didChangeMetrics using the following code:

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

Example

I have constructed an example StatefulWidget that compares the OrientationBuilder callback to 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();
        },
      ),
    );
  }
}

Results

Running this example shows the following times for the two functions:

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

So in my case, the difference in detection was about 0.06 seconds.

Observations

As you can see from above, the difference is insignificant (I would say). So I am not even sure if this will be useful to you.

Moreover, I have observed that the OrientationBuilder callback is actually called at the start of the device rotation - at least on my Android emulator. This would mean that you can rebuild your UI before the rotation happens.


I hope this was somehow helpful to you :)

like image 103
creativecreatorormaybenot Avatar answered Oct 19 '22 09:10

creativecreatorormaybenot


As you can see in this link, there is no "natural" way to do what you want:
https://github.com/flutter/flutter/issues/16322

Try to make something using this:

  1. Lock the orientation: https://dev.to/mightytechno/how-to-change-screen-orientation-in-flutter-32c1
  2. Listen to device rotation changes with this package (since the orientation will not change): https://pub.dev/packages/native_device_orientation
  3. Make your customization
  4. Set the new orientation using the link from step 1

You may try animated container for a better effect, but you would need to handle all the screen position and rotation manually before set the new orientation:
https://api.flutter.dev/flutter/widgets/AnimatedContainer-class.html

like image 28
Rod Avatar answered Oct 19 '22 10:10

Rod