当键盘出现时,Flutter 小部件会调整大小。如何防止这种情况?

When the keyboard appears, the Flutter widgets resize. How to prevent this?

我有一列这样的扩展小部件:

 return new Container(
      child: new Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          new Expanded(
            flex: 1,
            child: convertFrom,
          ),
          new Expanded(
            flex: 1,
            child: convertTo,
          ),
          new Expanded(
            flex: 1,
            child: description,
          ),
        ],
      ),
    );

看起来像这样:

convertFrom,包含一个 TextField。当我点击此文本字段时,屏幕上会出现 Android 键盘。这会更改屏幕大小,因此小部件会像这样调整大小:

有没有办法让键盘 "overlay" 显示在屏幕上,这样我的列就不会调整大小?如果我不使用 Expanded 小部件并为每个小部件硬编码高度,则小部件不会调整大小,但是当键盘出现时我会收到黑黄条纹错误(因为没有足够的 space).这也不适用于所有屏幕尺寸。

我不确定这是 Android 特定的还是 Flutter 特定的。

更新答案

resizeToAvoidBottomPadding 现在 已弃用

更新的解决方案是将 resizeToAvoidBottomInset 属性 设置为 false


原答案

在您的 Scaffold 中,将 resizeToAvoidBottomPadding 属性 设置为 false

根据用例,您还可以考虑在图库应用程序中使用 listview. That would ensure that the contents scroll when there is not enough room. As an example, you can look at the textfield 演示

设置 resizeToAvoidBottomInset to false instead of resizeToAvoidBottomPadding 已弃用。

    return Scaffold(
      resizeToAvoidBottomInset : false,
      body: YourWidgets(),
    );

方法1:从AndroidManifest.xml文件中删除android:windowSoftInputMode="adjustResize"(否则会覆盖flutter代码)并在Scaffold中添加resizeToAvoidBottomPadding: false,如下所示:

Scaffold(
      resizeToAvoidBottomPadding: false,
      appBar: AppBar()
)

方法二(不推荐): 只需在android中添加android:windowSoftInputMode="stateVisible" activity中的AndroidManifest.xml即可对于 Android 不适合 IOS 喜欢。

<activity
       ...
        android:windowSoftInputMode="stateVisible">

注意:不要设置为android:windowSoftInputMode="adjustResize"

大多数其他答案建议使用 resizeToAvoidBottomPadding=false。根据我的经验,如果文本字段位于键盘出现的位置下方,这允许键盘覆盖文本字段。

我目前的解决方案是强制我的列与屏幕高度相同,然后将其放在 SingleChildScrollView 中,这样当使用键盘时 Flutter 会自动将我的屏幕向上滚动。

Widget build(BuildContext context) {
  return Scaffold(
    body: SingleChildScrollView(
      physics: NeverScrollableScrollPhysics(),
      child: ConstrainedBox(
        constraints: BoxConstraints(
          minWidth: MediaQuery.of(context).size.width,
          minHeight: MediaQuery.of(context).size.height,
        ),
        child: IntrinsicHeight(
          child: Column(
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              // CONTENT HERE
            ],
          ),
        ),
      ),
    ),
  );
}

我使用 NeverScrollableScrollPhysics 这样用户就不能自己滚动。

好吧,我认为如果我们实施@Aman 的解决方案,它会使我们的应用程序在键盘出现时表现得很丑陋,它不会根据可用高度调整我们的屏幕视口,并且会使其他字段隐藏在键盘。所以我建议改用SingleChildScrollView

SingleChildScrollView 包装您的代码,如下所示,

 return new Container(
  child: SingleChildScrollView(
    child: new Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: <Widget>[
      new Expanded(
        flex: 1,
        child: convertFrom,
      ),
      new Expanded(
        flex: 1,
        child: convertTo,
      ),
      new Expanded(
        flex: 1,
        child: description,
      ),
    ],
  ),
 ),
);

我的方法是使用 SingleChildScrollViewClampingScrollPhysics 物理学。

SingleChildScrollView(
  physics: ClampingScrollPhysics(),
  child: Container(),
)

对我来说,将下面的项目 属性 从 true 更改为 false

<item name="android:windowFullscreen">false</item>

在文件中

android/app/src/main/res/values/styles.xml

已使 Flutter 在输入焦点上向上拖动所有页面内容

我的建议是无论如何都使用 resizeToAvoidBottomInset: false 以防止在键盘突然出现在屏幕上时小部件调整大小。例如,如果用户在您的应用中使用 Facebook 聊天头像。

为了防止键盘覆盖小部件,在您需要的屏幕上,我建议采用以下方法,其中 SingleChildScrollView 的高度降低到可用的 space 的高度。在这种情况下,SingleChildScrollView 也会滚动到焦点小部件。

final double screenHeight = MediaQuery.of(context).size.height;
final double keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
return Scaffold(
  resizeToAvoidBottomInset: false,
  body: SizedBox(
    height: screenHeight - keyboardHeight,
    child: SingleChildScrollView(
      child: Column(
        children: [
          const SizedBox(height: 200),
          for (var i = 0; i < 10; i++) const TextField()
        ],
      ),
    ),
  ),
);

我遇到了同样的问题,我开始尝试随机解决方案来修复它,突然间就解决了。

将主父容器包裹在 SingleChildScrollView() 中并为其指定设备高度,即 device_height = MediaQuery.of(context).size.height.

这将使整个页面可滚动,但不会调整任何小部件的大小。

避免调整小部件大小并且关注文本字段的最佳解决方案是将SingleChildScrollView()与[=一起使用12=] 物理学。另外,请记住为其 child 设置高度(例如:使用 container()),这样您就可以通过 [=14] 使用您的小部件=]:

return Scaffold(
   body: SingleChildScrollView(
      physics: ClampingScrollPhysics(),
      child: Container(
         height: size.height,
         child: Column(
            children:[
               TextFormField()
            ],
         ),
      ),
   ),
);

resizeToAvoidBottomInset 设置值 false 对我来说效果很好。

此外,resizeToAvoidBottomPadding 对我来说效果很好。你可以使用任何一个。

这是 完美 解决方案,让您能够在 中拥有 全屏列 SingleChildScrollView。这允许您为 所有屏幕尺寸 创建一个完美的布局 + 如果您打开键盘或如果渲染后屏幕溢出(例如文本输入字段验证)

class PerfectFullScreen extends StatelessWidget {
  const PerfectFullScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
      backgroundColor: Theme.of(context).backgroundColor,
      appBar: AppBar(),
      body: Builder(
        builder: (context) => SingleChildScrollView(
            child: ConstrainedBox(
                constraints: BoxConstraints(
                    minHeight: MediaQuery.of(context).size.height -
                        (MediaQuery.of(context).padding.top + kToolbarHeight)),
                child: IntrinsicHeight(
                    child: Column(
                  children: [
                    Container(
                      height: randomImageHeight,
                      child: Image.asset(
                        "assets/images/change_password.png",
                        fit: BoxFit.cover,
                      ),
                    ),
                    Expanded(
                        child: WidgetThatShouldTakeRemainingSpace() )
                  ],
                )))),
      ),
    ));
  }
}

重要的部分是 ConstrainedBox 以及正确的 BoxConstraintsInstrinsicHeight 小部件。

PS: (MediaQuery.of(context).padding.top + kToolbarHeight) == Appbar的高度

可能来不及回答,但以下对我有用

Scaffold(
  body: SingleChildScrollView(
    physics: ClampingScrollPhysics(parent: NeverScrollableScrollPhysics()),
      child: Container(

钳位将自动滚动以使文本字段可见,其父级 NeverScrollable 将不允许用户滚动。

特征:

  • 打开键盘时背景图像不会调整大小
  • 能够滚动隐藏在键盘后面的元素
import 'package:flutter/material.dart';

SizedBox addPaddingWhenKeyboardAppears() {
  final viewInsets = EdgeInsets.fromWindowPadding(
    WidgetsBinding.instance!.window.viewInsets,
    WidgetsBinding.instance!.window.devicePixelRatio,
  );

  final bottomOffset = viewInsets.bottom;
  const hiddenKeyboard = 0.0; // Always 0 if keyboard is not opened
  final isNeedPadding = bottomOffset != hiddenKeyboard;

  return SizedBox(height: isNeedPadding ? bottomOffset : hiddenKeyboard);
}

/// The size of the screen.
class ScreenSizeService {
  final BuildContext context;

  const ScreenSizeService(
    this.context,
  );

  Size get size => MediaQuery.of(context).size;
  double get height => size.height;
  double get width => size.width;
}

class LoginPage extends StatelessWidget {
  final _imageUrl =
      'https://images.unsplash.com/photo-1631823460501-e0c045fa716f?ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60';

  const LoginPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final screenWidth = ScreenSizeService(context).width;
    final screenHeight = ScreenSizeService(context).height;

    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: Container(
        decoration: BoxDecoration(
          image: DecorationImage(
            image: NetworkImage(_imageUrl),
            fit: BoxFit.cover,
          ),
        ),
        child: SingleChildScrollView(
          child: ConstrainedBox(
            constraints: BoxConstraints(
              minWidth: screenWidth,
              minHeight: screenHeight,
            ),
            child: Column(
              children: [
                ...List.generate(6, (index) {
                  return Column(
                    children: [
                      Container(
                        height: 60,
                        width: double.maxFinite,
                        color: Colors.pink[100],
                        child: Center(child: Text('$index')),
                      ),
                      const SizedBox(height: 40),
                    ],
                  );
                }),
                Container(color: Colors.white, child: const TextField()),
                addPaddingWhenKeyboardAppears(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

这将随键盘一起滚动,并在键盘消失时折叠大小。

showModalBottomSheet(
      isScrollControlled: true,
      context: context,
      builder: (context) {
        return Padding(
            padding: MediaQuery.of(context).viewInsets,
            child:SingleChildScrollView(
            physics: ClampingScrollPhysics(),
            child: Container(.......)));