Miguel de Sousa
Miguel de Sousa

Reputation: 303

Flutter: How to prevent my main widget from rotating and still keep track of it's orientation?

I'm trying to update a child widget's orientation with Rotated Box, based on my device orientation.

However, I have a constraint: I don't want my main UI app to rotate. I want to keep it always locked (like "portrait up"), but I still want to receive device orientation updates.

This is the partial solution I've found, using native_device_orientation package and SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).

The problem is: it's super sensible to changes, so it is not suitable in a user experience perspective (as pointed by native_device_orientation author here). Also, there is a 45 degree position that the sensor orientation goes frenzy, since the sensor is in the sweet spot between two orientations.

Is there another way or perspective to solve this problem?

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:native_device_orientation/native_device_orientation.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  int get_turns_based_on_orientation(NativeDeviceOrientation orientation) {
    switch(orientation) {
      _counter++;
      case NativeDeviceOrientation.portraitDown:
        return 2;
      case NativeDeviceOrientation.portraitUp:
        return 0;
      case NativeDeviceOrientation.landscapeLeft:
        return 1;
      case NativeDeviceOrientation.landscapeRight:
        return 3;
      case NativeDeviceOrientation.unknown:
        return 0;
    }
  }

  @override
  void initState() {
    super.initState();
    SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
  }
    
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: NativeDeviceOrientationReader(
        useSensor: true,
        builder: (context) {
          final orientation = NativeDeviceOrientationReader.orientation(context);
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                RotatedBox(
                  quarterTurns: get_turns_based_on_orientation(orientation),
                  child: Text(
                    '$_counter',
                    style: Theme.of(context).textTheme.headline4,
                  ),
                ),
              ],
            ),
          );
        }
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Upvotes: 1

Views: 1710

Answers (2)

Naslausky
Naslausky

Reputation: 3768

In order to have your orientation changed without any oscillation, you can try to reduce the sensibility with this as a workaround:

  • Every time you receive a device orientation change, store the orientation received and the moment in time this change happened.
  • Check after a specified amount of time if the orientation changed, if it has not changed in a while, re-render the screen based on this new stable orientation.
  • Always render the screen based on the last stable orientation.

Adapting to your code it would be as follows:

[...]
//State Class properties
DateTime timeOfLastChange = DateTime.now();
NativeDeviceOrientation stableOrientation = NativeDeviceOrientation.portraitUp;
NativeDeviceOrientation currentOrientation = NativeDeviceOrientation.portraitUp;

[...]
builder: (context) {
          currentOrientation = NativeDeviceOrientationReader.orientation(context);
          timeOfLastChange = DateTime.now();
          Future.delayed(Duration(milliseconds: 500), () {
            if (DateTime
                .now()
                .difference(timeOfLastChange)
                .inMilliseconds > 500) {
              setState((){stableOrientation = currentOrientation;});
            }
          });
[...]
          quarterTurns: get_turns_based_on_orientation(stableOrientation),
[...]
}

Keep in mind that the 500 value is a constant that can be adjusted to increase or decrease the delay in orientation changes.

Upvotes: 3

Akif
Akif

Reputation: 7640

I think you are not calling the setPreferredOrientations method in the correct place. You need to call it in your initState() and dispose() methods depend on your requirements. Widget build() method is the reason for the super sensible.

Upvotes: 0

Related Questions