千锋教育-做有情怀、有良心、有品质的职业教育机构

手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

领取全套视频
千锋教育

关注千锋学习站小程序
随时随地免费学习课程

当前位置:首页  >  技术干货  > Flutter键盘顶起底部

Flutter键盘顶起底部

来源:千锋教育
发布人:xqq
时间: 2023-11-23 00:46:24 1700671584

在Flutter中,打开键盘后,我们可能希望底部的内容不被覆盖。Flutter提供了一些方法来实现这一点。下面将从多个方面详细阐述如何使用Flutter实现键盘顶起底部的效果。

一、使用SingleChildScrollView

使用SingleChildScrollView可以让底部内容在键盘弹出时自动滚动到可见区域。我们只需要将底部内容包裹在SingleChildScrollView中,并在页面初始化时获得一个GlobalKey,然后在键盘弹出后通过该GlobalKey定位到bottom widget的位置,再通过动画滚动到该位置。

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State {
  final GlobalKey _bottomWidgetKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          children: [
            // other widgets
            Container(
              key: _bottomWidgetKey,
              child: // bottom widget
            ),
          ],
        ),
      ),
      appBar: AppBar(
        title: Text("Keyboard"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Show the keyboard
          FocusScope.of(context).requestFocus(FocusNode());
        },
        child: Icon(Icons.add),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    final BottomWidgetRenderBox =
        _bottomWidgetKey.currentContext.findRenderObject() as RenderBox;
    SchedulerBinding.instance!.addPostFrameCallback((_) {
      Future.delayed(const Duration(milliseconds: 300)).then((_) {
        final RenderBox keyboard =
            context.findRenderObject() as RenderBox;
        final keyboardHeight = keyboard.size.height;
        final heightDiff =
            keyboardHeight - (MediaQuery.of(context).viewInsets.bottom);
        final double offsetY =
            heightDiff > 0 ? -(BottomWidgetRenderBox.size.height + heightDiff) : 0;
        if (offsetY != 0) {
          _controller.animateTo(
              _controller.offset + offsetY,
              duration: new Duration(milliseconds: 300),
              curve: Curves.easeOut);
        }
      });
    });
  }
}

二、使用ListView

如果你希望底部内容可以滚动,我们可以使用ListView。ListView将自动在键盘弹出时滚动到底部,并且可以让用户滚动以查看所有内容。

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            child: ListView(
              children: [
                // other widgets
                // bottom widget
              ],
            ),
          ),
          // input widget
        ],
      ),
      appBar: AppBar(
        title: Text("ListView"),
      ),
    );
  }
}

三、使用Stack和AnimatedPositioned

使用Stack和AnimatedPositioned可以在键盘弹出时自动调整底部内容的位置,使其不被键盘遮挡。我们可以将输入框作为Stack的子元素,然后将底部内容作为Stack的第二个子元素。在键盘弹出时,我们可以使用AnimatedPositioned调整第二个子元素的位置。

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State {
  GlobalKey _globalKey = GlobalKey();

  double _bottom = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        title: Text("Stack and AnimatedPositioned"),
      ),
      body: Stack(
        children: [
          Positioned(
            top: 0,
            bottom: 0,
            left: 0,
            right: 0,
            child: GestureDetector(
              onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
              child: Container(
                color: Colors.white,
                child: SingleChildScrollView(
                  child: Column(
                    key: _globalKey,
                    children: [
                      // other widgets
                      Container(
                        height: 800,
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
          AnimatedPositioned(
            duration: Duration(milliseconds: 300),
            bottom: _bottom,
            left: 0,
            right: 0,
            child: Container(
              color: Colors.lightBlue,
              height: 140,
              child: Center(
                child: GestureDetector(
                  onTap: () {
                    FocusScope.of(context).requestFocus(FocusNode());
                  },
                  child: Text(
                    "Input",
                    style: TextStyle(
                      fontSize: 30,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    final RenderBox renderBoxRed = _globalKey.currentContext.findRenderObject() as RenderBox;
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      double height = renderBoxRed.size.height;
      double screenHeight = MediaQuery.of(context).size.height;
      double diff = screenHeight - height;
      setState(() {
        _bottom = diff;
      });
    });
  }
}

四、结合SingleChildScrollView和Stack

我们也可以结合ScrollView和Stack来实现键盘顶起底部的效果。具体操作是将输入框与底部内容放置在Stack中,并将Stack放置在SingleChildScrollView中。当键盘弹出时,可以与第一种方法类似地通过AnimationController将底部内容滑动到屏幕中央。

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State
    with SingleTickerProviderStateMixin {
  double _offset = 0.0;

  bool _isKeyboardShowing = false;

  late final AnimationController _controller = AnimationController(
      duration: const Duration(milliseconds: 200), vsync: this);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        title: Text("Stack and SingleChildScrollView"),
      ),
      body: SingleChildScrollView(
        child: Stack(
          children: [
            Column(
              children: [
                Container(
                  height: 800,
                ),
              ],
            ),
            Positioned(
              bottom: _offset,
              left: 0,
              right: 0,
              child: Container(
                color: Colors.lightBlue,
                height: 140,
                child: Center(
                  child: GestureDetector(
                    onTap: () {
                      FocusScope.of(context).requestFocus(FocusNode());
                    },
                    child: Text(
                      "Input",
                      style: TextStyle(
                        fontSize: 30,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      setState(() {
        _offset =
            MediaQuery.of(context).viewInsets.bottom * _controller.value;
      });
    });
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        setState(() {
          _isKeyboardShowing = true;
        });
      } else if (status == AnimationStatus.dismissed) {
        setState(() {
          _isKeyboardShowing = false;
        });
      }
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        title: Text("Stack and SingleChildScrollView"),
      ),
      body: SingleChildScrollView(
        child: Stack(
          children: [
            Column(
              children: [
                Container(
                  height: 800,
                ),
                GestureDetector(
                  onTap: () {
                    FocusScope.of(context).requestFocus(FocusNode());
                  }
                  child: // input widget
                ),
              ],
            ),
            Positioned(
              bottom: _offset,
              left: 0,
              right: 0,
              child: Container(
                color: Colors.lightBlue,
                height: 140,
                child: Center(
                  child: GestureDetector(
                    onTap: () {
                      FocusScope.of(context).requestFocus(FocusNode());
                    },
                    child: Text(
                      "Input",
                      style: TextStyle(
                        fontSize: 30,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }

  void _handleFocusChange() {
    if (FocusScope.of(context).hasFocus != _isKeyboardShowing) {
      _isKeyboardShowing = FocusScope.of(context).hasFocus;
      _controller.animateTo(
          _isKeyboardShowing ? 1.0 : 0.0,
          duration: const Duration(milliseconds: 150),
          curve: Curves.linear);
    }
  }
}

五、小结

本文介绍了Flutter中实现键盘顶起底部的几种方法,包括使用SingleChildScrollView、ListView、Stack以及结合方法。在使用这些方法时,我们需要考虑底部内容的特殊结构,确保键盘弹出时,底部内容不会被遮挡。希望本文能对您在Flutter开发中实现键盘顶起底部的需求提供帮助。

声明:本站稿件版权均属千锋教育所有,未经许可不得擅自转载。
10年以上业内强师集结,手把手带你蜕变精英
请您保持通讯畅通,专属学习老师24小时内将与您1V1沟通
免费领取
今日已有369人领取成功
刘同学 138****2860 刚刚成功领取
王同学 131****2015 刚刚成功领取
张同学 133****4652 刚刚成功领取
李同学 135****8607 刚刚成功领取
杨同学 132****5667 刚刚成功领取
岳同学 134****6652 刚刚成功领取
梁同学 157****2950 刚刚成功领取
刘同学 189****1015 刚刚成功领取
张同学 155****4678 刚刚成功领取
邹同学 139****2907 刚刚成功领取
董同学 138****2867 刚刚成功领取
周同学 136****3602 刚刚成功领取
相关推荐HOT