In Related question, app can hide the status bar with
SystemChrome.setEnabledSystemUIOverlays([]);
But if I surround that with a media query to detect orientation in the main app, it causes the app to error because there is no material ancestor widget.
// app error
if (MediaQuery.of(context).orientation == Orientation.landscape) {
SystemChrome.setEnabledSystemUIOverlays([]);
}
Is there a way to conditionally hide the status bar in Flutter for the entire app, if the device rotates to landscape mode?
MediaQuery widget can be accessed only when the ancestor of the widget that you access MediaQuery from is a material widget.
A more generic solution without MediaQuery would be like:
First
import "dart:ui" as ui;
then
Size s = ui.window.physicalSize/ui.window.devicePixelRatio;
bool landscape = s.width>s.height;
if (landscape) {
SystemChrome.setEnabledSystemUIOverlays([]);
}
Or
Using Orientation builder as root. Inside the build function,
new OrientationBuilder(
builder: (BuildContext context, Orientation orientation){
if (orientation == Orientation.landscape) {
SystemChrome.setEnabledSystemUIOverlays([]);
} else {
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values)
}
return new Scaffold(...
}
);
Hope that helped!
Related
Below the code loads several tweets in WebViews. This code works flawlessly in Android but produces a different result in iOS Simulator. No errors are produced in logs and nothing in flutter analyze pertain to this page and its classes.
Included <key>io.flutter.embedded_views_preview</key><true/> in info.plist
Widget build(BuildContext context) {
var child;
//print(_tweetHTML);
if (_tweetHTML != '' && _tweetHTML.length > 0) {
final downloadUrl = Uri.file(_filename).toString();
print(downloadUrl); //This prints the expected HTML file
// Create the WebView to contian the tweet HTML
final webView = WebView(
initialUrl: downloadUrl,
javascriptMode: JavascriptMode.unrestricted,
gestureNavigationEnabled: true,
navigationDelegate: (NavigationRequest request) {
if (request.url.startsWith(downloadUrl)) {
//print('allowing navigation to $request');
return NavigationDecision.navigate;
} else if (request.isForMainFrame) {
//print('allowing navigation to $request');
_launchURL(request.url);
return NavigationDecision.prevent;
} else {
//print('blocking navigation to $request}');
return NavigationDecision.prevent;
}
},);
final box = LimitedBox(
maxHeight: 500.0,
child: webView,
);
child = box;
} else {
child = Text('Loading...');
}
return Container(child: child);
}
Expected results:
As you can see this works in Android:
Actual results:
However on iOS:
Option 1: add this to html body
<script id="twitter-wjs" type="text/javascript" async defer src="//platform.twitter.com/widgets.js"></script>
Option 2: Check out this widget on github TweetWebView for Flutter
Option 3: use Flutter WebView Plugin by fluttercommunity.dev, BUT This webview is not integrated in the widget tree, it is a native view on top of the flutter view. You won't be able see snackbars, dialogs, or other flutter widgets that would overlap with the region of the screen taken up by the webview.
I want to lock the orientation of the first screen of the app to portrait. In some screens, I need landscape orientation. I am using the react-native-orientation plugin. But when I launch the application, I see landscape orientation for a while and then quickly rotating to portrait orientation. The next screens are ok.
Here is my code:
componentDidMount() {
Orientation.lockToPortrait();
}
Is it possible to solve this performance issue?
Add below code in your page where orientation lock is delayed:
componentWillMount(){
Orientation.lockToPortrait();
Orientation.addOrientationListener(this._orientationDidChange);
}
_orientationDidChange(orientation) {
Orientation.lockToPortrait();
}
componentWillUnmount() {
// remove listener
Orientation.removeOrientationListener(this._orientationDidChange);
}
react-native-orientation no longer seems maintained.
I encourage you to use react-native-orientation-locker which also provides Orientation.lockToPortrait()
It was my error.
I used in one class redundant code:
const setLandscapeRightOrientation = Platform.select({
ios: Orientation.lockToLandscapeRight(),
android: Orientation.lockToLandscapeLeft(),
});
Xamarin Android app that use the Xamarin Forms UI framework.
I detect the screen orientation and want to only allow portrait and landscape for a specific Page but portrait only for all other pages.
I tried to call RequestOrientation but that force the orientation to stay the same and doesn't fire my orientation change notification again.
Have a static property in your App.Xaml.cs
public static bool IsPortraitOnly { get; set; }
Set it to false in all pages and to true in the page you want it to be Portrait only.
In your MainActivity in android project, override the method OnConfigurationChanged :
public override void OnConfigurationChanged(Configuration newConfig)
{
//using to prevent the OnCreate from firing when rotating or screen size is changing
if (App.IsPortraitOnly)
{
RequestedOrientation = ScreenOrientation.Portrait;
newConfig.Orientation = Orientation.Portrait;
}
base.OnConfigurationChanged(newConfig);
}
The following are the available orientation values in android :
public enum Orientation
{
Landscape = 2,
Portrait = 1,
Square = 3,
Undefined = 0
}
To determine whether you’re in portrait or landscape mode is pretty easy:
static bool IsPortrait(Page p) { return p.Width < p.Height; }
you can use sizeChanged event.The SizeChanged event seems to get called exactly once as the user goes from portrait to landscape mode
SizeChanged += (sender, e) => Content = IsPortrait(this) ? portraitView : landscapeView;
RequestedOrientation = ScreenOrientation.Portrait;
If you write code on the code side, portrait mode is maintained.
I use following code to see if the device is in landscape mode or not:
UIDevice.currentDevice().orientation.isLandscape.boolValue
It works BUT if I put my device in landscape mode before the app is launched, and after viewDidLoad, I call this line of code, it always returns false.
If I use this instead:
interfaceOrientation.isLandscape
it returns true, which is correct, but the compiler is showing a warning that interfaceOrientation was deprecated in iOS 8.0.
What is the correct way to get the device orientation right after the app is launched?
DeviceOrientation vs. ScreenSize vs StatusBar.isLandscape?
iOS 11, Swift 4 and Xcode 9.X
Regardless of using AutoLayout or not, there are several ways to get the right device orientation, and they could be used to detect rotation changes while using the app, as well as getting the right orientation at app launch or after resuming from background.
This solutions work fine in iOS 11 and Xcode 9.X
1. UIScreen.main.bounds.size:
If you only want to know if the app is in landscape or portrait mode, the best point to start is in viewDidLoad in the rootViewController at launch time and in viewWillTransition(toSize:) in the rootViewController if you want to detect rotation changes while the app is in background, and should resume the UI in the right orientation.
let size = UIScreen.main.bounds.size
if size.width < size.height {
print("Portrait: \(size.width) X \(size.height)")
} else {
print("Landscape: \(size.width) X \(size.height)")
}
This also happens early during the app/viewController life cycles.
2. NotificationCenter
If you need to get the actual device orientation (including faceDown, faceUp, etc). you want to add an observer as follows (even if you do it in the application:didFinishLaunchingWithOptions method in the AppDelegate, the first notifications will likely be triggered after the viewDidLoad is executed
device = UIDevice.current
device?.beginGeneratingDeviceOrientationNotifications()
notificationCenter = NotificationCenter.default
notificationCenter?.addObserver(self, selector: #selector(deviceOrientationChanged),
name: Notification.Name("UIDeviceOrientationDidChangeNotification"),
object: nil)
And add the selector as follows. I split it in 2 parts to be able to run inspectDeviceOrientation() in viewWillTransition
#objc func deviceOrientationChanged() {
print("Orientation changed")
inspectDeviceOrientation()
}
func inspectDeviceOrientation() {
let orientation = UIDevice.current.orientation
switch UIDevice.current.orientation {
case .portrait:
print("portrait")
case .landscapeLeft:
print("landscapeLeft")
case .landscapeRight:
print("landscapeRight")
case .portraitUpsideDown:
print("portraitUpsideDown")
case .faceUp:
print("faceUp")
case .faceDown:
print("faceDown")
default: // .unknown
print("unknown")
}
if orientation.isPortrait { print("isPortrait") }
if orientation.isLandscape { print("isLandscape") }
if orientation.isFlat { print("isFlat") }
}
Note that the UIDeviceOrientationDidChangeNotification may be posted several times during launch, and in some cases it may be .unknown. What I have seen is that the first correct orientation notification is received after the viewDidLoad and viewWillAppear methods, and right before viewDidAppear, or even applicationDidBecomeActive
The orientation object will give you all 7 possible scenarios(from the enum UIDeviceOrientation definition):
public enum UIDeviceOrientation : Int {
case unknown
case portrait // Device oriented vertically, home button on the bottom
case portraitUpsideDown // Device oriented vertically, home button on the top
case landscapeLeft // Device oriented horizontally, home button on the right
case landscapeRight // Device oriented horizontally, home button on the left
case faceUp // Device oriented flat, face up
case faceDown // Device oriented flat, face down
}
Interestingly, the isPortrait read-only Bool variable is defined in an extension to UIDeviceOrientation as follows:
extension UIDeviceOrientation {
public var isLandscape: Bool { get }
public var isPortrait: Bool { get }
public var isFlat: Bool { get }
public var isValidInterfaceOrientation: Bool { get }
}
3. StatusBarOrientation
UIApplication.shared.statusBarOrientation.isLandscape
This also works fine to determine if orientation is portrait or landscape orientation and gives the same results as point 1. You can evaluate it in viewDidLoad (for App launch) and in viewWillTransition(toSize:) if coming from Background. But it won't give you the details of top/bottom, left/right, up/down you get with the notifications (Point 2)
This worked for me:
if UIScreen.main.bounds.width > UIScreen.main.bounds.height{
print("Portraitmode!")
}
It works on all devices based on the display dimensions:
https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html
I have tested many times about orientation, so I have summed up some experience.
In all iPhone devices, except iPhone6(s) plus, the only interface orientation is .portrait. If App is launched in landscape mode, there must be a change of orientation. One will receive the UIDeviceOrientationDidChangeNotification. It's an appropriate time to get the orientation.
Regarding the launching when in landscape with iPhone6, the orientation after the launch will change once:
The launching when in landscape with iPhone6 plus, after launch the orientation never changed:
Two different screenshot with the same app,
So before the app does change orientation, the orientation is still like in the home page.
In viewDidLoad, the orientation has not changed yet, the log will be the wrong direction.
The isValidInterfaceOrientation should be detected before checking the orientation isLandscape. Don't process the flat message with isValidInterfaceOrientation == false (when it has any value of isLandscape).
I had a hazzel with this until I read the topic more carefully. With consideration of the isValidInterfaceOrientation it works fine.
#objc func rotated() {
if (UIDevice.current.orientation.isValidInterfaceOrientation) {
if (UIDevice.current.orientation.isLandscape) {
if(!bLandscape) {
bLandscape = true
setupTabBar() // Repaint the app
}
} else { // Portait
if(bLandscape) {
bLandscape = false
setupTabBar() // Repaint the app
}
}
}
}
I had a problem to detect which orientation was before isFlat so I put this in my view controller
let orientation = UIDevice.current.orientation
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if orientation.isPortrait {
return .portrait
} else if orientation.isFlat{
if UIScreen.main.bounds.width < UIScreen.main.bounds.height{
return .portrait
} else {
return .landscape
}
} else {
return .landscape
}
}
I want to do application which will not change UI even if screen is changed to portrait or landscape direction. How to do that?
I have made a static method in one of my library classes:
public static void disableOrientationChange()
{
// force app to use only portrait mode
int directions = Display.DIRECTION_NORTH;
UiEngineInstance engineInstance = Ui.getUiEngineInstance();
if (engineInstance != null)
{
engineInstance.setAcceptableDirections(directions);
}
}
The trick is that this code only works for screens created after running it. So you must run the code BEFORE you show your first screen.
I call this method from my Application.main(String args) method, just before the call to enterEventDispatcher().
public static void main(String[] args)
{
MyApp app = new MyApp();
/*
* BlackBerry OS 5.0.0
* Disable screen orientation changes
*/
ScreenUtils.disableOrientationChange();
// enters the event processing loop thread if required
if (!app.isHandlingEvents())
{
app.enterEventDispatcher();
}
}
you can use this code to set the orientation to portrait. After that even if the device is held in landscape, the orientation wont change.
Ui.getUiEngineInstance().setAcceptableDirections(Display.DIRECTION_PORTRAIT);