I'm studing Flutter so I decided to create a Todo list app. First of all: my focus is a Windows app, that could be the problem tougth.
I'm tryng to do two lists, one with the to do items e other with done items. So I build a Scaffold Widget with a body using a Column with two lists:
import 'package:flutter/material.dart';
void main() => runApp(new TodoApp());
class TodoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Todo List',
home: new TodoList(),
);
}
}
class TodoList extends StatefulWidget {
@override
createState() => new TodoListState();
}
class TodoListState extends State<TodoList> {
List<String> _todoItems = [];
List<String> _doneItems = [];
Widget _buildTodoList() {
return new ListView.builder(
itemCount: _todoItems.length,
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: new Text(_todoItems[index]),
onTap: () => _promptRemoveTodoItem(index));
},
);
}
Widget _buildDoneList() {
return new ListView.builder(
itemCount: _doneItems.length,
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: new Text(_doneItems[index]),
onTap: () => _promptRemoveDoneItem(index));
},
);
}
void _promptRemoveTodoItem(int index) {
showDialog(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
title: new Text('Mark "${_todoItems[index]}" as done?'),
actions: <Widget>[
new TextButton(
child: new Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
new TextButton(
onPressed: () {
_removeTodoItem(index);
Navigator.of(context).pop();
},
child: new Text('Mark as done'),
)
]);
});
}
void _removeTodoItem(int index) {
setState(() {
_doneItems.add(_todoItems.removeAt(index));
});
}
void _promptRemoveDoneItem(int index) {
showDialog(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
title: new Text('Redo "${_doneItems[index]}" ?'),
actions: <Widget>[
new TextButton(
child: new Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
new TextButton(
onPressed: () {
_removeDoneItem(index);
Navigator.of(context).pop();
},
child: new Text('Redo task'),
)
]);
});
}
void _removeDoneItem(int index) {
setState(() {
_todoItems.add(_doneItems.removeAt(index));
});
}
void _pushAddTodoScreen() {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Add a new task')),
body: new TextField(
autofocus: true,
onSubmitted: (val) {
_addTodoItem(val);
Navigator.pop(context);
},
decoration: new InputDecoration(
hintText: 'Enter something to do...',
contentPadding: const EdgeInsets.all(16.0)),
));
}));
}
void _addTodoItem(String task) {
if (task.length > 0) {
setState(() => _todoItems.add(task));
}
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Todo List')),
body: Column(children: <Widget>[
_buildTodoList(),
_buildDoneList(),
]),
floatingActionButton: new FloatingActionButton(
onPressed: _pushAddTodoScreen,
tooltip: 'Add task',
child: new Icon(Icons.add)),
);
}
}
but when I run flutter run -d windows
I got a strange error:
[ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: Cannot hit test a render box that has never been laid out.
The hitTest() method was called on this RenderBox: RenderStack#f0d97 NEEDS-LAYOUT NEEDS-PAINT:
needs compositing
creator: Stack ← _FloatingActionButtonTransition ← MediaQuery ← LayoutId-[<_ScaffoldSlot.floatingActionButton>] ← CustomMultiChildLayout ← AnimatedBuilder ← DefaultTextStyle ← AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#584bd ink renderer] ← NotificationListener<LayoutChangedNotification> ← PhysicalModel ← AnimatedPhysicalModel ← ⋯
parentData: offset=Offset(0.0, 0.0); id=_ScaffoldSlot.floatingActionButton
constraints: MISSING
size: MISSING
alignment: Alignment.centerRight
textDirection: ltr
fit: loose
Unfortunately, this object's geometry is not known at this time, probably because it has never been laid out. This means it cannot be accurately hit-tested.
If you are trying to perform a hit test during the layout phase itself, make sure you only hit test nodes that have completed layout (e.g. the node's children, after their layout() method has been called).
#0 RenderBox.hitTest.<anonymous closure> (package:flutter/src/rendering/box.dart:2391:11)
#1 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2422:6)
#2 RenderBoxContainerDefaultsMixin.defaultHitTestChildren.<anonymous closure> (package:flutter/src/rendering/box.dart:2785:25)
#3 BoxHitTestResult.addWithPaintOffset (package:flutter/src/rendering/box.dart:787:31)
#4 RenderBoxContainerDefaultsMixin.defaultHitTestChildren (package:flutter/src/rendering/box.dart:2780:33)
#5 RenderCustomMultiChildLayoutBox.hitTestChildren (package:flutter/src/rendering/custom_layout.dart:417:12)
#6 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#7 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#8 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#9 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#10 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#11 RenderPhysicalModel.hitTest (package:flutter/src/rendering/proxy_box.dart:1892:18)
#12 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#13 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#14 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#15 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#16 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#17 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#18 RenderIgnorePointer.hitTest (package:flutter/src/rendering/proxy_box.dart:3249:31)
#19 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#20 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#21 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#22 RenderFractionalTranslation.hitTestChildren.<anonymous closure> (package:flutter/src/rendering/proxy_box.dart:2706:22)
#23 BoxHitTestResult.addWithPaintOffset (package:flutter/src/rendering/box.dart:787:31)
#24 RenderFractionalTranslation.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:2700:19)
#25 RenderFractionalTranslation.hitTest (package:flutter/src/rendering/proxy_box.dart:2686:12)
#26 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#27 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#28 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#29 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#30 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#31 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#32 RenderOffstage.hitTest (package:flutter/src/rendering/proxy_box.dart:3368:31)
#33 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#34 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#35 _RenderTheatre.hitTestChildren.<anonymous closure> (package:flutter/src/widgets/overlay.dart:765:25)
#36 BoxHitTestResult.addWithPaintOffset (package:flutter/src/rendering/box.dart:787:31)
#37 _RenderTheatre.hitTestChildren (package:flutter/src/widgets/overlay.dart:760:33)
#38 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#39 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#40 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#41 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#42 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#43 RenderAbsorbPointer.hitTest (package:flutter/src/rendering/proxy_box.dart:3466:17)
#44 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#45 RenderProxyBoxWithHitTestBehavior.hitTest (package:flutter/src/rendering/proxy_box.dart:180:19)
#46 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#47 RenderCustomPaint.hitTestChildren (package:flutter/src/rendering/custom_paint.dart:536:18)
#48 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#49 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#50 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#51 RenderProxyBoxMixin.hitTestChildren (package:flutter/src/rendering/proxy_box.dart:133:19)
#52 RenderBox.hitTest (package:flutter/src/rendering/box.dart:2424:11)
#53 RenderView.hitTest (package:flutter/src/rendering/view.dart:173:14)
#54 RendererBinding.hitTest (package:flutter/src/rendering/binding.dart:481:16)
#55 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:288:7)
#56 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:280:5)
#57 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:238:7)
#58 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:221:7)
#59 _rootRunUnary (dart:async/zone.dart:1370:13)
#60 _CustomZone.runUnary (dart:async/zone.dart:1265:19)
#61 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1170:7)
#62 _invoke1 (dart:ui/hooks.dart:180:10)
#63 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:276:7)
#64 _dispatchPointerDataPacket (dart:ui/hooks.dart:96:31)
however if I use only one builder list without column on the scaffold's body, like this:
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Todo List')),
body: _buildTodoList(),
floatingActionButton: new FloatingActionButton(
onPressed: _pushAddTodoScreen,
tooltip: 'Add task',
child: new Icon(Icons.add)),
);
}
it works pretty well!
What's going on?