Flutter组件布局异常:打开‘添加书籍’页面出现白屏无文本框
问题分析:Flutter添加书籍窗口白屏与布局错误
问题现象
- 打开“添加书籍”窗口时出现白屏,无法看到文本框,仅显示空白页面
- 控制台抛出以下错误信息:
NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
RenderRepaintBoundary#5e192 relayoutBoundary=up1 NEEDS-PAINT
Failed assertion: line 1785 pos 12: 'hasSize'
[ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Cannot hit test a render box that has never been laid out.
相关Build方法代码
@override Widget build(BuildContext context) { return Scaffold( //resizeToAvoidBottomPadding: false, appBar: AppBar(title: Text("Add book")), body: Container( child: Form( key: _formKey, child: Column( children: [ Expanded( child: SingleChildScrollView( child: Column( children: <Widget>[ // Book name Padding( padding: EdgeInsets.all(20.0), child: Container( //height: 30, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(20)), ), //alignment: Alignment.center, child: Container( padding: const EdgeInsets.only( top: 10, bottom: 10, left: 20, right: 20), decoration: new BoxDecoration( color: Color(0x70487bea), borderRadius: new BorderRadius.all(Radius.circular(30))), child: TextFormField( controller: nameController, decoration: InputDecoration( border: InputBorder.none, hintText: " Write name", hintStyle: TextStyle(color: Colors.grey[900]), ), ), ), ), ), // Author Padding( padding: EdgeInsets.all(20.0), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(20)), ), //alignment: Alignment.center, child: Container( padding: const EdgeInsets.only( top: 10, bottom: 10, left: 20, right: 20), decoration: new BoxDecoration( color: Color(0x70487bea), borderRadius: new BorderRadius.all(Radius.circular(30))), child: TextFormField( controller: authorController, decoration: InputDecoration( border: InputBorder.none, hintText: " Write name", hintStyle: TextStyle(color: Colors.grey[900]), ), ), ), ), ), // Category Padding( padding: EdgeInsets.all(20.0), child: DropdownButtonFormField( value: dropdownValue, icon: Icon(Icons.arrow_downward), decoration: InputDecoration( labelText: "Choose category", enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), ), ), //alignment: Alignment.center, items: listOfCategories.map((String value) { return new DropdownMenuItem<String>( value: value, child: new Text(value), ); }).toList(), onChanged: (String newValue) { setState(() { dropdownValue = newValue; }); }, ), ), //temp placement // Submit Flexible( child: Padding( padding: EdgeInsets.all(20.0), child: isLoading ? CircularProgressIndicator() : Container( alignment: Alignment.center, width: MediaQuery.of(context).size.width, padding: EdgeInsets.symmetric(vertical: 10), child: RaisedButton( onPressed: () { if (_formKey.currentState.validate()) { setState(() { isLoading = true; }); //getUsername(); addToFb(); //dispose(); } }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), side: BorderSide(color: Colors.indigo[500])), color: Colors.indigo[400], child: Text( "Save", style: TextStyle( fontSize: 16.0, color: Colors.white), ), padding: EdgeInsets.fromLTRB(35, 15, 35, 15), ), ), ), ) ], ), ), ), ], ), ), ), ); }
问题根源分析
我帮你梳理下,你遇到的白屏和布局错误,核心是布局层级的尺寸约束搞混了,具体有这几个坑:
Expanded和SingleChildScrollView的搭配错了Expanded的作用是把父组件剩下的空间全占了,但SingleChildScrollView是靠子内容来撑大小的,你把它俩套一起,就会让SingleChildScrollView试图无限扩张,里面的Column根本算不出自己该多大,直接触发了'hasSize'的断言失败——这就是白屏的主要原因。
提交按钮外面的
Flexible完全没必要SingleChildScrollView里的组件不需要用Flexible来分配空间,这玩意儿在滚动视图里根本起不了作用,反而会干扰Flutter的布局计算,导致渲染框连布局都完成不了,自然就报“无法点击未布局的渲染框”的错误了。
文本框的容器没给明确尺寸
- 你套了好几层
Container,但外层的都没设高度,Flutter不知道这些容器该多大,也会让布局计算乱套。
- 你套了好几层
修复方案
按照这几步调整代码,问题应该就能解决:
删掉多余的
Expanded和Flexible- 把
body里包着SingleChildScrollView的Expanded去掉,直接让滚动视图作为Form的子组件就行,滚动视图本身会自适应内容高度,不需要硬拉它占满空间。 - 提交按钮外面的
Flexible也删掉,让按钮自己该多大就多大。
- 把
简化容器嵌套,给文本框容器加个明确高度
- 你原来文本框套了两层
Container,可以合并成一层,同时给这个容器设个高度(比如50),这样Flutter就能明确知道文本框区域的大小,不会再乱计算。
- 你原来文本框套了两层
调整后的代码示例
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Add book")), body: Container( padding: EdgeInsets.symmetric(horizontal: 16), child: Form( key: _formKey, child: SingleChildScrollView( padding: EdgeInsets.symmetric(vertical: 20), child: Column( children: <Widget>[ // Book name Padding( padding: EdgeInsets.only(bottom: 20.0), child: Container( height: 50, decoration: BoxDecoration( color: Color(0x70487bea), borderRadius: BorderRadius.all(Radius.circular(30)) ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: TextFormField( controller: nameController, decoration: InputDecoration( border: InputBorder.none, hintText: " Write book name", hintStyle: TextStyle(color: Colors.grey[900]), ), ), ), ), ), // Author Padding( padding: EdgeInsets.only(bottom: 20.0), child: Container( height: 50, decoration: BoxDecoration( color: Color(0x70487bea), borderRadius: BorderRadius.all(Radius.circular(30)) ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: TextFormField( controller: authorController, decoration: InputDecoration( border: InputBorder.none, hintText: " Write author name", hintStyle: TextStyle(color: Colors.grey[900]), ), ), ), ), ), // Category Padding( padding: EdgeInsets.only(bottom: 20.0), child: DropdownButtonFormField( value: dropdownValue, icon: Icon(Icons.arrow_downward), decoration: InputDecoration( labelText: "Choose category", enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), ), ), items: listOfCategories.map((String value) { return DropdownMenuItem<String>( value: value, child: Text(value), ); }).toList(), onChanged: (String newValue) { setState(() { dropdownValue = newValue; }); }, ), ), // Submit Padding( padding: EdgeInsets.all(20.0), child: isLoading ? CircularProgressIndicator() : Container( width: double.infinity, child: RaisedButton( onPressed: () { if (_formKey.currentState.validate()) { setState(() { isLoading = true; }); addToFb(); } }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), side: BorderSide(color: Colors.indigo[500])), color: Colors.indigo[400], child: Text( "Save", style: TextStyle( fontSize: 16.0, color: Colors.white), ), padding: EdgeInsets.fromLTRB(35, 15, 35, 15), ), ), ), ], ), ), ), ), ); }
内容的提问来源于stack exchange,提问作者Chorche
相关产品推荐
相关产品推荐

