Flutter如何让悬浮操作按钮固定在底部导航栏不随键盘上浮
问题原因
FAB随键盘上浮是因为Scaffold默认resizeToAvoidBottomInset属性为true,键盘弹出时会重新调整页面布局尺寸,FAB作为Scaffold的浮动组件会同步上移。以下是两种可直接落地的解决方案:
方案1:固定FAB位置,不随键盘上浮
只需要修改Scaffold的配置,同时适配输入框的显示逻辑即可:
- 给Scaffold添加
resizeToAvoidBottomInset: false,禁止页面随键盘弹出调整布局 - 将原本的Column类型body嵌套进
SingleChildScrollView,避免输入框聚焦后被键盘遮挡
修改后的核心代码如下:
Scaffold( // 新增这一行配置 resizeToAvoidBottomInset: false, // 嵌套SingleChildScrollView body: SingleChildScrollView( child: Column(children: [ // 原有搜索框代码保持不变 Padding( padding: const EdgeInsets.only(left: 16, right: 16,top: 20), child: Container( height: 40, decoration: BoxDecoration( color: Color(0xffEBEDF4), borderRadius: BorderRadius.circular(25), ), child: Row( children: [ Expanded( flex: 2, child: Padding( padding: const EdgeInsets.all(8.0), child: TextFormField( keyboardType: TextInputType.text, inputFormatters: [UpperCaseTextFormatter()], style: const TextStyle( color: Colors.white, fontSize: 15, fontFamily: 'PoppinsRegular', ), decoration: InputDecoration( hintText: 'Search...', hintStyle: theme.textTheme.caption!.copyWith( color: const Color(0xffEBEDF4), ), isDense: true, contentPadding: const EdgeInsets.fromLTRB(10, 10, 10, 0), fillColor:const Color(0xffEBEDF4), filled: true, border: InputBorder.none, focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8.0), borderSide: const BorderSide( color: Color(0xffEBEDF4), ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8.0), borderSide:const BorderSide( color: Color(0xffEBEDF4), width: 1.0, ), ), ), ), ), ), IconButton( onPressed: () { searchStuff(); }, icon:const Icon(Icons.search)), ], ), ), ), ]), ), // 原有FAB和底部导航栏配置保持不变 floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, floatingActionButton: FloatingActionButton( onPressed: () { print('Test bottom nav bar'); }, child: const Icon(Icons.add), ), bottomNavigationBar: BottomNavigationBar( items: const <BottomNavigationBarItem>[ BottomNavigationBarItem( icon: Icon(Icons.call), label: 'Calls', ), BottomNavigationBarItem( icon: Icon(Icons.chat), label: 'Chats', ), ], currentIndex: _selectedIndex, onTap: _onItemTapped, ), )
方案2:将FAB嵌入底部导航栏,形成一体化效果
使用BottomAppBar替代原有直接挂载的BottomNavigationBar,自带适配FAB的凹槽效果,FAB会直接嵌在底部导航栏中完全固定:
Scaffold( resizeToAvoidBottomInset: false, body: SingleChildScrollView( child: Column(children: [ // 原有搜索框代码保持不变 ]), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, floatingActionButton: FloatingActionButton( onPressed: () { print('Test bottom nav bar'); }, child: const Icon(Icons.add), ), // 替换原有底部导航栏为BottomAppBar嵌套BottomNavigationBar bottomNavigationBar: BottomAppBar( shape: const CircularNotchedRectangle(), // 生成适配FAB的圆形凹槽 notchMargin: 6, // 凹槽和FAB之间的留白间距 elevation: 8, child: BottomNavigationBar( backgroundColor: Colors.transparent, // 必须设为透明,否则会挡住凹槽效果 elevation: 0, items: const <BottomNavigationBarItem>[ BottomNavigationBarItem( icon: Icon(Icons.call), label: 'Calls', ), BottomNavigationBarItem( icon: Icon(Icons.chat), label: 'Chats', ), ], currentIndex: _selectedIndex, onTap: _onItemTapped, ), ), )
额外适配方案(保留页面自动调整能力)
如果不希望禁用resizeToAvoidBottomInset(保留输入框不被键盘遮挡的默认适配),可以放弃Scaffold自带的FAB参数,手动用Stack定位FAB在页面底部:
Scaffold( // 保留默认的resizeToAvoidBottomInset: true body: Stack( children: [ // 原有页面body代码保持不变 Column( children: [ // 搜索框等原有内容 ], ), // 手动定位FAB在底部居中 Positioned( bottom: 20, left: 0, right: 0, child: Center( child: FloatingActionButton( onPressed: () { print('Test bottom nav bar'); }, child: const Icon(Icons.add), ), ), ) ], ), // 原有BottomNavigationBar代码不变,删除原来的floatingActionButton相关配置 bottomNavigationBar: BottomNavigationBar( items: const <BottomNavigationBarItem>[ BottomNavigationBarItem( icon: Icon(Icons.call), label: 'Calls', ), BottomNavigationBarItem( icon: Icon(Icons.chat), label: 'Chats', ), ], currentIndex: _selectedIndex, onTap: _onItemTapped, ), )
内容的提问来源于stack exchange,提问作者Kevnlan
相关产品推荐
相关产品推荐

