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 :)