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

如何防止Flutter中已失活页面因键盘弹出而重建?

解决Flutter中MediaQuery导致后台页面重建的问题

问题根源

当键盘弹出时,Flutter的MediaQueryData会更新(如屏幕可用尺寸、视图内边距变化),而Navigator栈中所有依赖MediaQuery.of(context)的页面(包括后台被覆盖的页面)都会触发build方法——因为这些页面并未被销毁,只是处于不可见状态。

解决方案

1. 非监听模式获取MediaQuery数据

如果仅需一次性获取尺寸,不需要响应后续变化,使用listen: false参数获取MediaQuery,避免因全局MediaQuery更新触发重建:

// 示例:获取高度但不监听变化
final height = MediaQuery.of(context, listen: false).size.height * 0.1;

注意:此方法不适用需要响应键盘弹出的布局场景。

2. 监听页面可见性,按需更新状态

通过WidgetsBindingObserver监听窗口变化,仅在页面当前可见时更新依赖MediaQuery的布局:

class _APageState extends State<APage> with WidgetsBindingObserver {
  double _containerHeight = 0;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _updateContainerHeight();
  }

  @override
  void didChangeMetrics() {
    // 仅当前页面可见时更新
    if (ModalRoute.of(context)?.isCurrent == true) {
      _updateContainerHeight();
    }
  }

  void _updateContainerHeight() {
    setState(() {
      _containerHeight = MediaQuery.of(context).size.height * 0.1;
    });
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print('A Page');
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Container(
            width: double.infinity,
            height: _containerHeight,
            color: Colors.green,
          ),
          ElevatedButton(
              onPressed: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => BPage(),
                    ));
              },
              child: const Text('BPage')),
          Container(
            color: Colors.amber,
            child: const TextField(),
          )
        ],
      ),
    );
  }
}

3. 用RouteObserver跟踪页面可见性

通过RouteObserver监听页面路由状态,仅在页面可见时响应MediaQuery变化:
首先在App中注册路由观察者:

final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: APage(),
      navigatorObservers: [routeObserver],
    );
  }
}

然后在页面State中混入RouteAware:

class _APageState extends State<APage> with RouteAware {
  double _containerHeight = 0;
  bool _isVisible = false;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    routeObserver.subscribe(this, ModalRoute.of(context)!);
    _updateContainerHeight();
  }

  @override
  void didPush() {
    setState(() => _isVisible = true);
  }

  @override
  void didPopNext() {
    setState(() => _isVisible = true);
    _updateContainerHeight();
  }

  @override
  void didPushNext() {
    setState(() => _isVisible = false);
  }

  @override
  void didPop() {
    routeObserver.unsubscribe(this);
    super.didPop();
  }

  void _updateContainerHeight() {
    if (_isVisible) {
      setState(() {
        _containerHeight = MediaQuery.of(context).size.height * 0.1;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    print('A Page');
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Container(
            width: double.infinity,
            height: _containerHeight,
            color: Colors.green,
          ),
          ElevatedButton(
              onPressed: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => BPage(),
                    ));
              },
              child: const Text('BPage')),
          Container(
            color: Colors.amber,
            child: const TextField(),
          )
        ],
      ),
    );
  }
}

4. 用LayoutBuilder替代MediaQuery

如果仅需父容器尺寸而非全屏尺寸,使用LayoutBuilder,它仅在父容器尺寸变化时重建,不受全局MediaQuery更新影响:

LayoutBuilder(
  builder: (context, constraints) {
    return Container(
      width: constraints.maxWidth,
      height: constraints.maxHeight * 0.1,
      color: Colors.green,
    );
  },
)

内容的提问来源于stack exchange,提问作者Anson NG

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 22:57:54