Flutter底部输入框和键盘处理

7,505 阅读2分钟

提供了一个完整可复用的弹出式底部输入框。

先看效果图                          


代码实现

第一页代码 main_bottom_input.dart

import 'package:flutter/material.dart';
import 'bottom_input/input_dialog.dart';

void main() => runApp(MaterialApp(home: InputHomeWidget()));

class InputHomeWidget extends StatefulWidget {
  @override
  _InputHomeWidgetState createState() => _InputHomeWidgetState();
}

class _InputHomeWidgetState extends State<InputHomeWidget> {
  String _inputString = "";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      /**弹出键盘会导致bottomInset值变化,可以通过MediaQuery获取到各种值。
      resizeToAvoidBottomInset:true(默认值),
      表示根据变化重新Build,页面会被顶到键盘上,当然也可以通过scollView处理 **/
      resizeToAvoidBottomInset: false,
      appBar: AppBar(title: Text("底部输入框"),),
      body: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          FlatButton(
            color: Colors.yellow,
            onPressed: _input,
            child: Text("弹出底部输入框"),
          ),
          Expanded(child: Text("结果:$_inputString")),
          Text("底部文案"),
          Container(height: 100, width: 20,)
        ],
      ),
    );
  }

  void _input() {
    InputDialog.show(context).then((value) {
      setState(() {
        _inputString = value;
      });
    });
  }
}

输入框Dialog代码 input_dialog.dart

这个InputOverlay是从Dialog抄过来的。Flutter中的页面都是route,可以设置route的各种属性:背景色,页面切换动画。这里重点是barrierColor设置背景色。dialog会有默认半透明。我们这个地方需要全透明,但记住不能设置 0x00000000,看报错是说全透明,有页面切换的错误。。

这个ModalRoute属性的各种玩法就很多了,可以改改属性试试各种效果


import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'input_widget.dart';

class InputDialog {
  static Future<String> show(BuildContext context) async {
    return Navigator.of(context).push(InputOverlay());
  }
}

class InputOverlay extends ModalRoute<String> {
  @override
  Duration get transitionDuration => Duration(milliseconds: 200);

  @override
  bool get opaque => false;

  @override
  bool get barrierDismissible => true;

  @override
  Color get barrierColor => const Color(0x01000000);

  @override
  String get barrierLabel => null;

  @override
  bool get maintainState => true;

  @override
  Widget buildPage(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
  ) {
    return InputWidget();
  }

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    return FadeTransition(
      opacity: CurvedAnimation(
        parent: animation,
        curve: Curves.easeOut,
      ),
      child: child,
    );
  }
}

输入框Widget代码 input_widget.dart

页面由两部分组成 上面的透明区域,点击可消失页面。底部的输入框区域。这个地方要注意resizeToAvoidBottomInset不可以设置成false了,否则顶不起来输入框哦

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class InputWidget extends StatefulWidget {
  InputWidget({Key key}) : super(key: key);

  @override
  _InputWidgetState createState() => _InputWidgetState();
}

class _InputWidgetState extends State<InputWidget> {
  @override
  Widget build(BuildContext context) {
    TextEditingController editingController = TextEditingController();

    return Scaffold(
      backgroundColor: Colors.transparent,
//      resizeToAvoidBottomInset: false,
      body: Container(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Expanded(
              child: GestureDetector(
                  onTapDown: (_) => Navigator.of(context).pop(),
                  child: Container(
                    color: Colors.transparent,
                  )),
            ),
            SafeArea(
              child: Container(
                color: Colors.white,
                child: Row(
                  children: <Widget>[
                    Container(
                      width: 20,
                    ),
                    Expanded(
                      child: Container(
                        margin: EdgeInsets.only(top: 10, bottom: 10),
                        decoration: BoxDecoration(
                            color: Color(0xfff6f8fb),
                            borderRadius:
                                BorderRadius.all(Radius.circular(20))),
                        alignment: Alignment.center,
                        child: TextField(
                          autofocus: true,
                          maxLengthEnforced: true,
                          inputFormatters: <TextInputFormatter>[
                            LengthLimitingTextInputFormatter(200)
                          ],
                          controller: editingController,
                          decoration: InputDecoration(
                              isDense: true,
                              contentPadding: EdgeInsets.only(
                                  left: 10, right: 10, top: 5, bottom: 5),
                              border: InputBorder.none,
                              hintStyle: TextStyle(color: Color(0xffcccccc)),
                              hintText: "说点什么吧"),
                        ),
                      ),
                    ),
                    GestureDetector(
                      onTap: (() {
                        var text = editingController.text?.trim() ?? "";
                        if (text.isNotEmpty) {
                          Navigator.pop(context, text);
                        }
                      }),
                      child: Container(
                        padding: EdgeInsets.only(
                            left: 10, right: 10, top: 10, bottom: 10),
                        alignment: Alignment.center,
                        child: Text(
                          "发送",
                          style: TextStyle(color: Color(0xff00BBBB)),
                        ),
                      ),
                    )
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}


Android中要特别注意 windowSoftInputMode 为 "adjustResize",才能触发键盘引起的bottomInset变化

<activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">