千鋒教育-做有情懷、有良心、有品質(zhì)的職業(yè)教育機(jī)構(gòu)

手機(jī)站
千鋒教育

千鋒學(xué)習(xí)站 | 隨時(shí)隨地免費(fèi)學(xué)

千鋒教育

掃一掃進(jìn)入千鋒手機(jī)站

領(lǐng)取全套視頻
千鋒教育

關(guān)注千鋒學(xué)習(xí)站小程序
隨時(shí)隨地免費(fèi)學(xué)習(xí)課程

當(dāng)前位置:首頁  >  技術(shù)干貨  > Flutter鍵盤頂起底部

Flutter鍵盤頂起底部

來源:千鋒教育
發(fā)布人:xqq
時(shí)間: 2023-11-23 00:46:24 1700671584

在Flutter中,打開鍵盤后,我們可能希望底部的內(nèi)容不被覆蓋。Flutter提供了一些方法來實(shí)現(xiàn)這一點(diǎn)。下面將從多個(gè)方面詳細(xì)闡述如何使用Flutter實(shí)現(xiàn)鍵盤頂起底部的效果。

一、使用SingleChildScrollView

使用SingleChildScrollView可以讓底部內(nèi)容在鍵盤彈出時(shí)自動滾動到可見區(qū)域。我們只需要將底部內(nèi)容包裹在SingleChildScrollView中,并在頁面初始化時(shí)獲得一個(gè)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

如果你希望底部內(nèi)容可以滾動,我們可以使用ListView。ListView將自動在鍵盤彈出時(shí)滾動到底部,并且可以讓用戶滾動以查看所有內(nèi)容。

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可以在鍵盤彈出時(shí)自動調(diào)整底部內(nèi)容的位置,使其不被鍵盤遮擋。我們可以將輸入框作為Stack的子元素,然后將底部內(nèi)容作為Stack的第二個(gè)子元素。在鍵盤彈出時(shí),我們可以使用AnimatedPositioned調(diào)整第二個(gè)子元素的位置。

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

四、結(jié)合SingleChildScrollView和Stack

我們也可以結(jié)合ScrollView和Stack來實(shí)現(xiàn)鍵盤頂起底部的效果。具體操作是將輸入框與底部內(nèi)容放置在Stack中,并將Stack放置在SingleChildScrollView中。當(dāng)鍵盤彈出時(shí),可以與第一種方法類似地通過AnimationController將底部內(nèi)容滑動到屏幕中央。

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

五、小結(jié)

本文介紹了Flutter中實(shí)現(xiàn)鍵盤頂起底部的幾種方法,包括使用SingleChildScrollView、ListView、Stack以及結(jié)合方法。在使用這些方法時(shí),我們需要考慮底部內(nèi)容的特殊結(jié)構(gòu),確保鍵盤彈出時(shí),底部內(nèi)容不會被遮擋。希望本文能對您在Flutter開發(fā)中實(shí)現(xiàn)鍵盤頂起底部的需求提供幫助。

聲明:本站稿件版權(quán)均屬千鋒教育所有,未經(jīng)許可不得擅自轉(zhuǎn)載。
10年以上業(yè)內(nèi)強(qiáng)師集結(jié),手把手帶你蛻變精英
請您保持通訊暢通,專屬學(xué)習(xí)老師24小時(shí)內(nèi)將與您1V1溝通
免費(fèi)領(lǐng)取
今日已有369人領(lǐng)取成功
劉同學(xué) 138****2860 剛剛成功領(lǐng)取
王同學(xué) 131****2015 剛剛成功領(lǐng)取
張同學(xué) 133****4652 剛剛成功領(lǐng)取
李同學(xué) 135****8607 剛剛成功領(lǐng)取
楊同學(xué) 132****5667 剛剛成功領(lǐng)取
岳同學(xué) 134****6652 剛剛成功領(lǐng)取
梁同學(xué) 157****2950 剛剛成功領(lǐng)取
劉同學(xué) 189****1015 剛剛成功領(lǐng)取
張同學(xué) 155****4678 剛剛成功領(lǐng)取
鄒同學(xué) 139****2907 剛剛成功領(lǐng)取
董同學(xué) 138****2867 剛剛成功領(lǐng)取
周同學(xué) 136****3602 剛剛成功領(lǐng)取
相關(guān)推薦HOT
久久亚洲中文字幕精品一区四,亚洲日本另类欧美一区二区,久久久久久久这里只有免费费精品,高清国产激情视频在线观看
在线天天看片免费 | 久久在国产精品视频 | 日本国产精品网色 | 综合激情久久综合激情 | 中文字幕欧美视频77 | 亚洲人成中文字幕在线观看 |