Get keyboard height in Flutter - dart

Is there any way to get an open keyboard's height in Flutter? I'm trying to pad a bottomSheet widget by the height of the keyboard while it's open.

Usually viewInsets provides data about any system ui that obscures the flutter ui. To know about the keyboard height, you can just check for the bottom property of viewInsets, when the keyboard is onscreen, this will hold the height of keyboard else zero.
You can check for the viewInsets with MediaQuery like:
MediaQuery.of(context).viewInsets.bottom
Note: The bottom property may have value even if some other system ui obscures the flutter ui from bottom.
Hope that helps!

The MediaQuery.of(context).viewInsets solution does not work for me. It always says zero even if keyboard is open. Moreover, looking at the highest-upvoted comment in this answer, it is a bad idea to use it as keyboard indicator.
Thus, here is a one-line solution:
final viewInsets = EdgeInsets.fromWindowPadding(WidgetsBinding.instance.window.viewInsets,WidgetsBinding.instance.window.devicePixelRatio);
Then do whatever you want (e.g. viewInsets.bottom is keyboard height) :)
EDIT: https://api.flutter.dev/flutter/dart-ui/FlutterView-class.html is a good source to see how keyboard affects various kinds of padding.

This one work for me:
https://pub.dev/packages/keyboard_utils
Sample code from package:

In case of complicated widget tree MediaQuery.of(context).viewInsets.bottom gives null even if the keyboard is open. So, we have to mutate values down the tree.
I made the package that provides all needed info down the tree https://pub.dev/packages/flutter_keyboard_size
Welcome to use and in case you find bugs or want to extend functionality please add the issue https://github.com/awaik/flutter_keyboard_size/issues

If MediaQuery.of(context).viewInsets.bottom shows 0.0, this should work:
First, go into your Scaffold and set this:
resizeToAvoidBottomInset: false,
THEN you can check the height of the keyboard in this way:
MediaQuery.of(context).viewInsets.bottom,

In case you need to get the keyboard height even when the keyboard is not open, you can use the flutter_persistent_keyboard_height package (note: it was created by me).
First thing you need to do is wrap a widget from children of which you want to get the keyboard height with PersistentKeyboardHeightProvider. Wrap your app widget (perhaps MaterialApp) if you want to get keyboard height from all widgets.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Persistent Keyboard Height Example',
home: const FlutterPersistentKeyboardHeightExample(),
builder: (context, child) => PersistentKeyboardHeightProvider(
child: child!,
),
);
}
}
And after that you can use the PersistentKeyboardHeight.of(context).keyboardHeight to get the height.

In my case nothing worked, but I needed screen height without keyboard height and LayoutBuilder work perfect.
LayoutBuilder(
builder: (context, constraints) => Container(
//maxHeight will change depending on your keyboard visible or not
height:constraints.maxHeight,
),
);
In theory you can do this
LayoutBuilder(
builder: (context, constraints) {
double keyboardHeight = MediaQuery.of(context).size.height - constrains.maxHeight;
}
);

Related

How to display a button at the bottom of a list in flutter?

I am new at Flutter. Your advice would be super appreciated!
I am trying to display a button at the bottom of a screen after a listview.
When I enter this code...I can perfectly display my listview. Please note there is no button yet in this code.
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: getTarotCards(),
),
);
}
Now, when I try to add in the Bottom Button which is a custom widget I made, I add in a column and a children widget so I can list the items I need to display.
I know that the problem is that the getTarotCards() is a method that returns a Listview so that's why I can't place it here (because I'm calling a function and not a widget.)
Does anyone know how I can call my method to display the listview and then display my button underneath this?
Right now when I run this card, my screen is blank and all I see is the black scaffold.
Thank you very much for your help! :)
You should make shrinkWrap (named parameter of ListView) to true in ListView if you are using ListView inside a Column

ListView loses scroll position when using InheritedWidget in Flutter

After adding InheritedWidget to app I recognize that ListView loses scroll position (selected item index) when I come back to list from details screen using Pop().
I know that widget can be rebuild any time but maybe someone can suggest solution without handling cache manually.
P.S.
Scroll position was fine before implementing InheritedWidget
no any code so a shot in the dark
try to add 'key'
like this
ListView(
key: const PageStorageKey<String>('some unique text'),
//other parameters
);

How to push a full-screen page that stays on top of Flutter BottomNavigationBar

I searched everywhere and could not come up with an answer.
I have a BottomNavigationBar and a FloatingActionButton.
What I am trying to do is pushing a full screen page when the user presses the FloatingActionButton.
This page needs to cover the area of the BottomNavigationBar and previous AppBar since in this new page user is not allowed to go to the other tabs of the BottomNavigationBar.
I came across fullscreenDialog, a property of PageRoute Widget class and got excited but could not get it to exactly work as I wanted it to work(top<->bottom as I will explain later)
This page will have it's own Scaffold and AppBar and is able to push/pop to next screens(inside of it's own navigation tree)
In order to pop this page, the user will press a 'x' button positioned at bottom center of the page.
I want to push/pop this new Page from top<->bottom instead of the usual navigation style which is left<->right (For iOS/Cupertino)
I am familiar with this type of UI from iOS devices(ModalViewController)
So How would we implement the push and pop commands?
Or is there another, better/recommended way to do this?
I had a similar problem, and it was solved with this:
await Navigator.of(context, rootNavigator:true).push( // ensures fullscreen
CupertinoPageRoute(
builder: (BuildContext context) {
return MyWidget();
}
) );
Found at https://stackoverflow.com/a/54017488/247451
You should try MaterialPageRoute with fullscreenDialog prop (docs):
Navigator.of(context).push(new MaterialPageRoute<Null>(
builder: (BuildContext context) {
return new AddEntryDialog();
},
fullscreenDialog: true
));
Might not be the best way in your case, but it will appear above the bottom bar and any other navigation widgets.
Code taken from: https://marcinszalek.pl/flutter/flutter-fullscreendialog-tutorial-weighttracker-ii/

Status bar color when navigating to new screen

I am using following in my build() method of 1st screen to change status bar color and it works fine.
// 1st screen's build() method
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Colors.green),
);
However when I navigate to 2nd screen the 1st screen status bar color appears on the 2nd screen too. And in 2nd screen's build() method, I am using the same code with different color
// 2nd screen's build() method
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Colors.red),
);
I had a similar problem and found the solution. The best way is to use AnnotatedRegion under Scaffold without AppBar (appBar is null) instead of the function SystemChrome.setSystemUIOverlayStyle() to set the system ui color.
Something like:
Scaffold(
body: AnnotatedRegion(
value: SystemUiOverlayStyle(statusBarColor: Colors.red)
child: MyBodyWidget(),
),
);
Btw, the value can be SystemUiOverlayStyle.dark or SystemUiOverlayStyle.light if you only want dark or light overlay instead of a color. Note that statusBarColor only works in Android so far.
Also note that appBar has to be null (absent) for AnnotatedRegion to work. You can write your own MyAppBar widget easily inside MyBodyWidget.
Try setting the 2nd status bar color before doing the Navigator.push to the second screen. Like this
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Colors.red),
);
await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => SecondScreen();
));
From Flutter Docs
Call this API in code whose lifecycle matches that of the desired system UI styles. For instance, to change the system UI style on a new page, consider calling when pushing/popping a new PageRoute.
It has a simple solution.
It is one way of several method.(AnnotatedRegion)
First, top parent page buildcontext in Saffold
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent, <---- transparent.
and 2nd page of 3rd page ...
if Appbar : Appbar( backgroundcolor: Colors.red <---- want color.
if no Appbar : Scaffold( backgroundcolor: Colors.red <---- want color.
This way, if you do a navigation pop, Page status color return the background color of Appbar or the background color of the scaffold.
It's my shortcut and I hope it helps.

What is the significance of the settings variable in MaterialPageRoute?

I have a list of ListTile and whenever I tapped them, a new page will appear. Currently, the new page will slide up (when appearing) and slide down (when removed). I wanted to change the transition animation to Fade.
I've read the solution of that in here then I edited the code from the link and here is the result.
class MyCustomRoute<T> extends MaterialPageRoute<T> {
MyCustomRoute({ WidgetBuilder builder})
: super(builder: builder);
#override
Widget buildTransitions(BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
if (settings.isInitialRoute)
return child;
return new FadeTransition(opacity: animation, child: child);
}
}
The only difference from the link's solution to mine was that I never supplied the "settings" variable to the MaterialPageRoute class.
And here is the part of the code where I've used the Navigator.push:
new ListTile(
onTap: (){
Navigator.push(context, new MyCustomRoute(builder: (context) => new SecondPage("someTitle", "someDescription") ));
},
//The rest of the code goes here
I've tried to run this code and I've never expected that this will run smoothly since I never provided the settings variable to the MaterialPageRoute widget but it ran perfectly.
My question is, is this the right way to do it? or
Should I provide settings for the MaterialPageRoute class?
And also since I didn't provide a settings variable for the MaterialPageRoute class, where did it get its settings? In this part of the code:
if (settings.isInitialRoute)
return child;
I would appreciate any enlightment. Thanks in advance.
The settings are only meant for two things - specifying the name, and letting the route know whether it's the first page to be opened.
The isInitialRoute simply tells the route whether it's the first page to be opened; this is important because you don't want a slide up animation on the very first page.
Since it seems your Custom Route is really only used after the first page, you don't need to worry about this. So you're probably fine ignoring the settings, unless you start to use the page as your first page (and even then, fading in might not be the worst thing).

Resources