Flutter dynamic screen rotation - flutter

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

Related

I try to set DeviceOrientation.landscapeLeft for my app but when run my mobile app in portrait situation it break the graphic like bellow

when I run my app while I let my phone portrait
when I run my app while I let my phone lanscape.
my code to set landscape left for my app.
void main() {
// Step 2
WidgetsFlutterBinding.ensureInitialized();
// Step 3
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
]).then((value) => runApp(MaterialApp(home:const MyApp())));
}
please help me to fix this problem I expect to have results when I run my app while I let my phone landscape.
You can try this way:
Instead of executing step 3 before the you execute the runApp, use the SystemChrome.setPreferrerdOrientation([]) method inside MyApp() initState(){} or in build(){} method, then dispose() it:
For Example:
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
]);
super.initState();
}
#override
void dispose() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
I hope this works :)

dispose() is called when using AutomaticKeepAliveClientMixin

I am under the impression that using AutomaticKeepAliveClientMixin would prevent the states dispose() callback from being called when the Widget isn't visible anymore.
However, I have a situation where dispose() and initState() get called every time I hide/show a Widget, even though I implemented AutomaticKeepAliveClientMixin correctly.
class IdleScreenState extends State<IdleScreen> with AutomaticKeepAliveClientMixin {
#override
void initState() {
super.initState();
print('IdleScreen initState');
}
#override
void dispose() {
print('IdleScreen dispose');
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
// ...build the page...
}
#override
bool get wantKeepAlive => true;
}
This is how I hide/show this Widget
class MainScreen extends State<MainScreen> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return somecondition ? IdleScreen() : OtherScreen();
}
#override
bool get wantKeepAlive => true;
}
Every time this Widget (screen) is shown, initState()gets called, and every time I hide it, dispose() gets called. It's as if the AutomaticKeepAliveClientMixin has no effect. All other similar issues I could find seem to be due to either missing the wantKeepAlive => true or the super.build(context), but they are 100% there in the code.
I tried supplying a GlobalKey for IdleScreen as well, but that didn't have any effect.
However, if I use an IndexedStack or Offstage to hide/show the widget, it works as expected (initState() and dispose() don't get called when hiding/showing the widget).
IndexedStack(
index: somecondition ? 0 : 1,
children: [
IdleScreen(),
OtherScreen()
],
),
Maybe I'm mistaken, but isn't the whole purpose of AutomaticKeepAliveClientMixin to not have to manually keep the widget around using this technique?
This is in a web project, if that matters.
The type argument T is the type of the StatefulWidget subclass of the State into which this class is being mixed.
you have to pass the widget class name like this..
class IdleScreenState extends State<IdleScreen>
with AutomaticKeepAliveClientMixin <IdleScreen> {...

Flutter screen rotation triggers overflow

Description : the issue I'am facing is that when I force rotate my screen in Flutter it does paint the layout and THEN rotate the screen. So before the rotation flutter show an overflow bar for some of the widgets (especially when I come back from the screen but it happens also when pushing a new one).
Initially I force the screen to be landscape. Then I rotate it for some menu. Once it is done I just come back to portrait using Navigator.pop(context)
Here is a video of the issue where I slowed down the important fragment : Video of the issue
Here is my code sample:
class ScreenRotator extends StatefulWidget {
const ScreenRotator({
Key? key,
required this.child,
}) : super(key: key);
final Widget child;
#override
State<ScreenRotator> createState() => _ScreenRotatorState();
}
class _ScreenRotatorState extends State<ScreenRotator> {
#override
void initState() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
]);
super.initState();
}
#override
dispose() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
Some points about my code :
Refering to the screen rotation it does need te be inside a StateFull widget (calling initState() and dispose() where the screen rotation happens).
I made a special widget called ScreenRotator which calls a StateLess widget (the screen layout).
I couldn't find anything linked to my question and this question refers to older ones which are the following :
Flutter: how to prevent device orientation changes and force portrait?
How to force the application to Portrait mode in flutter
Note : There is a problem with my buttons (TextButtons) but this is not the actual issue i'm asking help for.

Disposing Camera preview in CupertinoTabBar when switched in Flutter

I am building a QR code scanner app with a couple of tabs wrapped up within CupertinoTabBar in a CupertinoTabScaffold. I have a CupertinoTabController to take care of the switching between the tabs. One of this tabs has a CameraPreview widget from the Camera plugin of Flutter along with a proper dispose mechanism. However, whenever the tab are switched, the Camera stream still persists, causing the phone to heat up and also causes janky UX. Now I read that the BottomNavigationBar from Material widgets does not persist in this way. Any idea on how to achieve the same behaviour with CupertinoTabBar?
You can use the StatefulWidget for each a page of the tabs and then try to listening AppLifecycleState. Disponse controller if state inactive/paused.
In my case it's working fine.
class Example extends StatefulWidget {
#override
ExampleState createState() => ExampleState();
}
//Implement WidgetsBindingObserver to listen Lifecycle State
class ExampleState extends State<Example> with WidgetsBindingObserver {
late CameraController _controller;
...
...
#override
void initState() {
super.initState();
// Add Listener (Lifecycle State)
WidgetsBinding.instance!.addObserver(this);
}
Future<void> _setupController() async {
//todo setup/init controller
}
//Implements this method to listen Lifecycle State
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_controller.dispose();
_setupCameraAndControllerFuture = _setupController();
}
if (state == AppLifecycleState.inactive) {
_controller.dispose();
} else if (state == AppLifecycleState.paused) {
_controller.dispose();
}
}
#override
void dispose() {
// Remove Listener (Lifecycle State)
WidgetsBinding.instance!.removeObserver(this);
// dispose controller
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
...
...
);
}
}

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

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.