Listening For Device Orientation Changes In Flutter
Asked Answered
M

7

24

I am looking for a way of listening for changes to the phones orientation, with the intent to hide something if the phone is Landscape.

My Layout is currently only displayed in portrait, as intended, but I want my app to do something if the device is rotated to Landscape, while keeping the layout in Portrait.

I have tried using a OrientationBuilder, but this only works if the layout changes to Landscape.

I have also tried using MediaQuery.of(context).orientation, but it continues to return portrait once the device is rotated, again only using the layouts orientation.

Moia answered 4/3, 2019 at 15:49 Comment(5)
stephenmann.io/post/listening-to-device-rotations-in-flutterTranquil
That only seems to work if the preferred orientations allow horizontal, which my app does notMoia
your app needs to allow changing orientations, and your layout needs to be restricted from changing. This way the app requests orientation changes from the OS.Tranquil
I'm unsure if there is a way to restrict the layout from changing in that manner, as even before build is called the layout seems to flip. I may be force to build the layout on its side and use the OrientationBuilder to switch between the twoMoia
Here's a similar issue: github.com/flutter/flutter/issues/17215Moia
P
18

You can listen to screen size changes, but MediaQuery.of(...) should work as well and should cause rebuilds of your widget when orientation changes

https://stephenmann.io/post/listening-to-device-rotations-in-flutter/

import 'dart:ui';
import 'package:flutter/material.dart';

class WidthHeight extends StatefulWidget {
  WidthHeight({ Key key }) : super(key: key);

  @override
  WidthHeightState createState() => new WidthHeightState();
}

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

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

  double width = 0.0;
  double height = 0.0;

  @override void didChangeMetrics() {
    setState(() {
      width = window.physicalSize.width;
      height = window.physicalSize.height;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Text('Width: $width, Height $height');
  }
}
Preciado answered 5/3, 2019 at 9:0 Comment(8)
Thanks, but didChangeMetrics() is only called if the layout switches from Portrait to Landscape, or vise versa, I want my layout the remain in portrait and only change a single widget if the device is moved to portraitMoia
Works fine for me on my real phone (same as MediaQuery.of(context).orientation, but does not work on my emulator. Don't know yet why.Escobedo
@GünterZöchbauer same here. Have you figured it out?Cigarette
@BonanzaOne no, but I didn't investigate further yet.Escobedo
@MatthewWeilding any updates? Trying to do the same thing, i.e receiving a callback once the screen rotates, even though SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp ]); is setFreemason
Note that didChangeMetrics() is also called in other situations, such as when the keyboard pops up and the content gets shifted up. If you only want to use it for orientation changes, you should include a check like: final orientation = WidgetsBinding.instance.window.physicalSize.aspectRatio > 1 ? Orientation.landscape : Orientation.portrait; if (_currentOrientation != orientation) { // Do stuff _currentOrientation = orientation; }Cantara
@MatthewWeilding can you help how to achieve your requirement. I am also looking for the same.Thermobarometer
@Freemason any luck. Could you give me clues how to achieve ?Thermobarometer
S
10

Using MediaQuery directly in didChangeMetrics() returns previous values. To get the latest values after orientation change. Use WidgetsBinding.instance.addPostFrameCallback() inside it. https://github.com/flutter/flutter/issues/60899

class OrientationSample extends StatefulWidget {
  const OrientationSample({Key? key}) : super(key: key);

  @override
  _OrientationSampleState createState() => _OrientationSampleState();
}

class _OrientationSampleState extends State<OrientationSample> with WidgetsBindingObserver {
  Orientation? _currentOrientation;

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

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

  @override
  void didChangeMetrics() {
    _currentOrientation = MediaQuery.of(context).orientation;
    print('Before Orientation Change: $_currentOrientation');
    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
      setState(() {
        _currentOrientation = MediaQuery.of(context).orientation;
      });
      print('After Orientation Change: $_currentOrientation');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: _currentOrientation != null ? Text('$_currentOrientation') : Text('Rotate Device to Test'),
      ),
    );
  }
}
Soche answered 9/4, 2021 at 7:51 Comment(2)
and how i call than method? or using it?Neace
@mamenaware I've updated my answer. See if it helps.Soche
M
5

You can wrap your widget with visibility and set the opacity parameter to getOpacityForOrientation() and in your Screen you can add the function:

double getOpacityForOrientation() {
    if (MediaQuery.of(context).orientation == Orientation.landscape) {
      return 0;
    } else {
      return 1;
    }
}

when the orientation changes the widget will rebuild and the opacity will change and hide/show

Motley answered 4/3, 2019 at 16:12 Comment(1)
MediaQuery.of(context).orientation still returns Orientation.portrait when the device is rotatedMoia
C
3

Both OrientationBuilder and MediaQuery.of(context).orientation should be able to get the job done. But you're saying that the device's orientation never changes which makes me think that you have not enabled auto-rotate in your device.

Can you enable auto-rotate from quick settings and give it a try?

Corder answered 5/3, 2019 at 8:50 Comment(4)
OrientationBuilder only tells you if the parent widget is wider then heigh (or vice versa) and has nothing to do with the orientation of the device). That's a common misconception probably because the name is not ideal.Escobedo
@GünterZöchbauer Yep. You're right. OrientationBuilder gives us the parent widget's orientation and not device's. Mostly they are the same unless the parent widget is laid out horizontally. But OP here doesn't seem to have a problem with that as he has tried MediaQuery too. The crux of my answer is 'enabling auto-rotate'.Corder
Thank, but I want my layout to stay in Portrait mode throughout, I only want one widget to change when the device is rotated landscape. The problem is OrientationBuilder and MediaQuery.of(context).orientation still return portrait, even when the device is rotated (with screen rotation on), because my layout is fixed in portrait. I am trying to find a way of determining this, independent from the layouts orientationMoia
Hi, @MatthewWeilding did you find any solution to your question... I am looking for a similar solution. My app supports the only portrait mode, but on one screen I am capturing the pic using a camera. I have to enable the user to capture the pic horizontally. #64457693Implacable
G
0

Using provider and OrientationBuilder:

orientation_provider.dart

import 'package:flutter/material.dart';

class OrientationProvider extends ChangeNotifier {
  Orientation _orientation = Orientation.portrait;

  Orientation get getOrientation {
    return _orientation;
  }

  void changeOrientation(Orientation newOrientation) {
    print("CHANGE ORIENTATION CALLED: old: $_orientation, new: $newOrientation");
    bool hasChanged = _orientation != newOrientation;
    _orientation = newOrientation;
    if(hasChanged) notifyListeners();
  }
}

In parent widget, use OrientationBuilder and set orientation in provider

.
.
.
child: OrientationBuilder(

        builder: (context, orientation){
          WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
            context.read<OrientationProvider>().changeOrientation(orientation);
          });

          return MaterialApp(
            debugShowCheckedModeBanner: false,
            theme: ThemeData.dark().copyWith(
              textTheme: ThemeData.dark().textTheme.apply(
                fontFamily: 'Nunito',
              ),
.
.
.

Where orientation change need to be listened

 child: context.watch<OrientationProvider>().getOrientation == Orientation.portrait ? WIDGET_PORTRAIT
            : WIDGET_LANDSCAPE
Galibi answered 3/8, 2022 at 7:0 Comment(0)
S
0

So Im posting this here to try to help maybe a little, but I will be honest in that it feels like a hack. The problem that Matthew is describing is -- if you specifically lock your app to portrait mode at compile time, MediaQuery and OrientationBuilder don't help because they are never triggered. In my case, I have an app which is locked to portrait, but I am trying to add one screen that streams video, which I would like to play full screen when the phone is rotated. As above, because it is locked at compile time MediaQuery and OrientationBuilder won't work.

For my 'hack' in my screen controller, I listen to a stream subscription from the accelerometer events API. If the stream event.y is < 3, then the phone is close to being horizontal, I can then use this to change an int that controls the number of rotations for an Expanded rotating box that houses the video player.

This does not work for working out if it is right or left-handed rotation... and as I say, it feels like a hack, but it's a start...

Surrebuttal answered 19/12, 2022 at 10:38 Comment(0)
N
-4

just using code like this, if you wanna detect orientation of device

String type_orien = "potrait";

Future<void> detect_change_orientation() async {
  await Future.delayed(const Duration(seconds: 1), () async {
  if (MediaQuery.of(context).orientation == Orientation.landscape) {
    if (type_orien ==
        "potrait") // if orien change and before is potrait then reload again
    {
      print("landscape ss");
      await reload_set_scren();
      type_orien = "landscape";
    }
  } else {
    if (type_orien ==
        "landscape") // if orien change and before is landscape then reload again
    {
      print("potrait ss");
      await reload_set_scren();
      type_orien = "potrait";
     }
    }
  });
}

Future<void> reload_set_scren() {
//... do whats ever u want
}

@override
Widget build(BuildContext context) {



  detect_change_orientation(); // call function here <--


return Scaffold(
    );
}
Neace answered 25/6, 2021 at 6:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.