如何解决Flutter中获取Widget高度时的空安全错误?
解决Flutter中获取Widget逻辑高度的问题
问题说明
已实现获取设备物理高度、逻辑高度及屏幕键盘逻辑高度,但使用GlobalKey获取Widget逻辑高度时遇到错误:"The property 'size' can't be unconditionally accessed because the receiver can be null.",尝试?和!运算符无法解决。
错误原因
- 空安全类型问题:GlobalKey被声明为可空类型(
GlobalKey?),导致后续访问currentContext和size时存在空值风险。 - 布局时机问题:在
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
相关产品推荐
相关产品推荐

