Hello guys welcome to my post after a long time. In this post I will explain you how to move modal bottomsheet along with keyboard with two simple lines of codes.
Before proceeding full working sample app is hosted on github.
Source code (Github)
This is an issue that many new flutter developers are facing. The following
shows an example code section where we face the issue.
// main.dart showModalBottomSheet( isScrollControlled: true, context: context, builder: (_) { return BottomSheetScreen(onContactAdd: _contactAdded); });
// bottom_sheet.dart @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.zero, child: SingleChildScrollView( child: Container( padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0), child: Form( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ TextFormField( decoration: InputDecoration( labelText: "Name", ), controller: nameController, keyboardType: TextInputType.name, textInputAction: TextInputAction.next, ), SizedBox( height: 50, ), TextFormField( decoration: InputDecoration( labelText: "Phone No.", ), controller: phoneController, keyboardType: TextInputType.phone, textInputAction: TextInputAction.next, ), SizedBox( height: 50, ), DropdownButtonFormField( onChanged: (v) { setState(() { group = v; }); }, value: group, decoration: InputDecoration( labelText: "Contact Group", ), items: ContactGroup.values .map((e) => DropdownMenuItem( child: Text(e.name), value: e, )) .toList(), ), SizedBox( height: 50, ), SizedBox( height: 50.0, child: ElevatedButton( child: Text("Add new contact"), onPressed: () { widget.onContactAdd( Contact( name: nameController.text, phoneNo: int.parse(phoneController.text), group: group), ); Navigator.pop(context); }, ), ) ], ), ), ), ), ); }
Now, In order to achieve our goal we need to do the following things:
Step 1:
We need to add isScrollControlled = true to BottomSheetDialog which will allow the bottom sheet to take the full height and can be pushed by adding bottom adding.
// main.dart showModalBottomSheet( isScrollControlled: true, context: context, builder: (_) { return BottomSheetScreen(onContactAdd: _contactAdded); });
Step 2:
Add Keyboard padding using MediaQuery.of(context).viewInsets to the
outermost widget of child to BottomSheetDialog In this case I will add
it to padding as follows.
// bottom_sheet.dart @override Widget build(BuildContext context) { final MediaQueryData mediaQueryData = MediaQuery.of(context); return Padding( padding: mediaQueryData.viewInsets, // Rest of the contents here ); }
ALternatively, Please note that we can set the bottom padding only (like in code section below) as the bottom padding is responsible to push the keyboard up when textfield get keyboard focus. Its totally upto you to use any of the two. For this tutorial I will be using the first method.
// bottom_sheet.dart @override Widget build(BuildContext context) { final MediaQueryData mediaQueryData = MediaQuery.of(context); return Padding( padding: EdgeInsets.only(bottom: mediaQueryData.viewInsets.bottom), // Rest of the contents here ); }
Step 3: (This step is optional)
When using column we need to add mainAxisSize: MainAxisSize.min to
Column. As we know that by default Column height is unbounded which
means it takes as much height as it gets i.e. Full Available height. So in
order to prevent, we set mainAxisSize to MainAxisSize.min which
will take the required height by the child
// bottom_sheet.dart @override Widget build(BuildContext context) { final MediaQueryData mediaQueryData = MediaQuery.of(context); return Padding( padding: mediaQueryData.viewInsets, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ // Our Childrens ], ), ); }
Note: If we get content overflow error. We can alwasy warp it with SingleChildScrollView like in the code section below
// bottom_sheet.dart @override Widget build(BuildContext context) { final MediaQueryData mediaQueryData = MediaQuery.of(context); return Padding( padding: mediaQueryData.viewInsets, child: SingleChildScrollView(child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ // Our Childrens ], ), ), ); }The final code look like this
//main.dart showModalBottomSheet( isScrollControlled: true, context: context, builder: (_) { return BottomSheetScreen(onContactAdd: _contactAdded); });
// bottom_sheet.dart @override Widget build(BuildContext context) { final MediaQueryData mediaQueryData = MediaQuery.of(context); return Padding( padding: mediaQueryData.viewInsets, child: SingleChildScrollView( child: Container( padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0), child: Form( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ TextFormField( decoration: InputDecoration( labelText: "Name", ), controller: nameController, keyboardType: TextInputType.name, textInputAction: TextInputAction.next, ), SizedBox( height: 50, ), TextFormField( decoration: InputDecoration( labelText: "Phone No.", ), controller: phoneController, keyboardType: TextInputType.phone, textInputAction: TextInputAction.next, ), SizedBox( height: 50, ), DropdownButtonFormField( onChanged: (v) { setState(() { group = v; }); }, value: group, decoration: InputDecoration( labelText: "Contact Group", ), items: ContactGroup.values .map((e) => DropdownMenuItem( child: Text(e.name), value: e, )) .toList(), ), SizedBox( height: 50, ), SizedBox( height: 50.0, child: ElevatedButton( child: Text("Add new contact"), onPressed: () { widget.onContactAdd( Contact( name: nameController.text, phoneNo: int.parse(phoneController.text), group: group), ); Navigator.pop(context); }, ), ) ], ), ), ), ), ); }
At last you can wrap the entire child with AnimatedPadding to give a smooth transition effect when keyboard pops up. You can also use AnimatedCpntainer to get the same behaviour.
// bottom_sheet.dart @override Widget build(BuildContext context) { final MediaQueryData mediaQueryData = MediaQuery.of(context); return AnimatedPadding( duration: kThemeAnimationDuration, padding: mediaQueryData.viewInsets, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ // Our Childrens ], ), ); }
Thank you for going through entire tutorial. Happy Coding 🙋😀