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

如何解决Flutter中获取Widget高度时的空安全错误?

解决Flutter中获取Widget逻辑高度的问题

问题说明

已实现获取设备物理高度、逻辑高度及屏幕键盘逻辑高度,但使用GlobalKey获取Widget逻辑高度时遇到错误:"The property 'size' can't be unconditionally accessed because the receiver can be null.",尝试?和!运算符无法解决。

错误原因

  1. 空安全类型问题:GlobalKey被声明为可空类型(GlobalKey?),导致后续访问currentContext和size时存在空值风险。
  2. 布局时机问题:在onChanged回调中直接获取尺寸时,Widget可能尚未完成当前帧的布局,此时currentContext或对应的RenderObject可能未初始化。

解决步骤

步骤1:修正GlobalKey的类型

将可空的GlobalKey改为非空类型,因为初始化时已赋值,无需可空声明:

final GlobalKey _key01 = GlobalKey(); // 去掉?

步骤2:在布局完成后获取Widget尺寸

使用WidgetsBinding.instance.addPostFrameCallback延迟获取尺寸,确保Widget完成当前帧的布局后再获取RenderObject和尺寸:

修改TextField的onChanged回调:

onChanged: (value) {
  setState(() {
    result = value;
  });
  // 等待当前帧布局完成后获取尺寸
  WidgetsBinding.instance.addPostFrameCallback((_) {
    // 判空确保currentContext存在
    if (_key01.currentContext != null) {
      // 获取RenderBox对象
      final RenderBox renderBox = _key01.currentContext!.findRenderObject() as RenderBox;
      final Size widgetSize = renderBox.size;
      // 更新UI显示尺寸
      setState(() {
        dimensions = "宽: ${widgetSize.width}, 高: ${widgetSize.height}";
      });
    }
  });
},

完整修改后的代码

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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // 修正为非空GlobalKey
  final GlobalKey _key01 = GlobalKey();

  late final TextEditingController firstController;

  String result = "";
  String effective_available_screen_height = "";
  String dimensions = "";

  FlutterView view = WidgetsBinding.instance.platformDispatcher.views.first;

  late Size size;
  double width = 0;
  double height = 0;
  double height01 = 0;

  @override
  void initState() {
    super.initState();
    firstController = TextEditingController();
  }

  @override
  void dispose() {
    firstController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Size ph_size = view.physicalSize;
    double ph_height = ph_size.height;

    size = MediaQuery.of(context).size;
    width = size.width;
    height = size.height;

    final padding = MediaQuery.of(context).viewPadding;
    height01 = height - padding.top - padding.bottom;

    effective_available_screen_height = "$height01";

    // 屏幕键盘高度(收起时为0)
    double keyboard_height = MediaQuery.of(context).viewInsets.bottom;

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Container(
        padding: const EdgeInsets.all(5.0),
        child: Column(
          children: [
            Container(
              child: Row(
                children: [
                  Expanded(
                    flex: 5,
                    child: Container(
                      padding: const EdgeInsets.all(5.0),
                      child: TextField(
                        key: _key01,
                        style: const TextStyle(
                          fontSize: 16,
                        ),
                        controller: firstController,
                        keyboardType: TextInputType.number,
                        onChanged: (value) {
                          setState(() {
                            result = value;
                          });
                          // 布局完成后获取尺寸
                          WidgetsBinding.instance.addPostFrameCallback((_) {
                            if (_key01.currentContext != null) {
                              final RenderBox renderBox = _key01.currentContext!.findRenderObject() as RenderBox;
                              final Size widgetSize = renderBox.size;
                              setState(() {
                                dimensions = "宽: ${widgetSize.width}, 高: ${widgetSize.height}";
                              });
                            }
                          });
                        },
                      ),
                    ),
                  ),
                  const Expanded(
                    flex: 5,
                    child: Text("Input Value"),
                  ),
                ],
              ),
            ),
            Text("Result = $result"),
            Text("Total physical height = $ph_height"),
            Text("Total logical height = $height"),
            Text("Onscreen Keyboard height = $keyboard_height"),
            Text("Working Height Available (logical) = $effective_available_screen_height"),
            Text("Dimensions: $dimensions"),
          ],
        ),
      ),
    );
  }
}

额外说明

如果需要在Widget初始化时就获取尺寸,可以在initState中添加addPostFrameCallback:

@override
void initState() {
  super.initState();
  firstController = TextEditingController();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    // 此处获取初始尺寸
    if (_key01.currentContext != null) {
      final RenderBox renderBox = _key01.currentContext!.findRenderObject() as RenderBox;
      final Size widgetSize = renderBox.size;
      setState(() {
        dimensions = "初始宽: ${widgetSize.width}, 初始高: ${widgetSize.height}";
      });
    }
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 15:02:07