Flutter: How to set and lock screen orientation on-demand - flutter

On one of my flutter pages, I need the screen to set to landscape mode and lock it so it can't rotate into portrait mode, but only on the one page. So need a way to enable this function on-the-fly. Anyone know how to do this?
I would like it to rotate landscape-left or landscape-right, just not into portrait mode.

First import the services package:
import 'package:flutter/services.dart';
This will give you access to the SystemChrome class, which "Controls specific aspects of the operating system's graphical interface and how it interacts with the application."
When you load the Widget, do something like this:
#override
void initState(){
super.initState();
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
]);
}
then when I leave the page, put it back to normal like this:
#override
dispose(){
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
super.dispose();
}

I would use a simple mixin to lock phone in portrait. The following solution locks the entire app in portrait or sets specific screens to portrait while keeping rotation elsewere.
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
/// Forces portrait-only mode application-wide
/// Use this Mixin on the main app widget i.e. app.dart
/// Flutter's 'App' has to extend Stateless widget.
///
/// Call `super.build(context)` in the main build() method
/// to enable portrait only mode
mixin PortraitModeMixin on StatelessWidget {
#override
Widget build(BuildContext context) {
_portraitModeOnly();
return null;
}
}
/// Forces portrait-only mode on a specific screen
/// Use this Mixin in the specific screen you want to
/// block to portrait only mode.
///
/// Call `super.build(context)` in the State's build() method
/// and `super.dispose();` in the State's dispose() method
mixin PortraitStatefulModeMixin<T extends StatefulWidget> on State<T> {
#override
Widget build(BuildContext context) {
_portraitModeOnly();
return null;
}
#override
void dispose() {
_enableRotation();
super.dispose();
}
}
/// blocks rotation; sets orientation to: portrait
void _portraitModeOnly() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
}
void _enableRotation() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
}
To block rotation in the entire app implement PortraitModeMixin in the main App widget. Remember to call super.build(context) in Widget build(BuildContext context) method.
/// Main App widget
class App extends StatelessWidget with PortraitModeMixin {
const App();
#override
Widget build(BuildContext context) {
super.build(context);
return CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(),
home: Text("Block screen rotation example"),
);
}
}
To block rotation in a specific screen implement PortraitStatefulModeMixin<SampleScreen> in the specific screen's state. Remember to call super.build(context) in the State's build() method and super.dispose() in dispose() method. If your screen is a StatelessWidget - simply repeat the App's solution (previous example) i.e. use PortraitModeMixin.
/// Specific screen
class SampleScreen extends StatefulWidget {
SampleScreen() : super();
#override
State<StatefulWidget> createState() => _SampleScreenState();
}
class _SampleScreenState extends State<SampleScreen>
with PortraitStatefulModeMixin<SampleScreen> {
#override
Widget build(BuildContext context) {
super.build(context);
return Text("Flutter - Block screen rotation example");
}
#override
void dispose() {
super.dispose();
}
}
Mixins with such syntax work from Dart 2.1

First, Lock the entire app orientation to Portrait mode.
//Do this in main.dart
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) {
runApp(MyApp());
});
Second, Go the specific screen where you want to change the orientation.
#override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft
]);
}
#override
void dispose() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
super.dispose();
}
For using SystemChrome you will have to add 'package:flutter/services.dart'

void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
.then((_) {
runApp(new MyApp());
});
}

import services.dart package and add following code to lock device orientation to portraitUp mode:
import 'package:flutter/services.dart';
main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
runApp(MyHomePage());
}

Sometimes it could not work due to null info about orientation.
You can use it simply like this:
import services.dart
void main() {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp]
)
.then((_) {
runApp(new MyApp());
});
}
// wait for settings screen orientation after initiating app and -> then lock orientation

Important for iOS.
Enable the orientation in in info.plist file. for Example
Steps
Set the orientation in main.dart file. In my case, My application is only support for portrait except the one screen so i need to set the portrait mode at first time. for Example
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp,DeviceOrientation.portraitDown,]);
Add the following code in that screen which you need to rotate.
void initState() {
super.initState();
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
]);
}
#override
dispose(){
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
super.dispose();
}

Simple way to lock screen orientation in whole app
Add import 'package:flutter/services.dart'; to the start of main.dart file.
Create SystemChrome.setPreferredOrientations(); method to disable Screen rotation in Widget build area of MyApp class just before the return part.
Specify the orientation using [DeviceOrientation.<orientation-type>] in arguments of the method.
Use one of the following in place of <orientation-type> :
portraitUp
portraitDown
landscapeLeft
landscapeRight
Example Code:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' ;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Screen Orientation"),
),
body: Container(
),
),
);
}
}

import services.dart and your void main function should be like:
void main(){
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
.then((_){
runApp(MyApp());
}
);
}

You can use orientation_helper for this https://pub.dev/packages/orientation_helper . It’s main goal is to set orientation for each screen in an app.

For those who prefer to use hooks
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
useOrientation(List<DeviceOrientation> orientations) {
useEffect(
() {
SystemChrome.setPreferredOrientations(orientations);
return () {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
};
},
);
}
Use it like:
class MyWidget extends HookWidget {
void build(BuildContext context) {
useOrientation([DeviceOrientation.portraitUp]);
return Container();
}
}

In AndroidManifest file in main folder under activity tag set android:screenOrientation = "portrait"
<activity android:windowSoftInputMode="adjustResize" android:screenOrientation = "portrait">

Set the preferred orientation in the flutter.
// import this package
import 'package:flutter/services.dart';
// Lock the orientation to Portrait Only
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((value) => runApp(MyApp()));
You can also add Preferred Orientations in the setPreferredOrientations list like [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]
Below are the orientation you can set:
/// If the device shows its boot logo in portrait, then the boot logo is shown
/// in [portraitUp]. Otherwise, the device shows its boot logo in landscape
/// and this orientation is obtained by rotating the device 90 degrees
/// clockwise from its boot orientation.
portraitUp,
/// The orientation that is 90 degrees clockwise from [portraitUp].
///
/// If the device shows its boot logo in landscape, then the boot logo is
/// shown in [landscapeLeft].
landscapeLeft,
/// The orientation that is 180 degrees from [portraitUp].
portraitDown,
/// The orientation that is 90 degrees counterclockwise from [portraitUp].
landscapeRight,
Ref: https://greymag.medium.com/flutter-orientation-lock-portrait-only-c98910ebd769

In GetX, you need to use GetBuilder like this example:
final Controller ctrl = Get.find();
GetBuilder<Controller>(
initState: (_) => ctrl.setLandscape(),
dispose: (_) => ctrl.setPortrait(),
builder: (code) => Container(
padding: const EdgeInsets.zero,
alignment: Alignment.Center,
child: const SizedBox(),
),
),
In Controller file:
class Controller extends GetxController {
RxBool isLandscape = false.obs;
Future<void> setLandscape() async {
if (isLandscape.isTrue) {
await SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
update();
}
}
Future<void> setPortrait() async {
if (isLandscape.isFalse) {
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
update();
}
}
}
I hope this solution will answer developers who use GetX as their main state management. Good luck, bro! God blesses you all.

Related

Is it possible to listen for system language change in Flutter?

Is it possible to listen when the user changes their system language? I need to clear some list when the user changes their language. Now I am detecting the language in the void main() function on startup.
void main() async {
// Firebase Push Notifications initialization
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(backgroundHandler);
detectLanguageChange();
WidgetsBinding.instance.addObserver(this);
runApp(const MyApp());
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
}
#override
void didChangeLocales(List<Locale>? locales) {
// TODO: implement didChangeLocales
super.didChangeLocales(locales);
print("Locales changed");
}
It needs to run 'detectLanguage' every time the language changes.
You can use Getx library and use:
return GetMaterialApp(
locale: Get.deviceLocale, //returns Locale('<language code>', '<country code>')
);
from there you can store it in a variable and once the device language update changes so does the var and there you link your desired function.
Yes, it is possible. Take a look on this example:
#override
Widget build(BuildContext context) {
Locale myLocale = Localizations.localeOf(context);
_customFunction();
return Scaffold(
appBar: AppBar(
title: 'Title'),
),
body: Text(myLocale.languageCode == 'fr' ? 'Bonjour!' : 'Hello!'),
);
}

Flutter dynamic screen rotation

What is the best way to dynamically do screen rotation in Flutter for few of the screens and not affect to other screens?
So i tried two ways, with mixins. (This way stackoverflow ). It works initially, for the first enter and leave screen, but when I do re-run again some of screen which should be landscape again, it's fixed in portrait and thats all, until i re-run the app.
And tried directly, with setting preferred orientation. (This way stackoverflow This way mess-up whole app even if i just unlock rotation on build/initState (tried both) on one screen, and lock it on same screen on dispose.
For example, i need camera screen rotated with whole UI, and i have screen to edit the image, and i need that screen in the portrait, and there problems shows up.
UPDATE: So here is the link with code with mixins try. I created mixin which enables rotation in that screen, and on dispose, lock rotation again. CODE
And code here:
mixin RotationEnabledStatefulModeMixin<T extends StatefulWidget> on
State<T> {
#override
Widget build(BuildContext context) {
_enableRotation();
return null;
}
#override
void dispose() {
_portraitModeOnly();
print("RotationEnabledStatefulModeMixin -> disposed!");
}
}
void _portraitModeOnly() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
}
void _enableRotation() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
}
//this is screen where i need landscape only
class TakePictureScreen extends StatefulWidget {
final CameraDescription camera;
TakePictureScreen(
{Key key, #required this.camera, this.report, this.sectionIndex})
: super(key: key);
#override
TakePictureScreenState createState() => TakePictureScreenState();
}
class TakePictureScreenState extends State<TakePictureScreen> with
RotationEnabledStatefulModeMixin<TakePictureScreen>{
CameraController _controller;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
super.build(context);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
}

Flutter how to disable landscape orientation?

I want to disable the landscape mode. I tried to allow only portrait mode using following code. But it is not working with my physical device. How to solve this?
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
runApp(MyApp());
}
You need to paste the code in Widget build(). Consider this answer for more details
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
return new MaterialApp(...);
}
}

How to use Android Camera in Background in Flutter?

I need to create an examination application in Flutter, where we need to take photograph and video of a user at some intervals, And while doing this we don't want to show the camera screen.
I tried to use Camera plugin of Flutter, but I am not able to find any way to capture image and video without camera preview.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
List<CameraDescription> cameras;
Future<void> main() async {
cameras = await availableCameras();
runApp(CameraApp());
}
class CameraApp extends StatefulWidget {
#override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
CameraController controller;
#override
void initState() {
super.initState();
controller = CameraController(cameras[0], ResolutionPreset.medium);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (!controller.value.isInitialized) {
return Container();
}
return AspectRatio(
aspectRatio:
controller.value.aspectRatio,
child: CameraPreview(controller));
}
}
I want not to show the preview screen and take the images or videos at some interval
While accessing the Camera in the background was possible on earlier iterations of Android. Since Android P, accessing sensors like Camera and Mic can only be done while the app is in the foreground. More details can be read here.

Listening For Device Orientation Changes In Flutter

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.
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');
}
}
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'),
),
);
}
}
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
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?
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
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...
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(
);
}