You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CupertinoTabScaffold中Push页面键盘弹出时背景图防拉伸方案

解决CupertinoTabScaffold中push页面背景图随键盘拉伸的问题(保留iOS底部导航)

核心思路

把背景图统一放在CupertinoTabView的最外层,让整个Tab导航栈共享同一个背景容器,而非给每个页面单独套背景。这样键盘弹出时,仅页面内容区域会调整位置,背景图不受键盘inset影响,同时底部导航栏保持iOS原生的可见状态。

修改步骤

  1. 调整CupertinoTabView的builder结构,将ContainerWithBG作为TabView的根组件,内部包裹页面导航栈
  2. 移除所有页面(包括UpperLayer)内部的ContainerWithBG包裹,仅保留CupertinoPageScaffold并设置backgroundColor: Colors.transparent
  3. 确保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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 16:34:56