Flutter开发:使用TextField触发RenderBox无尺寸命中测试错误
问题
我正在开发一款沉浸式闹钟Flutter应用,用户需完成答题任务才能关闭闹钟,以此刺激大脑辅助清醒。在使用TextField实现答案输入功能时,调试代码跳转到box.dart文件,触发如下错误:
throw FlutterError.fromParts(<DiagnosticsNode>[ ErrorSummary('Cannot hit test a render box with no size.'), describeForError('The hitTest() method was called on this RenderBox'), ErrorDescription( 'Although this node is not marked as needing layout, ' 'its size is not set.', ), ErrorHint( 'A RenderBox object must have an ' 'explicit size before it can be hit-tested. Make sure ' 'that the RenderBox in question sets its size during layout.', ), ]); }
以下是我的完整代码:
import 'package:flutter/material.dart'; import 'package:audioplayers/audioplayers.dart'; //import 'package:audioplayers/audio_cache.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Time Attack', theme: ThemeData( primarySwatch: Colors.red, ), home: const 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 MaterialApp( home: Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.red, title: const Text('Time Attack'), centerTitle: true, ), floatingActionButton: FloatingActionButton( onPressed: () async { final player = AudioCache(); //bool isPLaying = false; await player.load('musicForapp.mp3'); AudioPlayer p = AudioPlayer(); p.audioCache = player; await p.play(AssetSource('musicForapp.mp3')); }, backgroundColor: Colors.red, child: const Icon(Icons.punch_clock), ), body: Padding( padding: const EdgeInsets.all(170), child: Column( children: [ const Text( "Click the Icon in the bottem right to simulate alarm", style: TextStyle( color: Colors.white, fontSize: 30.0, ), ), ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.red), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const TasksPage(), ), ); }, child: const Text( "Tasks page", style: TextStyle(color: Colors.white), ), ) ], ), ), ), ); } } class TasksPage extends StatefulWidget { const TasksPage({super.key}); @override State<TasksPage> createState() => _TasksPageState(); } class _TasksPageState extends State<TasksPage> { final _textcontroller = TextEditingController(); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.red, title: const Text('Tasks'), centerTitle: true, ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const SecondTask(), ), ); }, backgroundColor: Colors.red, child: const Text("Next"), ), body: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Text( "How many states are there in the Uninted States of America?", style: TextStyle( backgroundColor: Colors.white, color: Colors.black, fontSize: 30.0, ), ), TextField( controller: _textcontroller, decoration: InputDecoration( hintText: "Type your answer here!", border: const OutlineInputBorder(), suffixIcon: IconButton( onPressed: () { _textcontroller.clear(); }, icon: const Icon(Icons.clear), ), ), ), ], ), ), ), ), ); } } class SecondTask extends StatefulWidget { const SecondTask({super.key}); @override State<SecondTask> createState() => _SecondTask(); } class _SecondTask extends State<SecondTask> { final _textcontroller2 = TextEditingController(); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.red, title: const Text('Tasks'), centerTitle: true, ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const Thirdtask(), ), ); }, backgroundColor: Colors.red, child: const Text("Next"), ), body: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Text( "how many oceans are there?", style: TextStyle( backgroundColor: Colors.white, color: Colors.black, fontSize: 30.0, ), ), TextField( controller: _textcontroller2, decoration: InputDecoration( hintText: "Type your answer here!", border: const OutlineInputBorder(), suffixIcon: IconButton( onPressed: () { _textcontroller2.clear(); }, icon: const Icon(Icons.clear), ), ), ), ], ), ), ), ), ); } } class Thirdtask extends StatefulWidget { const Thirdtask({super.key}); @override State<Thirdtask> createState() => _Thirdtask(); } class _Thirdtask extends State<Thirdtask> { final _textcontroller3 = TextEditingController(); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.red, title: const Text('Tasks'), centerTitle: true, ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const Finaltask(), ), ); }, backgroundColor: Colors.red, child: const Text("Next"), ), body: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Text( "What is 8 x 7?", style: TextStyle( backgroundColor: Colors.white, color: Colors.black, fontSize: 30.0, ), ), TextField( controller: _textcontroller3, decoration: InputDecoration( hintText: "Type your answer here!", border: const OutlineInputBorder(), suffixIcon: IconButton( onPressed: () { _textcontroller3.clear(); }, icon: const Icon(Icons.clear), ), ), ), ], ), ), ), ), ); } } class Finaltask extends StatefulWidget { const Finaltask({super.key}); @override State<Finaltask> createState() => _Finaltask(); } class _Finaltask extends State<Finaltask> { final _textcontroller4 = TextEditingController(); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.red, title: const Text('Tasks'), centerTitle: true, ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const Congrats(), ), ); }, backgroundColor: Colors.red, child: const Text("Submit"), ), body: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Text( "Riddle: What goes up but never comes back down?", style: TextStyle( backgroundColor: Colors.white, color: Colors.black, fontSize: 30.0, ), ), TextField( controller: _textcontroller4, decoration: InputDecoration( hintText: "Type your answer here!", border: const OutlineInputBorder(), suffixIcon: IconButton( onPressed: () { _textcontroller4.clear(); }, icon: const Icon(Icons.clear), ), ), ), ], ), ), ), ), ); } } class Congrats extends StatefulWidget { const Congrats({super.key}); @override State<Congrats> createState() => _Congrats(); } class _Congrats extends State<Congrats> { final _textcontroller4 = TextEditingController(); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.red, title: const Text('Good Job!'), centerTitle: true, ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const MyHomePage(), ), ); //player.clear('explosion.mp3'); (to stop alarm) }, backgroundColor: Colors.red, child: const Text("Home"), ), body: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: const [ Text( "Congratulations you completed your tasks, Click the home button to return home and stop the timer!", style: TextStyle( backgroundColor: Colors.white, color: Colors.black, fontSize: 30.0, ), ), ], ), ), ), ), ); } }
问题分析与解决方案
错误根源
- 重复实例化MaterialApp:每个页面都创建了新的MaterialApp,破坏Flutter上下文层级,导致布局渲染异常。Flutter应用仅需一个根MaterialApp。
- Row布局中TextField无宽度约束:Row组件内的TextField无法自动确定自身尺寸,触发无尺寸渲染错误。
- 长文本未做换行处理:部分题目文本过长,在Row中挤压TextField空间,加剧布局冲突。
修复方案
- 移除页面中的MaterialApp:所有子页面直接返回Scaffold,保留根MyApp中的MaterialApp作为全局配置。
- 给TextField添加宽度约束:用
SizedBox指定TextField宽度,或通过Expanded分配布局空间。 - 优化文本布局:将长文本放入
Expanded组件,允许自动换行,避免挤压其他元素。
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:audioplayers/audioplayers.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Time Attack', theme: ThemeData( primarySwatch: Colors.red, ), home: const 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 Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.red, title: const Text('Time Attack'), centerTitle: true, ), floatingActionButton: FloatingActionButton( onPressed: () async { final player = AudioCache(); await player.load('musicForapp.mp3'); AudioPlayer p = AudioPlayer(); p.audioCache = player; await p.play(AssetSource('musicForapp.mp3')); }, backgroundColor: Colors.red, child: const Icon(Icons.punch_clock), ), body: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( "Click the Icon in the bottom right to simulate alarm", style: TextStyle( color: Colors.white, fontSize: 20.0, ), textAlign: TextAlign.center, ), const SizedBox(height: 30), ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.red), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const TasksPage(), ), ); }, child: const Text( "Tasks page", style: TextStyle(color: Colors.white), ), ) ], ), ), ); } } class TasksPage extends StatefulWidget { const TasksPage({super.key}); @override State<TasksPage> createState() => _TasksPageState(); } class _TasksPageState extends State<TasksPage> { final _textcontroller = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.red, title: const Text('Tasks'), centerTitle: true, ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const SecondTask(), ), ); }, backgroundColor: Colors.red, child: const Text("Next"), ), body: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: const Text( "How many states are there in the United States of America?", style: TextStyle( backgroundColor: Colors.white, color: Colors.black, fontSize: 20.0, ), textAlign: TextAlign.center, ), ), const SizedBox(width: 10), SizedBox( width: 150, child: TextField( controller: _textcontroller, decoration: InputDecoration( hintText: "Type your answer here!", border: const OutlineInputBorder(), suffixIcon: IconButton( onPressed: () { _textcontroller.clear(); }, icon: const Icon(Icons.clear), ), ), ), ), ], ), ), ), ); } } class SecondTask extends StatefulWidget { const SecondTask({super.key}); @override State<SecondTask> createState() => _SecondTask(); } class _SecondTask extends State<SecondTask> { final _textcontroller2 = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.red, title: const Text('Tasks'), center
相关产品推荐
相关产品推荐

