Flutter中如何将TextFormField定位在屏幕底部且键盘弹出时自动上移
Flutter 底部固定输入框随键盘同步上浮解决方案
问题原因
- 你当前将
TextFormField放在了SingleChildScrollView内部的Column中,属于滚动内容的一部分,无法固定在屏幕底部,也无法跟随键盘触发的布局调整自动上移。
实现方案
调整Scaffold的布局结构即可实现需求:
- 将Scaffold的body改为Column结构,第一部分用
Expanded包裹原有的SingleChildScrollView(包含帖子详情、评论列表等所有需要滚动的内容),第二部分直接放置输入框组件 - Scaffold默认开启
resizeToAvoidBottomInset: true属性,键盘唤起时会自动压缩页面可用高度,底部输入框就会同步跟随键盘上移
核心代码修改示例
import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:untitled/ui/view/threadModel/thread_viewmodel.dart'; import 'package:hexcolor/hexcolor.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:untitled/ui/view/comment_view.dart'; import 'package:flutter/services.dart'; class ThreadView extends StatelessWidget { // const ThreadView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return ViewModelBuilder<ThreadViewModel>.reactive( builder: (context, model, child) => Scaffold( // 该属性默认就是true,不需要额外配置,不要手动改为false resizeToAvoidBottomInset: true, appBar: AppBar( systemOverlayStyle: SystemUiOverlayStyle( statusBarColor: HexColor('00b87c'), ), backgroundColor: Colors.white, toolbarHeight: 54, leading: Icon( Icons.arrow_back_ios_rounded, color: HexColor('#333333'), size: 16, ), titleSpacing: 0, title: Text( model.threads, style: GoogleFonts.lato( textStyle: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, fontStyle: FontStyle.normal, color: HexColor('#201e1f'), ), ), ), ), // body改为Column结构 body: Column( children: [ // 滚动内容用Expanded包裹,占除输入框外的所有高度 Expanded( child: SingleChildScrollView( physics: BouncingScrollPhysics(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Container( child: ConstrainedBox( constraints: BoxConstraints(minHeight: 212), child: Padding( padding: const EdgeInsets.fromLTRB(16, 16, 21, 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ SizedBox(height: 16), Text( 'Message in', style: GoogleFonts.lato( fontWeight: FontWeight.w400, fontSize: 14, fontStyle: FontStyle.normal, color: HexColor('#727272'), ), ), SizedBox( width: 4, ), Icon( Icons.lock, size: 12, color: HexColor('#1A61DB'), ), SizedBox( width: 4, ), Text( 'team-socrates', style: GoogleFonts.lato( color: HexColor('#1A61DB'), fontSize: 12, fontStyle: FontStyle.normal, fontWeight: FontWeight.w600, ), ), ], ), SizedBox( height: 16, ), Row( children: [ Container( decoration: BoxDecoration( border: Border.all( color: Colors.white, ), borderRadius: BorderRadius.all( Radius.circular(4), ), ), width: 42, height: 42, child: FittedBox( child: Image.asset('images/Rectangle 172.png'), fit: BoxFit.fill, ), ), SizedBox( width: 16, ), Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Text( 'princessliz', style: GoogleFonts.lato( fontWeight: FontWeight.w700, fontStyle: FontStyle.normal, fontSize: 16, ), ), Container( margin: EdgeInsets.only(left: 4.0), height: 14, width: 14, child: Image.asset('images/Group 1280.png'), ), ], ), SizedBox( height: 4, ), Text( '30 Aug at 20:31', style: GoogleFonts.lato( fontWeight: FontWeight.w400, fontSize: 12, fontStyle: FontStyle.normal), ) ], ), Icon( Icons.bookmark_border_outlined, size: 18, ), ], ), SizedBox( height: 12, ), Text( "Aristotle - @caculuz.,\n" "Aquinas - @caculuz.,\n" "Socrates - @Feranmi..", style: GoogleFonts.lato( fontWeight: FontWeight.w400, fontSize: 16, fontStyle: FontStyle.normal, height: 1.3, color: HexColor('#424141'), ), ), SizedBox( height: 16, ), Container( decoration: BoxDecoration( color: HexColor('#93B0E1'), borderRadius: BorderRadius.all( Radius.circular(5), ), border: Border.all( color: HexColor('#1a61db'), ), ), width: 45, height: 25, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( "images/po.png", width: 16, height: 16, ), SizedBox( width: 4, ), Text( '3', style: GoogleFonts.lato( fontSize: 12, fontStyle: FontStyle.normal, fontWeight: FontWeight.w700), ), ], ), ), ], ), ), ), ), Divider( color: HexColor('#7b8794'), ), Padding( padding: EdgeInsets.fromLTRB(16, 12, 20, 13), child: Container( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "1 reply", style: GoogleFonts.lato( fontWeight: FontWeight.w600, fontStyle: FontStyle.normal, fontSize: 15, color: HexColor('#424141'), ), ), Row( children: [ Icon( Icons.forward_outlined, size: 24, color: HexColor('#424141'), ), SizedBox( width: 33.92, ), Icon( Icons.more_vert_rounded, color: HexColor('#424141'), size: 24, ), ], ), ], ), ), ), Divider( color: HexColor('#7b8794'), ), Comment(), // 删掉原来放在这里的输入框代码 ], ), ), ), // 输入框移到这里,固定在Column底部 Container( // 可根据需求加背景色、内边距等样式 padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16), child: TextFormField( decoration: InputDecoration( contentPadding: EdgeInsets.fromLTRB(16, 12, 8, 12), hintText: "Add a reply", ), ), ), ], ), ), viewModelBuilder: () => ThreadViewModel(), ); } }
补充注意事项
- 可以给输入框外层Container增加底部安全区适配,避免全面屏设备上输入框被系统手势条遮挡:
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom) - 如果需要在输入框右侧加发送、表情等操作按钮,直接在输入框所在的Container中嵌套Row布局即可
内容的提问来源于stack exchange,提问作者Emmanuel Ekpenyong
相关产品推荐
相关产品推荐

