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

在Flutter build函数中声明MediaQuery引发重复重建的问题

Flutter中聚焦TextFormField触发StatelessWidget多次重建的问题解决

问题场景

  • 创建普通StatelessWidget
  • 在build函数内通过MediaQuery.of(context).size获取屏幕尺寸
  • 在build函数中添加日志log('Building home screen');
  • Scaffold的body中设置居中的Column,并添加TextFormField

现象:打开/聚焦TextFormField时,build函数会多次触发重建,原因是键盘弹出导致设备屏幕可用尺寸变化,而Widget依赖了MediaQuery的尺寸信息。

原代码示例

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(
      home: HomeScreen(),
    );
  }
}
import 'dart:developer';
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    log('Building home screen');
    // 获取设备屏幕尺寸
    final Size size = MediaQuery.of(context).size;
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [
          TextFormField(),
        ],
      ),
    );
  }
}

问题原因

MediaQuery是Flutter中的InheritedWidget,当键盘弹出/收起时,系统会更新屏幕的可用尺寸,导致MediaQuery的信息发生变化。而你的HomeScreen在build函数中直接依赖了MediaQuery.of(context).size,StatelessWidget会在依赖的InheritedWidget更新时自动触发build重建,因此每次键盘状态变化都会导致多次重建。

解决办法

方案1:阻止页面随键盘调整尺寸

如果不需要页面内容随键盘上移,可以给Scaffold添加resizeToAvoidBottomInset: false,这样键盘弹出时不会改变屏幕的可用尺寸,MediaQuery的size不会更新,也就不会触发重建。

修改后的HomeScreen代码:

import 'dart:developer';
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    log('Building home screen');
    final Size size = MediaQuery.of(context).size;
    return Scaffold(
      // 阻止页面随键盘调整布局
      resizeToAvoidBottomInset: false,
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [
          TextFormField(),
        ],
      ),
    );
  }
}

方案2:避免在build中直接依赖MediaQuery

改为StatefulWidget,在初始化或专门的尺寸监听方法中获取屏幕尺寸,而非在build函数中直接依赖,这样即使MediaQuery变化,只要不更新状态,就不会触发build重建。

示例代码:

import 'dart:developer';
import 'package:flutter/material.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  late Size _screenSize;

  @override
  void initState() {
    super.initState();
    // 页面渲染完成后获取初始尺寸
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _screenSize = MediaQuery.of(context).size;
    });
    // 监听屏幕尺寸变化
    WidgetsBinding.instance.addObserver(_metricsObserver);
  }

  @override
  void dispose() {
    // 移除监听,避免内存泄漏
    WidgetsBinding.instance.removeObserver(_metricsObserver);
    super.dispose();
  }

  void _metricsObserver() {
    final newSize = MediaQuery.of(context).size;
    if (_screenSize != newSize) {
      setState(() {
        _screenSize = newSize;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    log('Building home screen');
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [
          TextFormField(),
        ],
      ),
    );
  }
}

说明

  • 方案1适合不需要页面随键盘上移的场景,实现简单;
  • 方案2适合需要监听屏幕尺寸变化,但不想每次键盘状态改变都触发build的场景。

内容的提问来源于stack exchange,提问作者VISHNU PRABHAKARAN

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:18:14