David PHAM-VAN

Convert Flex to a SpanningWidget

@@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
11 - Add more font drawing options 11 - Add more font drawing options
12 - Add Opacity Widget 12 - Add Opacity Widget
13 - Fix Text height with TrueType fonts 13 - Fix Text height with TrueType fonts
  14 +- Convert Flex to a SpanningWidget
14 15
15 ## 1.4.1 16 ## 1.4.1
16 17
@@ -56,7 +56,28 @@ enum VerticalDirection { @@ -56,7 +56,28 @@ enum VerticalDirection {
56 56
57 typedef _ChildSizingFunction = double Function(Widget child, double extent); 57 typedef _ChildSizingFunction = double Function(Widget child, double extent);
58 58
59 -class Flex extends MultiChildWidget { 59 +class _FlexContext extends WidgetContext {
  60 + int firstChild = 0;
  61 + int lastChild = 0;
  62 +
  63 + @override
  64 + void apply(WidgetContext other) {
  65 + if (other is _FlexContext) {
  66 + firstChild = other.firstChild;
  67 + lastChild = other.lastChild;
  68 + }
  69 + }
  70 +
  71 + @override
  72 + WidgetContext clone() {
  73 + return _FlexContext()..apply(this);
  74 + }
  75 +
  76 + @override
  77 + String toString() => '$runtimeType first:$firstChild last:$lastChild';
  78 +}
  79 +
  80 +class Flex extends MultiChildWidget implements SpanningWidget {
60 Flex({ 81 Flex({
61 @required this.direction, 82 @required this.direction,
62 this.mainAxisAlignment = MainAxisAlignment.start, 83 this.mainAxisAlignment = MainAxisAlignment.start,
@@ -80,6 +101,8 @@ class Flex extends MultiChildWidget { @@ -80,6 +101,8 @@ class Flex extends MultiChildWidget {
80 101
81 final VerticalDirection verticalDirection; 102 final VerticalDirection verticalDirection;
82 103
  104 + final _FlexContext _context = _FlexContext();
  105 +
83 double _getIntrinsicSize( 106 double _getIntrinsicSize(
84 {Axis sizingDirection, 107 {Axis sizingDirection,
85 double 108 double
@@ -209,7 +232,6 @@ class Flex extends MultiChildWidget { @@ -209,7 +232,6 @@ class Flex extends MultiChildWidget {
209 {bool parentUsesSize = false}) { 232 {bool parentUsesSize = false}) {
210 // Determine used flex factor, size inflexible items, calculate free space. 233 // Determine used flex factor, size inflexible items, calculate free space.
211 int totalFlex = 0; 234 int totalFlex = 0;
212 - final int totalChildren = children.length;  
213 Widget lastFlexChild; 235 Widget lastFlexChild;
214 assert(constraints != null); 236 assert(constraints != null);
215 final double maxMainSize = direction == Axis.horizontal 237 final double maxMainSize = direction == Axis.horizontal
@@ -219,8 +241,9 @@ class Flex extends MultiChildWidget { @@ -219,8 +241,9 @@ class Flex extends MultiChildWidget {
219 241
220 double crossSize = 0; 242 double crossSize = 0;
221 double allocatedSize = 0; // Sum of the sizes of the non-flexible children. 243 double allocatedSize = 0; // Sum of the sizes of the non-flexible children.
  244 + int index = _context.firstChild;
222 245
223 - for (Widget child in children) { 246 + for (Widget child in children.sublist(_context.firstChild)) {
224 final int flex = child is Flexible ? child.flex : 0; 247 final int flex = child is Flexible ? child.flex : 0;
225 final FlexFit fit = child is Flexible ? child.fit : FlexFit.loose; 248 final FlexFit fit = child is Flexible ? child.fit : FlexFit.loose;
226 if (flex > 0) { 249 if (flex > 0) {
@@ -266,9 +289,16 @@ class Flex extends MultiChildWidget { @@ -266,9 +289,16 @@ class Flex extends MultiChildWidget {
266 assert(child.box != null); 289 assert(child.box != null);
267 allocatedSize += _getMainSize(child); 290 allocatedSize += _getMainSize(child);
268 crossSize = math.max(crossSize, _getCrossSize(child)); 291 crossSize = math.max(crossSize, _getCrossSize(child));
  292 + if (direction == Axis.vertical &&
  293 + allocatedSize > constraints.maxHeight) {
  294 + break;
  295 + }
269 } 296 }
270 lastFlexChild = child; 297 lastFlexChild = child;
  298 + index++;
271 } 299 }
  300 + _context.lastChild = index;
  301 + final int totalChildren = _context.lastChild - _context.firstChild;
272 302
273 // Distribute free space to flexible children, and determine baseline. 303 // Distribute free space to flexible children, and determine baseline.
274 final double freeSpace = 304 final double freeSpace =
@@ -409,7 +439,9 @@ class Flex extends MultiChildWidget { @@ -409,7 +439,9 @@ class Flex extends MultiChildWidget {
409 direction == Axis.vertical); 439 direction == Axis.vertical);
410 double childMainPosition = 440 double childMainPosition =
411 flipMainAxis ? actualSize - leadingSpace : leadingSpace; 441 flipMainAxis ? actualSize - leadingSpace : leadingSpace;
412 - for (Widget child in children) { 442 +
  443 + for (Widget child
  444 + in children.sublist(_context.firstChild, _context.lastChild)) {
413 double childCrossPosition; 445 double childCrossPosition;
414 switch (crossAxisAlignment) { 446 switch (crossAxisAlignment) {
415 case CrossAxisAlignment.start: 447 case CrossAxisAlignment.start:
@@ -458,11 +490,31 @@ class Flex extends MultiChildWidget { @@ -458,11 +490,31 @@ class Flex extends MultiChildWidget {
458 context.canvas 490 context.canvas
459 ..saveContext() 491 ..saveContext()
460 ..setTransform(mat); 492 ..setTransform(mat);
461 - for (Widget child in children) { 493 +
  494 + for (Widget child
  495 + in children.sublist(_context.firstChild, _context.lastChild)) {
462 child.paint(context); 496 child.paint(context);
463 } 497 }
464 context.canvas.restoreContext(); 498 context.canvas.restoreContext();
465 } 499 }
  500 +
  501 + @override
  502 + bool get canSpan => direction == Axis.vertical;
  503 +
  504 + @override
  505 + bool get hasMoreWidgets => true;
  506 +
  507 + @override
  508 + void restoreContext(WidgetContext context) {
  509 + if (context is _FlexContext) {
  510 + _context.firstChild = context.lastChild;
  511 + }
  512 + }
  513 +
  514 + @override
  515 + WidgetContext saveContext() {
  516 + return _context;
  517 + }
466 } 518 }
467 519
468 class Row extends Flex { 520 class Row extends Flex {
@@ -328,6 +328,9 @@ class GridView extends MultiChildWidget implements SpanningWidget { @@ -328,6 +328,9 @@ class GridView extends MultiChildWidget implements SpanningWidget {
328 bool get canSpan => true; 328 bool get canSpan => true;
329 329
330 @override 330 @override
  331 + bool get hasMoreWidgets => true;
  332 +
  333 + @override
331 void restoreContext(WidgetContext context) { 334 void restoreContext(WidgetContext context) {
332 if (context is _GridViewContext) { 335 if (context is _GridViewContext) {
333 _context.firstChild = context.lastChild; 336 _context.firstChild = context.lastChild;
@@ -25,7 +25,9 @@ abstract class WidgetContext { @@ -25,7 +25,9 @@ abstract class WidgetContext {
25 } 25 }
26 26
27 abstract class SpanningWidget extends Widget { 27 abstract class SpanningWidget extends Widget {
28 - bool get canSpan => false; 28 + bool get canSpan;
  29 +
  30 + bool get hasMoreWidgets;
29 31
30 /// Get unmodified mutable context object 32 /// Get unmodified mutable context object
31 @protected 33 @protected
@@ -170,6 +172,10 @@ class MultiPage extends Page { @@ -170,6 +172,10 @@ class MultiPage extends Page {
170 172
171 while (index < children.length) { 173 while (index < children.length) {
172 final Widget child = children[index]; 174 final Widget child = children[index];
  175 + bool canSpan = false;
  176 + if (child is SpanningWidget) {
  177 + canSpan = child.canSpan;
  178 + }
173 179
174 assert(() { 180 assert(() {
175 // Detect too big widgets 181 // Detect too big widgets
@@ -226,7 +232,7 @@ class MultiPage extends Page { @@ -226,7 +232,7 @@ class MultiPage extends Page {
226 } 232 }
227 233
228 // If we are processing a multi-page widget, we restore its context 234 // If we are processing a multi-page widget, we restore its context
229 - if (widgetContext != null && child is SpanningWidget) { 235 + if (widgetContext != null && canSpan && child is SpanningWidget) {
230 child.restoreContext(widgetContext); 236 child.restoreContext(widgetContext);
231 widgetContext = null; 237 widgetContext = null;
232 } 238 }
@@ -238,14 +244,13 @@ class MultiPage extends Page { @@ -238,14 +244,13 @@ class MultiPage extends Page {
238 if (offsetStart - child.box.height < offsetEnd) { 244 if (offsetStart - child.box.height < offsetEnd) {
239 // If it is not a multi-page widget and its height 245 // If it is not a multi-page widget and its height
240 // is smaller than a full new page, we schedule a new page creation 246 // is smaller than a full new page, we schedule a new page creation
241 - if (child.box.height <= pageHeight - pageHeightMargin &&  
242 - !(child is SpanningWidget)) { 247 + if (child.box.height <= pageHeight - pageHeightMargin && !canSpan) {
243 context = null; 248 context = null;
244 continue; 249 continue;
245 } 250 }
246 251
247 // Else we crash if the widget is too big and cannot be splitted 252 // Else we crash if the widget is too big and cannot be splitted
248 - if (!(child is SpanningWidget)) { 253 + if (!canSpan) {
249 throw Exception( 254 throw Exception(
250 'Widget won\'t fit into the page as its height (${child.box.height}) ' 255 'Widget won\'t fit into the page as its height (${child.box.height}) '
251 'exceed a page height (${pageHeight - pageHeightMargin}). ' 256 'exceed a page height (${pageHeight - pageHeightMargin}). '
@@ -270,7 +275,7 @@ class MultiPage extends Page { @@ -270,7 +275,7 @@ class MultiPage extends Page {
270 ); 275 );
271 276
272 // Has it finished spanning? 277 // Has it finished spanning?
273 - if (!span.canSpan) { 278 + if (!span.hasMoreWidgets) {
274 sameCount = 0; 279 sameCount = 0;
275 index++; 280 index++;
276 } 281 }
@@ -286,8 +291,9 @@ class MultiPage extends Page { @@ -286,8 +291,9 @@ class MultiPage extends Page {
286 x: _margin.left, 291 x: _margin.left,
287 y: offsetStart - child.box.height, 292 y: offsetStart - child.box.height,
288 constraints: constraints, 293 constraints: constraints,
289 - widgetContext:  
290 - child is SpanningWidget ? child.saveContext().clone() : null, 294 + widgetContext: child is SpanningWidget && canSpan
  295 + ? child.saveContext().clone()
  296 + : null,
291 ), 297 ),
292 ); 298 );
293 299
@@ -315,7 +321,7 @@ class MultiPage extends Page { @@ -315,7 +321,7 @@ class MultiPage extends Page {
315 321
316 for (_MultiPageWidget widget in page.widgets) { 322 for (_MultiPageWidget widget in page.widgets) {
317 final Widget child = widget.child; 323 final Widget child = widget.child;
318 - if (child is SpanningWidget) { 324 + if (child is SpanningWidget && child.canSpan) {
319 final WidgetContext context = child.saveContext(); 325 final WidgetContext context = child.saveContext();
320 context.apply(widget.widgetContext); 326 context.apply(widget.widgetContext);
321 } 327 }
@@ -224,6 +224,9 @@ class Table extends Widget implements SpanningWidget { @@ -224,6 +224,9 @@ class Table extends Widget implements SpanningWidget {
224 @override 224 @override
225 bool get canSpan => true; 225 bool get canSpan => true;
226 226
  227 + @override
  228 + bool get hasMoreWidgets => true;
  229 +
227 /// The rows of the table. 230 /// The rows of the table.
228 final List<TableRow> children; 231 final List<TableRow> children;
229 232
@@ -109,7 +109,10 @@ class Wrap extends MultiChildWidget implements SpanningWidget { @@ -109,7 +109,10 @@ class Wrap extends MultiChildWidget implements SpanningWidget {
109 bool get textDirection => false; 109 bool get textDirection => false;
110 110
111 @override 111 @override
112 - bool get canSpan => _context.lastChild < children.length; 112 + bool get canSpan => true;
  113 +
  114 + @override
  115 + bool get hasMoreWidgets => _context.lastChild < children.length;
113 116
114 final _WrapContext _context = _WrapContext(); 117 final _WrapContext _context = _WrapContext();
115 118