简体   繁体   English

Flutter - 当键盘出现在 android 上时,文本字段不显示

[英]Flutter - Textfield not showing up when keyboard appears on android

My problem is that when I focus on the textfield, the keyboard appears but the layout doesn't resize to keep it in the view.我的问题是,当我专注于文本字段时,键盘会出现,但布局不会调整大小以将其保留在视图中。 This problem is only with android and not iOS: Here a screenshot of what I mean:这个问题只存在于 android 而不是 iOS: 这是我的意思的截图:

Before and after on android: android前后:

前 后

Before and after on iOS: iOS 前后:

在此处输入图像描述 在此处输入图像描述

Here is my class:这是我的 class:

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

And here is my 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();
  }
}

Here is my android manifest:这是我的 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>

You can wrap your BottomPlayerBar widget with the Transform Widget as shown below.您可以使用 Transform Widget 包装您的 BottomPlayerBar 小部件,如下所示。

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

Resizing the page when the soft keyboard appears is handled through the android:windowSoftInputMode activity parameter in AndroidManifest.出现软键盘时调整页面大小是通过 AndroidManifest 中的android:windowSoftInputMode活动参数处理的。 See the official documnetation官方文档

If you have android:windowSoftInputMode="adjustResize" in your AndroidManifest.xml, the textfield will automatically reposition when the softkeyboard appears.如果您的 AndroidManifest.xml 中有android:windowSoftInputMode="adjustResize" ,则当软键盘出现时,文本字段将自动重新定位。 The same behaviour will not happen if the parameter is not there in AndroidManifest.xml如果AndroidManifest.xml中没有该参数,则不会发生相同的行为

Could you recheck this by modifying your AndroidManifest.您能否通过修改您的 AndroidManifest. That will also explain why it is not working only in Android but works in iOS (Because iOS handles this implicitly)这也将解释为什么它不仅仅在 Android 中工作,而是在 iOS 中工作(因为 iOS 隐式处理)

PS: When modifying the AndoridManifest.xml, hot reload may not work. PS:修改 AndoridManifest.xml 时,热重载可能不起作用。 You will need an actual stop and start running the app.您将需要实际停止并开始运行该应用程序。

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

For me changing below item property from true to false对我来说,将下面的项目属性从 true 更改为 false

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

I had this issue, too.我也有这个问题。 This line was added as a result of using the flutter_native_splash package.由于使用了 flutter_native_splash package,因此添加了这一行。

you can add isScrollControlled = true to showModalBottomSheet it'll allow the bottom sheet to take the full required height which gives more insurance that TextField is not covered by the keyboard.您可以将 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(),
          ),
    ));

So that's why I don't usually try to use Scaffold build in parameters.所以这就是为什么我通常不尝试使用Scaffold内置参数的原因。 To save time on resolving platform dependent issues I would go with using simple Widgets to achieve result you want.为了节省解决平台相关问题的时间,我会 go 使用简单的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()
              ),
            ],
        ),
    ),
  );
}

For your example above PlayerSelectionPage .对于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)
      ),
    );
  }
}

A simple solution might be to use the bottom padding for you BottomPlayerBar,一个简单的解决方案可能是为您的 BottomPlayerBar 使用底部填充,

Instead of,代替,

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

Try,尝试,

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

Here we are just adding any padding added to the bottom of the scaffold from在这里,我们只是添加添加到脚手架底部的任何填充

MediaQuery.of(context).padding.bottom

You can use a stack like this:您可以使用这样的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(),
            )
          ],
        )
      ],
    ),
  );
}

Use margin for your BottomPlayerBar widget:为您的BottomPlayerBar小部件使用边距:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM