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

278

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

 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 包含一个文本框。当我点击该文本框时,Android键盘会出现在屏幕上。这会改变屏幕大小,因此小部件会像这样调整大小:

在此输入图片描述

有没有办法让键盘“覆盖”屏幕,使我的Column不重新调整大小?如果我不使用Expanded小部件并为每个小部件硬编码高度,则小部件不会重新调整大小,但是当键盘出现时,我会收到黑黄相间的错误(因为没有足够的空间)。这对于所有屏幕尺寸也不够灵活。

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


1
在你的 Scaffold 主体中使用一个 SingleChildScrollView。 - linhadiretalipe
这个例子将帮助您 - Flutter登录页面带有背景图片。链接:https://androidride.com/flutter-background-image/#bg_login_page - Vijay Ram
对于未来的读者:如果可能的话,请删除脚手架,这将解决问题。 - PRANAV SINGH
24个回答

639

更新的答案

resizeToAvoidBottomPadding现在已被弃用

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


原来的答案

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


24
有没有类似于 android:windowSoftInputMode="adjustPan" 的方法可以产生相似的效果?如果我将 resizeToAvoidBottomPadding 添加到 scaffold 中,会导致 TextField 被覆盖。 - Nathan Bird
4
在这种情况下,将布局放在ListView中通常是一种很好的实践方法,但对于Android我不是很了解。 - Shady Aziza
@KishanVyas 我也是,图片不见了。 - stuckedoverflow
4
resizeToAvoidBottomPadding已被弃用,请使用resizeToAvoidBottomInset查看此答案 - ArtiomLK
2
这与问题中所建议的完全不同。键盘下面的所有小部件仍然会调整大小。 - Bisclavret
显示剩余2条评论

122

大多数其他答案建议使用resizeToAvoidBottomPadding=false。但在我的经验中,如果文本字段位于键盘将出现的位置下方,则会导致键盘覆盖文本字段。

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

Widget build(BuildContext context) {
  return Scaffold(
    body: SingleChildScrollView(
      physics: const 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 来防止用户自己滚动。


10
我尝试了你的方法,不使用 NeverScrollableScrollPhysics 一切看起来都很好,除了用户可以尝试向上或向下滑动,并且他们能看到过度滚动效果。当我使用 NeverScrollableScrollPhysics 时,它给我带来了与 resizeToAvoidBottomInset 相同的行为。 - Sajad Jaward
13
使用NeverScrollableScrollPhysics可以防止它随着键盘一起向上滚动。 - hewa jalal
10
要使其随键盘向上滚动,请删除SingleChildScrollView小部件中的NeverScrollableScrollPhysics()行,或将ClampingScrollPhysics()替换为此项。 - Supun Dewapriya
2
我在使用IntrinsicHeight Widget的时候和不使用它的时候遇到了同样的问题。文档中建议避免使用它:“这个类比较耗费性能。尽可能避免使用它。” - henriqueor
SingleChildScrollView完美地解决了我的问题,即键盘覆盖了我的TextField,导致无法看到输入的内容。 - JChen___
显示剩余2条评论

40

4
@Ojonugwa,它们是相同的答案。也许在我回答后编辑人员更新了被接受的答案,所以现在的区别在于我的答案包含一个例子? - ArtiomLK

37

我的方法是使用SingleChildScrollView,并使用ClampingScrollPhysics物理引擎。

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

20

我的建议是始终使用resizeToAvoidBottomInset: false,以防止小部件在键盘突然出现时重新调整大小。例如,如果用户在您的应用中使用Facebook聊天头。

为了防止键盘覆盖小部件,在需要的屏幕上,我建议采用以下方法,其中SingleChildScrollView的高度减小到可用空间的高度。在这种情况下,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()
        ],
      ),
    ),
  ),
);

10
避免调整小部件大小并专注于文本字段的最佳解决方案是使用带有ClampingScrollPhysics()物理效应的SingleChildScrollView()。此外,记得为其子元素设置高度(例如使用container()),这样您就可以通过Column()使用小部件:
return Scaffold(
   body: SingleChildScrollView(
      physics: ClampingScrollPhysics(),
      child: Container(
         height: size.height,
         child: Column(
            children:[
               TextFormField()
            ],
         ),
      ),
   ),
);

8
可能已经太晚了,但是下面的方法对我有用。
Scaffold(
  body: SingleChildScrollView(
    physics: ClampingScrollPhysics(parent: NeverScrollableScrollPhysics()),
      child: Container(

夹紧(Clamping)将自动滚动以使文本字段可见,其父级组件 NeverScrollable 不允许用户滚动。


7

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

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

方法2(不推荐):只需在AndroidManifest.xml文件中的活动中添加android:windowSoftInputMode="stateVisible"即可,但仅适用于Android,而不适用于像IOS这样的其他操作系统。

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

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


6

功能:

  • 当键盘打开时,背景图像不会缩放
  • 能够滚动键盘后面被隐藏的元素
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(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

6

resizeToAvoidBottomInset设置值为false对我很有效。

resizeToAvoidBottomPadding也对我很有效。你可以使用其中任何一个。


1
你不能使用其中任何一个,后者在Flutter v1.1.9中已被弃用。 https://docs.flutter.dev/release/breaking-changes/1-22-deprecations#scaffoldresizetoavoidbottompadding - DarkMikey
3
如果你能提供那个东西应该放在哪里的信息,会更有帮助。 - West
@West 你可以在 Scaffold Widget 内设置该值。 - Amimul Ihsan
@DarkMikey 你是对的。现在,你需要为 resizeToAvoidBottomInset 设置值。 - Amimul Ihsan
1
是的!这个解决方案完美地解决了我的问题,谢谢 :) - Anteino
@Anteino 非常欢迎你。继续使用Flutter并分享你的学习经验。 - Amimul Ihsan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接