In my following piece of code, I intend to set the height of my DropdownMenu
according to the presence/absence of onscreen keyboard. I have also considered height of other elements like AppBar
, and TextField
while calculating the appropriate height of the dropdown menu (for details please refer to the code). I've not been able to update properly the height of the keyboard heightKeyboard
variable in my code such that the computation of heightDropdownMenu
can use it. The heightKeyboard remains unupdated and hence the height of dropdown menu does not adapt to the presence of onscreen keyboard, and some part of it hides below the keyboard such that the user can't access them.
What I've tried: In an attempt to fix it, I tried implementing InkWell
around the TextField
and in its onTap
property I tried to update the keyboard height and then dropdown height, but this didn't seem to be working, also I don't see the print() function within it working as well, and so can't say if the values are being updated or not.
Hope someone would help me out. Thank you.
import 'dart:ui';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
const List<String> list = <String>[
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
];
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey? _key01 = GlobalKey();
final GlobalKey? _key02 = GlobalKey();
late final TextEditingController firstController;
late Size screenSize;
double screenWidth = 0;
double screenHeight = 0;
double heightAppBar = 0;
double heightTextField = 0;
double heightKeyboard = 0;
double heightOthersCummulative = 0;
double heightAdjustment = 60;
double heightDropdownMenu = 0;
void setDropdownMenuHeight(
double heightAppBar, double heightTextField, double heightKeyboard) {
if (heightAppBar > 0 && heightTextField > 0) {
heightDropdownMenu = screenHeight -
heightAppBar -
heightTextField -
heightKeyboard -
heightOthersCummulative -
heightAdjustment;
// return heightDropdownMenu;
} else {
heightDropdownMenu = 150;
// return heightDropdownMenu;
}
}
late String dropdownValue = list.first;
String result = "";
String effective_available_screen_height = "";
String dimensions = "";
FlutterView view = WidgetsBinding.instance.platformDispatcher.views.first;
late Size size;
double width = 0;
double height = 0;
double height01 = 0;
@override
void initState() {
super.initState();
firstController = TextEditingController();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
heightAppBar = _key01?.currentContext?.size?.height ?? -1;
heightTextField = _key02?.currentContext?.size?.height ?? -1;
heightKeyboard = MediaQuery.of(context).viewInsets.bottom;
setDropdownMenuHeight(heightAppBar, heightTextField, heightKeyboard);
dimensions =
"KeyboardHeight=${heightKeyboard}\nDropdownMenuHeight=${heightDropdownMenu}\nAppBarHeight=${_key01?.currentContext?.size?.height}\nTextFieldHeight=${_key02?.currentContext?.size?.height}";
});
});
}
@override
void dispose() {
firstController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size;
screenWidth = screenSize.width;
screenHeight = screenSize.height;
Size ph_size = view.physicalSize;
double ph_height = ph_size.height;
size = MediaQuery.of(context).size;
width = size.width;
height = size.height;
final padding = MediaQuery.of(context).viewPadding;
height01 = height - padding.top - padding.bottom;
effective_available_screen_height = "$height01";
// Height of Keyboard when onscreen (ohterwise zero):
double keyboard_height = MediaQuery.of(context).viewInsets.bottom;
return Scaffold(
appBar: AppBar(
key: _key01,
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Container(
padding: const EdgeInsets.all(5.0),
child: Column(
children: [
Container(
child: Row(
children: [
Expanded(
flex: 5,
child: Container(
padding: const EdgeInsets.all(5.0),
child: InkWell(
onTap: () {
setState(() {
heightKeyboard =
MediaQuery.of(context).viewInsets.bottom;
setDropdownMenuHeight(
heightAppBar, heightTextField, heightKeyboard);
// THESE PRINT DOES NOT SEEM TO PRINT ONTAP:
print("==========[InkWell]$heightDropdownMenu");
print("========[InkWell](kbdHt=)$heightKeyboard");
});
},
child: TextField(
key: _key02,
style: const TextStyle(
fontSize: 16,
),
controller: firstController,
keyboardType: TextInputType.number,
onChanged: (value) {
setState(() {
result = value;
/*
// The following gave error:
dimensions =
"${_key01?.currentContext.size.height}";
*/
dimensions =
"${_key01?.currentContext?.size?.height}";
});
},
),
),
),
),
Expanded(
flex: 5,
child: Container(
padding: const EdgeInsets.all(5.0),
child: DropdownMenu<String>(
menuHeight: heightDropdownMenu,
// menuHeight: 300,
textStyle: const TextStyle(
fontSize: 16.0,
),
initialSelection: dropdownValue,
onSelected: (String? value) {
print("==========[DropdownMenu]$heightDropdownMenu");
print(
"========[DropdownMenu](kbdHt=)$heightKeyboard");
setState(() {
// codes here
});
},
dropdownMenuEntries:
list.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(
value: value, label: value);
}).toList(),
),
),
),
],
),
),
Text("Result = $result"),
Text("Total physical height = $ph_height"),
Text("Total logical height = $height"),
Text("Onscreen Keyboard height = $keyboard_height"),
Text(
"Working Height Available (logical) = $effective_available_screen_height"),
Text("Dimensions: $dimensions"),
],
),
),
);
}
}
You can't use InkWell
because the TextField
will intercept the gesture, what you can do is use a FocusNode
, set it into the TextField
en listen when the focus change, then wait some milliseconds to wait the keyboard appears and recalculate the size.
Result:
Code:
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey _key01 = GlobalKey();
final GlobalKey _key02 = GlobalKey();
late final TextEditingController firstController;
late Size screenSize;
double screenWidth = 0;
double screenHeight = 0;
double heightAppBar = 0;
double heightTextField = 0;
double heightKeyboard = 0;
double heightOthersCummulative = 0;
double heightAdjustment = 60;
double heightDropdownMenu = 0;
void setDropdownMenuHeight(
double heightAppBar, double heightTextField, double heightKeyboard) {
if (heightAppBar > 0 && heightTextField > 0) {
heightDropdownMenu = screenHeight -
heightAppBar -
heightTextField -
heightKeyboard -
heightOthersCummulative -
heightAdjustment;
// return heightDropdownMenu;
} else {
heightDropdownMenu = 150;
// return heightDropdownMenu;
}
}
late String dropdownValue = list.first;
String result = "";
String effective_available_screen_height = "";
String dimensions = "";
FlutterView view = WidgetsBinding.instance.platformDispatcher.views.first;
late Size size;
double width = 0;
double height = 0;
double height01 = 0;
final FocusNode _focusNode = FocusNode();
@override
void initState() {
super.initState();
firstController = TextEditingController();
_focusNode.addListener(_onFocusChange);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
heightAppBar = _key01.currentContext?.size?.height ?? -1;
heightTextField = _key02.currentContext?.size?.height ?? -1;
heightKeyboard = MediaQuery.of(context).viewInsets.bottom;
setDropdownMenuHeight(heightAppBar, heightTextField, heightKeyboard);
dimensions =
"KeyboardHeight=$heightKeyboard\nDropdownMenuHeight=$heightDropdownMenu\nAppBarHeight=${_key01.currentContext?.size?.height}\nTextFieldHeight=${_key02.currentContext?.size?.height}";
});
});
}
void _onFocusChange() {
Future.delayed(const Duration(milliseconds: 500), () {
setState(() {
heightKeyboard = MediaQuery.of(context).viewInsets.bottom;
setDropdownMenuHeight(heightAppBar, heightTextField, heightKeyboard);
});
});
}
@override
void dispose() {
_focusNode.removeListener(_onFocusChange);
_focusNode.dispose();
firstController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size;
screenWidth = screenSize.width;
screenHeight = screenSize.height;
Size phSize = view.physicalSize;
double phHeight = phSize.height;
size = MediaQuery.of(context).size;
width = size.width;
height = size.height;
final padding = MediaQuery.of(context).viewPadding;
height01 = height - padding.top - padding.bottom;
effective_available_screen_height = "$height01";
// Height of Keyboard when onscreen (ohterwise zero):
double keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
return Scaffold(
appBar: AppBar(
key: _key01,
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
padding: const EdgeInsets.all(5.0),
child: Column(
children: [
Container(
child: Row(
children: [
Expanded(
flex: 5,
child: Container(
padding: const EdgeInsets.all(5.0),
child: TextField(
key: _key02,
style: const TextStyle(
fontSize: 16,
),
controller: firstController,
keyboardType: TextInputType.number,
focusNode: _focusNode,
onChanged: (value) {
setState(() {
result = value;
/*
// The following gave error:
dimensions =
"${_key01?.currentContext.size.height}";
*/
dimensions =
"${_key01.currentContext?.size?.height}";
});
},
),
),
),
Expanded(
flex: 5,
child: Container(
padding: const EdgeInsets.all(5.0),
child: DropdownMenu<String>(
menuHeight: heightDropdownMenu,
// menuHeight: 300,
textStyle: const TextStyle(
fontSize: 16.0,
),
initialSelection: dropdownValue,
onSelected: (String? value) {
print(
"==========[DropdownMenu]$heightDropdownMenu");
print(
"========[DropdownMenu](kbdHt=)$heightKeyboard");
setState(() {
// codes here
});
},
dropdownMenuEntries: list
.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(
value: value, label: value);
}).toList(),
),
),
),
],
),
),
Text("Result = $result"),
Text("Total physical height = $phHeight"),
Text("Total logical height = $height"),
Text("Onscreen Keyboard height = $keyboardHeight"),
Text(
"Working Height Available (logical) = $effective_available_screen_height"),
Text("Dimensions: $dimensions"),
],
),
),
),
);
}
}