【问题标题】:Flutter - Textfield not showing up when keyboard appears on androidFlutter - 当键盘出现在android上时文本字段不显示
【发布时间】:2020-12-11 12:06:39
【问题描述】:

我的问题是,当我专注于文本字段时,键盘会出现,但布局不会调整大小以将其保留在视图中。这个问题只存在于 android 而不是 iOS:这里是我的意思的截图:

在 android 上之前和之后:

iOS 之前和之后:

这是我的课:

class PlayerSelectionPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int itemCount = Provider.of<PlayerProvider>(context).getPlayerList.length;
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.portraitUp,
    ]);
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context).translate('player_selection_page_title')),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: (){
          HapticFeedback.mediumImpact();
          Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage(), settings: RouteSettings(name: 'Home page')));
        },
        child: Icon(
          Icons.chevron_right,
          size: 30,
          color: Colors.white,
        ),
      ),
      body: itemCount > 0 ? ListView.builder(
          itemCount: itemCount,
          itemBuilder: (context, index) {
            return Column(
              children: [
                PlayerDismissible(index),
                Divider(
                  height: 0,
                )
              ],
            );
          }) : Container(
          padding: EdgeInsets.all(20),
          alignment: Alignment.topCenter,
          child: Text(AppLocalizations.of(context).translate('player_selection_page_empty_text'), textAlign: TextAlign.center, style: Theme.of(context).textTheme.subtitle2)
      ),
      bottomSheet: BottomPlayerBar(),
    );
  }
}

这是我的 BottomPlayerBar() :

class BottomPlayerBar extends StatefulWidget{
  @override
  _BottomPlayerBarState createState() => _BottomPlayerBarState();
}

class _BottomPlayerBarState extends State<BottomPlayerBar> {
  String playerName;
  FocusNode myFocusNode;


  @override
  void initState() {
    super.initState();
    myFocusNode = FocusNode();
    SchedulerBinding.instance.addPostFrameCallback((_) {
      if (ModalRoute.of(context).isCurrent) {
        myFocusNode.requestFocus();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        height: 80, color: Theme.of(context).primaryColor,
        padding: EdgeInsets.only(top: 20, bottom: 25, left: 20, right: 70),
        child: TextField(
          focusNode: myFocusNode,
          textCapitalization: TextCapitalization.words,
          onChanged: (val) => playerName = val.trim(),
          onSubmitted: (val) {
            if (playerName != null && playerName != '') {
              Provider.of<PlayerProvider>(context, listen: false).addPlayer(playerName);
              HapticFeedback.lightImpact();
              myFocusNode.requestFocus();
            } else {
              myFocusNode.unfocus();
            }
          },
          maxLength: 19,
          autocorrect: false,
          decoration: new InputDecoration(
              counterText: "",
              border: new OutlineInputBorder(
                borderSide: BorderSide.none,
                borderRadius: const BorderRadius.all(
                  const Radius.circular(30.0),
                ),
              ),
              filled: true,
              contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
              hintStyle: GoogleFonts.rubik(color: Colors.grey[500], fontWeight: FontWeight.bold),
              hintText: AppLocalizations.of(context).translate('player_selection_page_hint'),
              fillColor: Colors.white),
        )
    );
  }

  @override
  void dispose() {
    super.dispose();
    myFocusNode.dispose();
  }
}

这是我的 android 清单:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="myApp">
    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="myApp !"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
              android:name="io.flutter.embedding.android.LaunchTheme"
              android:resource="@style/LaunchTheme"
              />
            <meta-data
              android:name="io.flutter.embedding.android.SplashScreenDrawable"
              android:resource="@drawable/launch_background"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <intent-filter>
                <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>

【问题讨论】:

  • 你能在android清单文件中发布设置吗
  • 这里是,但我真的不知道它会有什么帮助。
  • 因为你的 'android:windowSoftInputMode="adjustResize"'。它应该做你想做的事。
  • 我应该删除这行吗?
  • 没用...

标签: android ios flutter dart textfield


【解决方案1】:
android/app/src/main/res/values/styles.xml

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

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

我也有这个问题。由于使用了 flutter_native_splash 包而添加了这一行。

【讨论】:

  • 非常感谢,这正是问题所在!
【解决方案2】:

在出现软键盘时调整页面大小是通过 AndroidManifest 中的android:windowSoftInputMode 活动参数处理的。见official documnetation

如果您的 AndroidManifest.xml 中有 android:windowSoftInputMode="adjustResize",则当软键盘出现时,文本字段将自动重新定位。 如果AndroidManifest.xml 中没有参数,则不会发生相同的行为

您能否通过修改您的 AndroidManifest.这也将解释为什么它不仅在 Android 中有效,而且在 iOS 中有效(因为 iOS 隐式处理)

P.S: 修改 AndoridManifest.xml 时,热重载可能不起作用。您将需要实际停止并开始运行应用程序。

【讨论】:

    【解决方案3】:

    您可以使用 Transform Widget 包装您的 BottomPlayerBar 小部件,如下所示。

    Transform.translate(
        offset: Offset(0.0, -1 * MediaQuery.of(context).viewInsets.bottom),
        child: BottomPlayerBar(),
        );
    

    【讨论】:

      【解决方案4】:

      为您的 BottomPlayerBar 小部件使用边距:

      margin: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
      

      【讨论】:

        【解决方案5】:

        您可以像这样使用stack

        Widget build(BuildContext context) {
          return Scaffold(
            body: Stack(
              children: [
                Column(
                  children: [
                    Flexible(
                      child: ListView.builder(itemBuilder: null),
                    ),
                    Container(
                      width: double.infinity,
                      height: 100,
                      child: TextField(),
                    )
                  ],
                )
              ],
            ),
          );
        }
        

        【讨论】:

          【解决方案6】:

          一个简单的解决方案可能是为您的 BottomPlayerBar 使用底部填充,

          而不是,

          padding: EdgeInsets.only(top: 20, bottom: 25, left: 20, right: 70),
          

          试试,

          padding: EdgeInsets.only(top: 20, bottom: 25 + MediaQuery.of(context).padding.bottom, left: 20, right: 70),
          

          这里我们只是添加添加到脚手架底部的任何填充

          MediaQuery.of(context).padding.bottom
          

          【讨论】:

            【解决方案7】:

            这就是为什么我通常不尝试使用Scaffold 内置参数的原因。为了节省解决平台相关问题的时间,我会使用简单的Widgets 来实现您想要的结果。

            Widget build(BuildContext context) {
              return Scaffold(
                body: SingleChildScrollView(
                  physics: NeverScrollableScrollPhysics(),
                  child: Column(
                        mainAxisSize: MainAxisSize.max,
                        children: <Widget>[
            
                          // YOU BODY VIEW 85 HEIGHT %
                          ConstrainedBox(
                            constraints: BoxConstraints(
                              minWidth: MediaQuery.of(context).size.width,
                              minHeight: MediaQuery.of(context).size.height * 0.85,
                            ),
                            child: MainWidget()
                          ),
            
                          // YOUR BOTTOM VIEW 15 %
                          ConstrainedBox(
                            constraints: BoxConstraints(
                              minWidth: MediaQuery.of(context).size.width,
                              minHeight: MediaQuery.of(context).size.height * 0.15,
                            ),
                            child: BottomPlayerBar()
                          ),
                        ],
                    ),
                ),
              );
            }
            

            对于你上面的例子PlayerSelectionPage

            class PlayerSelectionPage extends StatelessWidget {
              @override
              Widget build(BuildContext context) {
                int itemCount = Provider.of<PlayerProvider>(context).getPlayerList.length;
                SystemChrome.setPreferredOrientations([
                  DeviceOrientation.portraitUp,
                ]);
                return Scaffold(
                  appBar: AppBar(
                    title: Text(AppLocalizations.of(context).translate('player_selection_page_title')),
                  ),
                  floatingActionButton: FloatingActionButton(
                    onPressed: (){
                      HapticFeedback.mediumImpact();
                      Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage(), settings: RouteSettings(name: 'Home page')));
                    },
                    child: Icon(
                      Icons.chevron_right,
                      size: 30,
                      color: Colors.white,
                    ),
                  ),
                  body: SingleChildScrollView(
                    physics: NeverScrollableScrollPhysics(),
                    child: Column(
                          mainAxisSize: MainAxisSize.max,
                          children: <Widget>[
            
                            // NEW VIEW
                            ConstrainedBox(
                            constraints: BoxConstraints(
                              minWidth: MediaQuery.of(context).size.width,
                              minHeight: MediaQuery.of(context).size.height * 0.85,
                            ),
                          child: itemCount > 0 ? ListView.builder(
                              itemCount: itemCount,
                              itemBuilder: (context, index) {
                                return Column(
                                  children: [
                                    PlayerDismissible(index),
                                    Divider(
                                      height: 0,
                                    )
                                  ],
                                );
                              }) : Container(
                              padding: EdgeInsets.all(20),
                              alignment: Alignment.topCenter,
                              child: Text(AppLocalizations.of(context).translate('player_selection_page_empty_text'), textAlign: TextAlign.center, style: Theme.of(context).textTheme.subtitle2)
                          ),
                        ),
            
                            // NEW VIEW
                            ConstrainedBox(
                              constraints: BoxConstraints(
                                minWidth: MediaQuery.of(context).size.width,
                                minHeight: MediaQuery.of(context).size.height * 0.15,
                              ),
                              child: BottomPlayerBar()
                            ),
                          ],
                      ),
                  ),
                  body: itemCount > 0 ? ListView.builder(
                      itemCount: itemCount,
                      itemBuilder: (context, index) {
                        return Column(
                          children: [
                            PlayerDismissible(index),
                            Divider(
                              height: 0,
                            )
                          ],
                        );
                      }) : Container(
                      padding: EdgeInsets.all(20),
                      alignment: Alignment.topCenter,
                      child: Text(AppLocalizations.of(context).translate('player_selection_page_empty_text'), textAlign: TextAlign.center, style: Theme.of(context).textTheme.subtitle2)
                  ),
                );
              }
            }
            

            【讨论】:

              【解决方案8】:

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

              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: BottomPlayerBar(),
                        ),
                  ));
              

              【讨论】:

                猜你喜欢
                • 2020-05-21
                • 2014-10-12
                • 1970-01-01
                • 1970-01-01
                • 2013-09-12
                • 1970-01-01
                • 2014-10-30
                • 2020-05-19
                • 2016-06-11
                相关资源
                最近更新 更多