CupertinoTabScaffold中Push页面键盘弹出时背景图防拉伸方案
解决CupertinoTabScaffold中push页面背景图随键盘拉伸的问题(保留iOS底部导航)
核心思路
把背景图统一放在CupertinoTabView的最外层,让整个Tab导航栈共享同一个背景容器,而非给每个页面单独套背景。这样键盘弹出时,仅页面内容区域会调整位置,背景图不受键盘inset影响,同时底部导航栏保持iOS原生的可见状态。
修改步骤
- 调整
CupertinoTabView的builder结构,将ContainerWithBG作为TabView的根组件,内部包裹页面导航栈 - 移除所有页面(包括
UpperLayer)内部的ContainerWithBG包裹,仅保留CupertinoPageScaffold并设置backgroundColor: Colors.transparent - 确保
CupertinoTabScaffold的backgroundColor保持透明,让外层背景能正常透出
修改后的完整代码
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( title: 'Demo', home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return ContainerWithBG( child: CupertinoTabScaffold( backgroundColor: Colors.transparent, tabBar: CupertinoTabBar(items: const [ BottomNavigationBarItem( icon: Icon(Icons.abc), ), BottomNavigationBarItem( icon: Icon(Icons.adb), ), ]), tabBuilder: (context, index) { // 修改:将ContainerWithBG作为CupertinoTabView的根 return CupertinoTabView( builder: (context) { return ContainerWithBG( child: CupertinoPageScaffold( backgroundColor: Colors.transparent, navigationBar: CupertinoNavigationBar( middle: Text('Base Tab $index - Img not moving when keyboard toggled'), ), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( 'Random Content of Tab $index', style: const TextStyle( color: Colors.white, fontSize: 25, ), ), const SizedBox(height: 30), const InputField(), const SizedBox(height: 30), ElevatedButton( onPressed: () { Navigator.of(context).push( MaterialPageRoute( builder: (context) => const UpperLayer( triggeredViaRootNav: false, ), ), ); }, child: const Text('Push To Upper Screen (Normal Push)'), ), const SizedBox(height: 30), ElevatedButton( onPressed: () { Navigator.of(context, rootNavigator: true).push( MaterialPageRoute( builder: (context) => const UpperLayer( triggeredViaRootNav: true, ), ), ); }, child: const Text('Push To Upper Screen (Rootnav Push)'), ), ], ), ), ), ); }, ); }, ), ); } } /// Text Field class InputField extends StatelessWidget { const InputField({super.key}); @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: Container( color: Colors.white, margin: const EdgeInsets.all(25.0), child: TextFormField(), ), ); } } /// BG IMG Decoration class ContainerWithBG extends StatelessWidget { final Widget child; const ContainerWithBG({ super.key, required this.child, }); @override Widget build(BuildContext context) { return Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/imgs/bg.png"), fit: BoxFit.cover, ), ), child: child, ); } } class UpperLayer extends StatelessWidget { final bool triggeredViaRootNav; const UpperLayer({ super.key, required this.triggeredViaRootNav, }); @override Widget build(BuildContext context) { // 修改:移除ContainerWithBG,直接使用透明背景的Scaffold return CupertinoPageScaffold( backgroundColor: Colors.transparent, navigationBar: CupertinoNavigationBar( middle: Text( triggeredViaRootNav ? 'Pushed Screen - Img NOT Moving when keyboard toggled (RootNav Push)' : 'Pushed Screen - Img NOT Moving when keyboard toggled (Normal Push)'), ), child: const Center( child: InputField(), ), ); } }
原理说明
- 每个
CupertinoTabView对应一个独立的导航栈,将背景图放在TabView的根节点后,整个栈内的页面都在这个背景之上渲染 - 键盘弹出时,Flutter会调整
CupertinoPageScaffold内部内容的位置(因resizeToAvoidBottomInset默认开启),但背景图所在的容器基于屏幕尺寸布局,不受键盘inset的影响 - 普通
Navigator.push()打开的页面属于当前TabView的导航栈,因此共享同一个背景,同时底部导航栏保持iOS原生的可见状态,无需使用rootNavigator
内容的提问来源于stack exchange,提问作者DevelJoe
相关产品推荐
相关产品推荐

