如何将底页与具有文本字段的键盘一起移动(自动对焦为真)?

Posted

技术标签:

【中文标题】如何将底页与具有文本字段的键盘一起移动(自动对焦为真)?【英文标题】:How to Move bottomsheet along with keyboard which has textfield(autofocused is true)? 【发布时间】:2019-05-20 23:58:26 【问题描述】:

我正在尝试制作一个包含文本字段且自动对焦设置为 true 的底页,以便弹出键盘。但是,底页与键盘重叠。有没有办法将底页移到键盘上方?

Padding(
  padding:
      EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
  child: Column(children: <Widget>[
    TextField(
      autofocus: true,
      decoration: InputDecoration(hintText: 'Title'),
    ),
    TextField(
      decoration: InputDecoration(hintText: 'Details!'),
      keyboardType: TextInputType.multiline,
      maxLines: 4,
    ),
    TextField(
      decoration: InputDecoration(hintText: 'Additional details!'),
      keyboardType: TextInputType.multiline,
      maxLines: 4,
    ),]);

【问题讨论】:

Github 上还有一些未解决的问题:main issue 和 another 带有提供动画解决方案的评论。 对我来说,只需添加一个Padding 元素作为Column 的最后一个子元素并将其填充设置为padding: MediaQuery.of(context).viewInsets 即可解决此问题 【参考方案1】:

isScrollControlled = true 添加到BottomSheetDialog 它将允许底部表格占据所需的全部高度,从而更加确保TextField 不会被键盘覆盖。

使用MediaQuery.of(context).viewInsets.bottom添加键盘填充

注意

如果您的BottomSheetModelColumn,请确保添加mainAxisSize: MainAxisSize.min,,否则表格将覆盖整个屏幕。

示例

 showModalBottomSheet(
    shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(25.0))),
    backgroundColor: Colors.black,
    context: context,
    isScrollControlled: true,
    builder: (context) => Padding(
      padding: const EdgeInsets.symmetric(horizontal:18 ),
      child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 12.0),
                child: Text('Enter your address',
                    style: TextStyles.textBody2),
              ),
              SizedBox(
                height: 8.0,
              ),
              Padding(
                padding: EdgeInsets.only(
                    bottom: MediaQuery.of(context).viewInsets.bottom),
                child: TextField(
                  decoration: InputDecoration(
                    hintText: 'adddrss'
                  ),
                  autofocus: true,
                  controller: _newMediaLinkAddressController,
                ),
              ),

              SizedBox(height: 10),
            ],
          ),
    ));

请注意:

shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(25.0))),

这不是必需的。只是我正在创建一个圆形底片。

padding: MediaQuery.of(context).viewInsets

更新 Flutter 2.2 再次打破了这些变化!”现在您需要再次提供底部填充,这样键盘就不会与底部表重叠。

【讨论】:

应用后,底部的工作表占据了整个屏幕。通过为底部工作表的 Container 小部件的 height 属性放置一个静态值来解决它。 @Abed 我试过你的方法。它对我的问题不起作用吗..请检查***.com/q/58240592/8822337 2021 年!你现在不需要它:padding: MediaQuery.of(context).viewInsets 在 showModalBottomSheet 上添加“isScrollControlled = true”,在 Column 小部件上添加“mainAxisSize: MainAxisSize.min”解决了我的所有问题。谢谢阿贝德。 当输入聚焦在可滚动模式中时如何添加自动滚动?【参考方案2】:

只需添加:

isScrollControlled: true 显示ModalBottomSheet padding: MediaQuery.of(context).viewInsets 构建器中的小部件 列/包装两个作品
showModalBottomSheet<void>(
  isScrollControlled: true,
  context: context,
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.only(
        topLeft: Radius.circular(30.0),
        topRight: Radius.circular(30.0)),
  ),
  builder: (BuildContext context) 
    return Padding(
        padding: MediaQuery.of(context).viewInsets,
        child: Container(
            child: Wrap(
          children: <Widget>[
            TextField(
              decoration: InputDecoration(
                  border: InputBorder.none,
                  hintText: 'Enter a search term'),
            ),
            TextField(
              decoration: InputDecoration(
                  border: InputBorder.none,
                  hintText: 'Enter a search term'),
            ),
            TextField(
              decoration: InputDecoration(
                  border: InputBorder.none,
                  hintText: 'Enter a search term'),
            ),
            TextField(
              decoration: InputDecoration(
                  border: InputBorder.none,
                  hintText: 'Enter a search term'),
            )
          ],
        )));
  ,
);

2020 年 2 月 25 日更新更好的解决方案

showModalBottomSheet(
 isScrollControlled: true,
 builder: (BuildContext context) 

    return SingleChildScrollView(
      child: Container(
        padding:
            EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
        child: Padding(
          padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0), // content padding
          child: Form(...)) // From with TextField inside


);

【讨论】:

只是给每个人的提示:对于所提出的问题,不需要 Wrap 小部件,因为问题在于 bottomSheet 能够在键盘上方移动,而不是其自身的底部工作表的内容。 它对我有用【参考方案3】:

为了专注于BottomSheet 中的键盘 - 将TextField 包裹在 Padding 小部件中,如下所示,例如代码:

showModalBottomSheet(
              context: context,
              builder: (context) 
                return Container(
                  child: Padding(
                    padding: EdgeInsets.only(
                        bottom: MediaQuery.of(context).viewInsets.bottom),
                    child: TextField(
                      autofocus: true,
                    ),
                  ),
                );
              ); 

【讨论】:

您好,谢谢。这适用于两个子字段,但在我们添加第三个字段时无效。我用我的代码示例编辑了问题。 @ManjunathRao 您可能需要在文本字段中添加“密钥”以保持它们的唯一性,以便获得超过 2 个。如果这有帮助,请告诉我 BottomSheet - 具有maxHeight: constraints,因为字段正在增加,高度不会增加。您可以在 BottomSheet 中使用 ListView 使字段在滚动中可见。或者你可以试试这个 - ***.com/questions/53311553/… @ManjunathRao - 你不会在 BottomSheet 中获得两个以上的字段 - 由于maxHeight: constraints【参考方案4】:

在最新版本的颤振中,您可以使用isScrollControlled 属性/命名参数移动您的底部表。假设我有一个函数(_showModal),它会在按下按钮时调用。我在该函数上定义了底部工作表功能。

void _showModal() 
  showModalBottomSheet(
    isScrollControlled: true,
    context: context,
    builder: (BuildContext context) 
      return Column(
        children: <Widget>[
          TextField(// your other code),
          SizedBox(height: 5.0),
          TextField(// your other code),
          SizedBox(height: 5.0),
          TextField(// your other code),
        ]
      );
    ,
  );

这里会出现一个 ModalBottomSheet,但高度全屏。而且你不需要那个高度。所以,你需要将Column的mainAxisSize改为min

Column(
  mainAxisSize: MainAxisSize.min,
  // your other code
)

解决了全屏高度问题,但出现键盘时 ModalBottomSheet 不会移到顶部。好的,要解决此问题,您需要将 viewInsets 底部填充设置为您的 ModalBottomSheet。所以要设置填充,我们需要用 Container 或 Padding 包裹我们的列,然后设置填充。最终代码如下所示

void _showModal() 
  showModalBottomSheet(
    isScrollControlled: true,
    context: context,
    builder: (BuildContext context) 
      return Container(
        padding: EdgeInsets.only(
          bottom: MediaQuery.of(context).viewInsets.bottom,
        ),
        // You can wrap this Column with Padding of 8.0 for better design
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            TextField(// your other code),
            SizedBox(height: 5.0),
            TextField(// your other code),
            SizedBox(height: 5.0),
            TextField(// your other code),
          ]
        ),
      );
    ,
  );

希望您的问题得到解决。谢谢?

【讨论】:

像魅力一样工作......!?【参考方案5】:

试试这个

我的解决方案是

使用isScrollControlled: true 添加填充
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom)
将您的布局包裹在SingleChildScrollView

示例代码

Future<void> future = showModalBottomSheet(
  context: context,
  isDismissible: true,
  isScrollControlled: true,
  backgroundColor: Colors.white.withOpacity(0.2),
  builder: (context) => SingleChildScrollView(
    child: GestureDetector(
      child: Padding(
        padding: EdgeInsets.only(
          bottom: MediaQuery.of(context).viewInsets.bottom
        ),
        child: Container(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
          child: Column(
            children: <Widget>[
              // add your widget here
            ],
          ),
        ),
      )
    ),
  )
);

【讨论】:

SingleChildScrollView 的包装点很重要。谢谢【参考方案6】:

我通过在打开键盘时增加子小部件的高度来修复它。 MediaQuery.of(context).viewInsets.bottom 的初始值为 0,当键盘获得焦点时它会改变。

showModalBottomSheet<void>(
      enableDrag: true,
      isScrollControlled: true,
      context: context,
      builder: (BuildContext context) 
        return Card(
          color: Colors.white,
          child: Container(
            height: MediaQuery.of(context).size.height / 2 +
                MediaQuery.of(context).viewInsets.bottom,
            child: Column(
              children: <Widget>[
                TextField(),
                TextField(),
              ],
            ),
          ),
        );
      ,
    );

【讨论】:

【参考方案7】:

包裹:https://pub.dev/packages/modal_bottom_sheet

将您的小部件包装到 Padding 中并像这样设置填充 ==>

padding: MediaQuery.of(context).viewInsets // viewInsets will decorate your screen

你可以使用 showMaterialModalBottomSheet 或 showModalBottomSheet 或 showCupertinoModalBottomSheet

showModalBottomSheet(
        context: context,
        barrierColor: popupBackground,
        isScrollControlled: true, // only work on showModalBottomSheet function
        shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.only(
                topLeft: Radius.circular(borderRadiusMedium),
                topRight: Radius.circular(borderRadiusMedium))),
        builder: (context) =>  Padding(
            padding: MediaQuery.of(context).viewInsets,
            child: Container(
                   height: 400, //height or you can use Get.width-100 to set height
                   child: <Your Widget here>
             ),)),)

【讨论】:

【参考方案8】:

2021 年 5 月更新 2.2! 现在你需要给底部填充。下面写的是一个错误。

2020 年更新!

这个answer 是真的,但你现在不必给底部填充! 查找并删除此行:

padding: MediaQuery.of(context).viewInsets

【讨论】:

谢谢,好提示。您的解决方案对我有用(Flutter 稳定版 60bd88df91)。但是在github.com/flutter/flutter/commits/master/packages/flutter/lib/… 处查看参考 a5262(还原“[showModalBottomSheet] 修复:showModalBottomSheet 不会沿键盘移动”)。所以我认为我们可能需要在即将到来的 Flutter 更新中跟踪问题。 已确认,flutter 2.2 再次打破了这一点,您再次需要 MediaQuery。【参考方案9】:

如果您有全屏或固定尺寸showModalBottomSheet,请不要使用padding,它不会解决您的问题。像这样使用margin 而不是padding

  showModalBottomSheet(
          context: context,
          builder: (context) 
            return Container(
                marign: EdgeInsets.only(
                    bottom: MediaQuery.of(context).viewInsets.bottom),
                child: TextField()
            );
          ); 

【讨论】:

【参考方案10】:

对于那些尝试所有答案都无法解决问题的人。这些答案是正确的,但不是很清楚。

使用时

MediaQuery.of(context).viewInsets.bottom)

确保您的上下文变量使用的是底部工作表生成器属性提供的变量。

builder :(**c**)=&gt;MediaQuery.of(**c**)

【讨论】:

【参考方案11】:

结合不同的解决方案后,我得到了这个:

如果您不希望它是 全屏 并且不想使用 Padding 解决方法,请使用

  showModalBottomSheet(
      context: context,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
      ),
      enableDrag: true,
      isDismissible: true,
      useRootNavigator: true,
      builder: (BuildContext ctx) 
        return Scaffold( // use CupertinoPageScaffold for ios
          backgroundColor: Colors.transparent,
          resizeToAvoidBottomInset: true, // important
          body: SingleChildScrollView(
            child: Form(
              child: Container(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: <Widget>[
                    TextFormField(),
                    TextFormField(),
                  ],
                ),
              ),
            ),
          ),
        );
      ,
    );

关于 Flutter(Channel master,v1.15.3-pre.37,Mac OS X 10.15.2 19C57,语言环境 en-US)

【讨论】:

【参考方案12】:

不要在构建器中使用builder: (BuildContext context) ,而是使用builder: (context)

使用此解决方案,我的模态底部工作表会粘在状态栏上(类似于 ScaffoldresizeToAvoidBottomInset: false),并允许查看所有表单字段并在仍需要查看底部文本字段时滚动表单。

有关更多详细信息,这是我找到解决方案的链接-https://github.com/flutter/flutter/issues/18564#issuecomment-602604778

【讨论】:

【参考方案13】:

在github找到这个

Padding(
  padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
  child: TextField()
)

【讨论】:

【参考方案14】:

试试这个。

showModalBottomSheet(
        isScrollControlled: true,
        context: context,
        builder: (context) 
          return AnimatedPadding(
              padding: MediaQuery.of(context).viewInsets,
              duration: const Duration(milliseconds: 100),
              curve: Curves.decelerate,
              child: Container(
                  child: Wrap(
                children: [
                  TextField(
                    decoration: InputDecoration(labelText: "1"),
                  ),
                  TextField(
                    decoration: InputDecoration(labelText: "2"),
                  ),
                  TextField(
                    decoration: InputDecoration(labelText: "3"),
                  ),
                ],
              )));
        ,
      )

【讨论】:

【参考方案15】:

Scaffold 小部件包裹Form,然后用SingleChildScrollView 包裹TextFormField


 return Container(
          height: screenHeight * .66,
          child: Scaffold(
             body: Form(
               key: _form,
               child: SingleChildScrollView(
                 child:TextFormField()
               )
              )
             )
           )

【讨论】:

Scaffold(child:?【参考方案16】:
showModalBottomSheet(
 isScrollControlled: true,
 builder: (BuildContext context) 

    return SingleChildScrollView(
      child: Container(
        padding:
            EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
        child: Padding(
          padding: const EdgeInsets.all(15.0), // content padding
          child: Container()));

注意:这条线可以发挥所有作用

【讨论】:

【参考方案17】:

然后你被命令使用这个,

showModalBottomSheet(
    isScrollControlled: true,
    context: context,
    shape: RoundedRectangleBorder(
      // <-- for border radius
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(10.0),
        topRight: Radius.circular(10.0),
      ),
    ),
    builder: (BuildContext context) 
      return SingleChildScrollView(
        padding:
            EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
        child: drunkenWidget()...

//BTW, Never ever Drink

【讨论】:

【参考方案18】:

在底部表格的最后一个小部件之后添加它

Padding(padding: EdgeInsets.only(bottom:MediaQuery.of(context).viewInsets.bottom))

【讨论】:

【参考方案19】:

简单的解决方案,您可以自定义:

Container(
          margin: EdgeInsets.only(left: 15),
          child: InkWell(
              onTap: () 
                showModalBottomSheet(
                    isScrollControlled : true,
                    context: context,
                    backgroundColor: Colors.transparent,
                    builder: (context) 
                      return Container(
                        padding: EdgeInsets.only(top: 15, left: 15, right: 15, bottom: 10),
                        width: double.infinity,
                        decoration: BoxDecoration(
                          color: AppTheme.leadItemColor1,
                          borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
                        ),
                        child: Column(
                          children: [
                            _assignTo(widget.viewModel, context),
                            SizedBox(height: 12,),
                            txtComment(widget.viewModel),
                            SizedBox(height: 12,),
                            CRMButton(
                              title: 'Select',
                              onTap: () async 
                                Navigator.pop(context);
                                await widget.viewModel.updateStatus(7, why: "$ConstantData.lostOptions[_selectedNumber]");
                              ,
                            )
                          ],
                        ),
                      );
                    ,
                );
              ,
              child: CustomTabBarItem1(
                image: widget.viewModel.leadDetail.success.lStatus == 7 ? 'assets/appimages/LeadDetail/icons-03-01.png' : 'assets/appimages/LeadDetail/icons-04-01.png',
                bottomTitle: 'Lost',
                topTitle: widget.viewModel.leadDetail.success.lStatus > 7 ? 'assets/appimages/LeadDetail/Ellipse 61@2x.png' : widget.viewModel.leadDetail.success.lStatus == 7 ? 'assets/appimages/LeadDetail/Group 486-1.png' : 'assets/appimages/LeadDetail/Ellipse-61@3x.png',
                height : widget.viewModel.leadDetail.success.lStatus == 7 ? "0" : "1",
              )),
        ),

【讨论】:

【参考方案20】:

只需添加

if (_isEditing) SizedBox(height: MediaQuery.of(context).viewInsets.bottom),

在键盘下方并用

隐藏文本字段下方的所有其他内容
if (!_isEditing) Widget(...),

【讨论】:

以上是关于如何将底页与具有文本字段的键盘一起移动(自动对焦为真)?的主要内容,如果未能解决你的问题,请参考以下文章

移动 Safari 自动对焦文本字段

Sencha Touch自动对焦在文本字段上

iOS webview自动对焦在键盘打开时不起作用

如何在自动对焦中隐藏离子键盘

关闭键盘无法与 PickerView 一起使用

如何使底栏随键盘向上移动(颤动)