Flutter - Move Bottomsheet Along With Keyboard Which Has Textfield


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 🙋😀

Post a Comment

Previous Post Next Post