@@ -30,6 +30,7 @@ class TableRow {
30
30
this .repeat = false ,
31
31
this .verticalAlignment,
32
32
this .decoration,
33
+ this .columnSpans,
33
34
});
34
35
35
36
/// The widgets that comprise the cells in this row.
@@ -41,6 +42,8 @@ class TableRow {
41
42
final BoxDecoration ? decoration;
42
43
43
44
final TableCellVerticalAlignment ? verticalAlignment;
45
+
46
+ final Map <int , int >? columnSpans;
44
47
}
45
48
46
49
enum TableCellVerticalAlignment { bottom, middle, top, full }
@@ -91,40 +94,6 @@ class TableBorder extends Border {
91
94
92
95
final BorderSide horizontalInside;
93
96
final BorderSide verticalInside;
94
-
95
- void paintTable (Context context, PdfRect box,
96
- [List <double ?>? widths, List <double >? heights]) {
97
- super .paint (context, box);
98
-
99
- if (verticalInside.style.paint) {
100
- verticalInside.style.setStyle (context);
101
- var offset = box.x;
102
- for (final width in widths! .sublist (0 , widths.length - 1 )) {
103
- offset += width! ;
104
- context.canvas.moveTo (offset, box.y);
105
- context.canvas.lineTo (offset, box.top);
106
- }
107
- context.canvas.setStrokeColor (verticalInside.color);
108
- context.canvas.setLineWidth (verticalInside.width);
109
- context.canvas.strokePath ();
110
-
111
- verticalInside.style.unsetStyle (context);
112
- }
113
-
114
- if (horizontalInside.style.paint) {
115
- horizontalInside.style.setStyle (context);
116
- var offset = box.top;
117
- for (final height in heights! .sublist (0 , heights.length - 1 )) {
118
- offset -= height;
119
- context.canvas.moveTo (box.x, offset);
120
- context.canvas.lineTo (box.right, offset);
121
- }
122
- context.canvas.setStrokeColor (horizontalInside.color);
123
- context.canvas.setLineWidth (horizontalInside.width);
124
- context.canvas.strokePath ();
125
- horizontalInside.style.unsetStyle (context);
126
- }
127
- }
128
97
}
129
98
130
99
class TableContext extends WidgetContext {
@@ -315,7 +284,6 @@ class Table extends Widget with SpanningWidget {
315
284
316
285
final TableWidth tableWidth;
317
286
318
- final List <double ?> _widths = < double ? > [];
319
287
final List <double > _heights = < double > [];
320
288
321
289
final TableContext _context = TableContext ();
@@ -334,69 +302,110 @@ class Table extends Widget with SpanningWidget {
334
302
_context.firstLine = _context.lastLine;
335
303
}
336
304
305
+ List <Widget > _getFilledChildrenFromColumnSpans (TableRow row) {
306
+ if (row.columnSpans == null ) {
307
+ return row.children;
308
+ }
309
+ final filledChildren = < Widget > [];
310
+ var n = 0 ;
311
+ // TODO(Gustl22): Handle intrinsic column widths:
312
+ // Currently, every cell is calculated by filling the remaining spanned
313
+ // cells with empty containers and then sum up their calculated widths.
314
+ for (final child in row.children) {
315
+ // Columns, which are currently spanned.
316
+ final columnSpan = row.columnSpans! [n] ?? 1 ;
317
+ filledChildren.add (child);
318
+ if (columnSpan > 1 ) {
319
+ filledChildren
320
+ .addAll (Iterable .generate (columnSpan - 1 , (index) => Container ()));
321
+ }
322
+ n += columnSpan;
323
+ }
324
+ return filledChildren;
325
+ }
326
+
327
+ List <double ?> _getSpannedWidths (List <double ?> widths, TableRow row) {
328
+ final spannedWidths = < double ? > [];
329
+ var n = 0 ;
330
+ for (var i = 0 ; i < row.children.length; i++ ) {
331
+ final columnSpan = row.columnSpans? [n] ?? 1 ;
332
+ final indices = Iterable .generate (columnSpan, (span) => n + span);
333
+ final width = indices.fold <double ?>(null , (prev, curIndex) {
334
+ final current = widths[curIndex];
335
+ if (prev == null && current == null ) {
336
+ return null ;
337
+ }
338
+ return (prev ?? 0 ) + (current ?? 0 );
339
+ });
340
+ spannedWidths.add (width);
341
+ n += columnSpan;
342
+ }
343
+ return spannedWidths;
344
+ }
345
+
337
346
@override
338
347
void layout (Context context, BoxConstraints constraints,
339
348
{bool parentUsesSize = false }) {
340
349
// Compute required width for all row/columns width flex
341
350
final flex = < double ? > [];
342
- _widths. clear () ;
351
+ final widths = < double ? > [] ;
343
352
_heights.clear ();
344
353
var index = 0 ;
345
354
346
355
for (final row in children) {
347
356
var n = 0 ;
348
- for (final child in row.children ) {
357
+ for (final child in _getFilledChildrenFromColumnSpans ( row) ) {
349
358
final columnWidth = columnWidths != null && columnWidths! [n] != null
350
359
? columnWidths! [n]!
351
360
: defaultColumnWidth;
352
361
final columnLayout = columnWidth.layout (child, context, constraints);
353
362
if (flex.length < n + 1 ) {
354
363
flex.add (columnLayout.flex);
355
- _widths .add (columnLayout.width);
364
+ widths .add (columnLayout.width);
356
365
} else {
357
366
if (columnLayout.flex! > 0 ) {
358
367
flex[n] = math.max (flex[n]! , columnLayout.flex! );
359
368
}
360
- _widths [n] = math.max (_widths [n]! , columnLayout.width! );
369
+ widths [n] = math.max (widths [n]! , columnLayout.width! );
361
370
}
362
371
n++ ;
363
372
}
364
373
}
365
374
366
- if (_widths .isEmpty) {
375
+ if (widths .isEmpty) {
367
376
box = PdfRect .fromPoints (PdfPoint .zero, constraints.smallest);
368
377
return ;
369
378
}
370
379
371
- final maxWidth = _widths .reduce ((double ? a, double ? b) => a! + b! );
380
+ final maxWidth = widths .reduce ((double ? a, double ? b) => a! + b! );
372
381
373
382
// Compute column widths using flex and estimated width
374
383
if (constraints.hasBoundedWidth) {
375
384
final totalFlex = flex.reduce ((double ? a, double ? b) => a! + b! )! ;
376
385
var flexSpace = 0.0 ;
377
- for (var n = 0 ; n < _widths .length; n++ ) {
386
+ for (var n = 0 ; n < widths .length; n++ ) {
378
387
if (flex[n] == 0.0 ) {
379
- final newWidth = _widths [n]! / maxWidth! * constraints.maxWidth;
388
+ final newWidth = widths [n]! / maxWidth! * constraints.maxWidth;
380
389
if ((tableWidth == TableWidth .max && totalFlex == 0.0 ) ||
381
- newWidth < _widths [n]! ) {
382
- _widths [n] = newWidth;
390
+ newWidth < widths [n]! ) {
391
+ widths [n] = newWidth;
383
392
}
384
- flexSpace += _widths [n]! ;
393
+ flexSpace += widths [n]! ;
385
394
}
386
395
}
387
396
final spacePerFlex = totalFlex > 0.0
388
397
? ((constraints.maxWidth - flexSpace) / totalFlex)
389
398
: double .nan;
390
399
391
- for (var n = 0 ; n < _widths .length; n++ ) {
400
+ for (var n = 0 ; n < widths .length; n++ ) {
392
401
if (flex[n]! > 0.0 ) {
393
402
final newWidth = spacePerFlex * flex[n]! ;
394
- _widths [n] = newWidth;
403
+ widths [n] = newWidth;
395
404
}
396
405
}
397
406
}
398
407
399
- final totalWidth = _widths .reduce ((double ? a, double ? b) => a! + b! )! ;
408
+ final totalWidth = widths .reduce ((double ? a, double ? b) => a! + b! )! ;
400
409
401
410
// Compute final widths
402
411
var totalHeight = 0.0 ;
@@ -406,17 +415,21 @@ class Table extends Widget with SpanningWidget {
406
415
continue ;
407
416
}
408
417
418
+ final spannedWidths = _getSpannedWidths (widths, row);
419
+
409
420
var n = 0 ;
410
421
var x = 0.0 ;
411
422
412
423
var lineHeight = 0.0 ;
424
+
413
425
for (final child in row.children) {
414
- final childConstraints = BoxConstraints .tightFor (width: _widths[n]);
426
+ final childConstraints =
427
+ BoxConstraints .tightFor (width: spannedWidths[n]);
415
428
child.layout (context, childConstraints);
416
429
assert (child.box != null );
417
430
child.box =
418
431
PdfRect (x, totalHeight, child.box! .width, child.box! .height);
419
- x += _widths [n]! ;
432
+ x += spannedWidths [n]! ;
420
433
lineHeight = math.max (lineHeight, child.box! .height);
421
434
n++ ;
422
435
}
@@ -428,13 +441,13 @@ class Table extends Widget with SpanningWidget {
428
441
n = 0 ;
429
442
x = 0 ;
430
443
for (final child in row.children) {
431
- final childConstraints =
432
- BoxConstraints . tightFor ( width: _widths [n], height: lineHeight);
444
+ final childConstraints = BoxConstraints . tightFor (
445
+ width: spannedWidths [n], height: lineHeight);
433
446
child.layout (context, childConstraints);
434
447
assert (child.box != null );
435
448
child.box =
436
449
PdfRect (x, totalHeight, child.box! .width, child.box! .height);
437
- x += _widths [n]! ;
450
+ x += spannedWidths [n]! ;
438
451
n++ ;
439
452
}
440
453
}
@@ -527,14 +540,36 @@ class Table extends Widget with SpanningWidget {
527
540
);
528
541
}
529
542
530
- for (final child in row.children) {
543
+ for (final cell in row.children) {
544
+ final cellBox = cell.box! ;
531
545
context.canvas
532
546
..saveContext ()
533
- ..drawRect (
534
- child.box! .x, child.box! .y, child.box! .width, child.box! .height)
547
+ ..drawRect (cellBox.x, cellBox.y, cellBox.width, cellBox.height)
535
548
..clipPath ();
536
- child .paint (context);
549
+ cell .paint (context);
537
550
context.canvas.restoreContext ();
551
+ if (border? .verticalInside.style.paint == true &&
552
+ cell != row.children.first) {
553
+ border! .verticalInside.style.setStyle (context);
554
+
555
+ context.canvas.moveTo (cellBox.x, cellBox.bottom);
556
+ context.canvas.lineTo (cellBox.x, cellBox.top);
557
+ context.canvas.setStrokeColor (border! .verticalInside.color);
558
+ context.canvas.setLineWidth (border! .verticalInside.width);
559
+ context.canvas.strokePath ();
560
+
561
+ border! .verticalInside.style.unsetStyle (context);
562
+ }
563
+ if (border? .horizontalInside.style.paint == true &&
564
+ row != children.first) {
565
+ border! .horizontalInside.style.setStyle (context);
566
+ context.canvas.moveTo (cellBox.left, cellBox.top);
567
+ context.canvas.lineTo (cellBox.right, cellBox.top);
568
+ context.canvas.setStrokeColor (border! .horizontalInside.color);
569
+ context.canvas.setLineWidth (border! .horizontalInside.width);
570
+ context.canvas.strokePath ();
571
+ border! .horizontalInside.style.unsetStyle (context);
572
+ }
538
573
}
539
574
if (index >= _context.lastLine) {
540
575
break ;
@@ -568,8 +603,9 @@ class Table extends Widget with SpanningWidget {
568
603
569
604
context.canvas.restoreContext ();
570
605
606
+ // Paint outside borders
571
607
if (border != null ) {
572
- border! .paintTable (context, box! , _widths, _heights );
608
+ border! .paint (context, box! );
573
609
}
574
610
}
575
611
0 commit comments