David PHAM-VAN

Convert GridView to a SpanningWidget

@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 * Add Document properties 3 * Add Document properties
4 * Add Page.orientation to force landscape or portrait 4 * Add Page.orientation to force landscape or portrait
5 * Improve MultiPage Widget 5 * Improve MultiPage Widget
  6 +* Convert GridView to a SpanningWidget
6 7
7 # 1.3.3 8 # 1.3.3
8 * Fix a bug with the RichText Widget 9 * Fix a bug with the RichText Widget
@@ -16,7 +16,19 @@ @@ -16,7 +16,19 @@
16 16
17 part of widget; 17 part of widget;
18 18
19 -class GridView extends MultiChildWidget { 19 +class _GridViewContext extends WidgetContext {
  20 + int firstChild = 0;
  21 + int lastChild = 0;
  22 +
  23 + double childCrossAxis;
  24 + double childMainAxis;
  25 +
  26 + @override
  27 + String toString() =>
  28 + 'GridViewContext first:$firstChild last:$lastChild size:${childCrossAxis}x$childMainAxis';
  29 +}
  30 +
  31 +class GridView extends MultiChildWidget implements SpanningWidget {
20 GridView( 32 GridView(
21 {this.direction = Axis.vertical, 33 {this.direction = Axis.vertical,
22 this.padding = EdgeInsets.zero, 34 this.padding = EdgeInsets.zero,
@@ -35,15 +47,22 @@ class GridView extends MultiChildWidget { @@ -35,15 +47,22 @@ class GridView extends MultiChildWidget {
35 final double crossAxisSpacing; 47 final double crossAxisSpacing;
36 final double childAspectRatio; 48 final double childAspectRatio;
37 49
38 - double _childCrossAxis;  
39 - double _childMainAxis;  
40 - double _totalMain;  
41 - double _totalCross; 50 + final _GridViewContext _context = _GridViewContext();
  51 +
42 int _mainAxisCount; 52 int _mainAxisCount;
43 53
44 @override 54 @override
45 void layout(Context context, BoxConstraints constraints, 55 void layout(Context context, BoxConstraints constraints,
46 {bool parentUsesSize = false}) { 56 {bool parentUsesSize = false}) {
  57 + assert(() {
  58 + if (constraints.maxHeight.isInfinite && childAspectRatio.isInfinite) {
  59 + print(
  60 + 'Unable to calculate the GridView dimensions. Please set one the height constraints or childAspectRatio.');
  61 + return false;
  62 + }
  63 + return true;
  64 + }());
  65 +
47 double mainAxisExtent; 66 double mainAxisExtent;
48 double crossAxisExtent; 67 double crossAxisExtent;
49 switch (direction) { 68 switch (direction) {
@@ -57,16 +76,38 @@ class GridView extends MultiChildWidget { @@ -57,16 +76,38 @@ class GridView extends MultiChildWidget {
57 break; 76 break;
58 } 77 }
59 78
60 - _mainAxisCount = (children.length / crossAxisCount).ceil();  
61 - _childCrossAxis = crossAxisExtent / crossAxisCount - 79 + if (constraints.maxHeight.isInfinite || _mainAxisCount == null) {
  80 + _mainAxisCount =
  81 + ((children.length - _context.firstChild) / crossAxisCount).ceil();
  82 +
  83 + _context.childCrossAxis = crossAxisExtent / crossAxisCount -
62 (crossAxisSpacing * (crossAxisCount - 1) / crossAxisCount); 84 (crossAxisSpacing * (crossAxisCount - 1) / crossAxisCount);
63 - _childMainAxis = math.min(  
64 - _childCrossAxis * childAspectRatio, 85 +
  86 + _context.childMainAxis = math.min(
  87 + _context.childCrossAxis * childAspectRatio,
65 mainAxisExtent / _mainAxisCount - 88 mainAxisExtent / _mainAxisCount -
66 (mainAxisSpacing * (_mainAxisCount - 1) / _mainAxisCount)); 89 (mainAxisSpacing * (_mainAxisCount - 1) / _mainAxisCount));
67 - _totalMain =  
68 - (_childMainAxis + mainAxisSpacing) * _mainAxisCount - mainAxisSpacing;  
69 - _totalCross = (_childCrossAxis + crossAxisSpacing) * crossAxisCount - 90 +
  91 + if (_context.childCrossAxis.isInfinite) {
  92 + throw Exception(
  93 + 'Unable to calculate child height as the height constraint is infinite.');
  94 + }
  95 + } else {
  96 + _mainAxisCount = ((mainAxisExtent + mainAxisSpacing) /
  97 + (mainAxisSpacing + _context.childMainAxis))
  98 + .floor();
  99 +
  100 + if (_mainAxisCount < 0) {
  101 + // Not enough space to put one line, try to ask for more space.
  102 + _mainAxisCount = 0;
  103 + }
  104 + }
  105 +
  106 + final double totalMain =
  107 + (_context.childMainAxis + mainAxisSpacing) * _mainAxisCount -
  108 + mainAxisSpacing;
  109 + final double totalCross =
  110 + (_context.childCrossAxis + crossAxisSpacing) * crossAxisCount -
70 crossAxisSpacing; 111 crossAxisSpacing;
71 112
72 final double startX = padding.left; 113 final double startX = padding.left;
@@ -77,30 +118,35 @@ class GridView extends MultiChildWidget { @@ -77,30 +118,35 @@ class GridView extends MultiChildWidget {
77 switch (direction) { 118 switch (direction) {
78 case Axis.vertical: 119 case Axis.vertical:
79 innerConstraints = BoxConstraints.tightFor( 120 innerConstraints = BoxConstraints.tightFor(
80 - width: _childCrossAxis, height: _childMainAxis); 121 + width: _context.childCrossAxis, height: _context.childMainAxis);
81 crossAxis = startX; 122 crossAxis = startX;
82 mainAxis = startY; 123 mainAxis = startY;
83 break; 124 break;
84 case Axis.horizontal: 125 case Axis.horizontal:
85 innerConstraints = BoxConstraints.tightFor( 126 innerConstraints = BoxConstraints.tightFor(
86 - width: _childMainAxis, height: _childCrossAxis); 127 + width: _context.childMainAxis, height: _context.childCrossAxis);
87 mainAxis = startX; 128 mainAxis = startX;
88 crossAxis = startY; 129 crossAxis = startY;
89 break; 130 break;
90 } 131 }
91 132
92 int c = 0; 133 int c = 0;
93 - for (Widget child in children) { 134 + _context.lastChild = _context.firstChild;
  135 +
  136 + for (Widget child in children.sublist(
  137 + _context.firstChild,
  138 + math.min(children.length,
  139 + _context.firstChild + crossAxisCount * _mainAxisCount))) {
94 child.layout(context, innerConstraints); 140 child.layout(context, innerConstraints);
95 141
96 switch (direction) { 142 switch (direction) {
97 case Axis.vertical: 143 case Axis.vertical:
98 child.box = PdfRect.fromPoints( 144 child.box = PdfRect.fromPoints(
99 PdfPoint( 145 PdfPoint(
100 - (_childCrossAxis - child.box.width) / 2.0 + crossAxis,  
101 - _totalMain + 146 + (_context.childCrossAxis - child.box.width) / 2.0 + crossAxis,
  147 + totalMain +
102 padding.bottom - 148 padding.bottom -
103 - (_childMainAxis - child.box.height) / 2.0 - 149 + (_context.childMainAxis - child.box.height) / 2.0 -
104 mainAxis - 150 mainAxis -
105 child.box.height), 151 child.box.height),
106 child.box.size); 152 child.box.size);
@@ -108,10 +154,10 @@ class GridView extends MultiChildWidget { @@ -108,10 +154,10 @@ class GridView extends MultiChildWidget {
108 case Axis.horizontal: 154 case Axis.horizontal:
109 child.box = PdfRect.fromPoints( 155 child.box = PdfRect.fromPoints(
110 PdfPoint( 156 PdfPoint(
111 - (_childMainAxis - child.box.width) / 2.0 + mainAxis,  
112 - _totalCross + 157 + (_context.childMainAxis - child.box.width) / 2.0 + mainAxis,
  158 + totalCross +
113 padding.bottom - 159 padding.bottom -
114 - (_childCrossAxis - child.box.height) / 2.0 - 160 + (_context.childCrossAxis - child.box.height) / 2.0 -
115 crossAxis - 161 crossAxis -
116 child.box.height), 162 child.box.height),
117 child.box.size); 163 child.box.size);
@@ -119,24 +165,38 @@ class GridView extends MultiChildWidget { @@ -119,24 +165,38 @@ class GridView extends MultiChildWidget {
119 } 165 }
120 166
121 if (++c >= crossAxisCount) { 167 if (++c >= crossAxisCount) {
122 - mainAxis += _childMainAxis + mainAxisSpacing; 168 + mainAxis += _context.childMainAxis + mainAxisSpacing;
  169 + switch (direction) {
  170 + case Axis.vertical:
123 crossAxis = startX; 171 crossAxis = startX;
  172 + break;
  173 + case Axis.horizontal:
  174 + crossAxis = startY;
  175 + break;
  176 + }
124 c = 0; 177 c = 0;
  178 +
  179 + if (mainAxis > mainAxisExtent) {
  180 + _context.lastChild++;
  181 +
  182 + break;
  183 + }
125 } else { 184 } else {
126 - crossAxis += _childCrossAxis + crossAxisSpacing; 185 + crossAxis += _context.childCrossAxis + crossAxisSpacing;
127 } 186 }
  187 + _context.lastChild++;
128 } 188 }
129 189
130 switch (direction) { 190 switch (direction) {
131 case Axis.vertical: 191 case Axis.vertical:
132 box = constraints.constrainRect( 192 box = constraints.constrainRect(
133 - width: _totalCross + padding.horizontal,  
134 - height: _totalMain + padding.vertical); 193 + width: totalCross + padding.horizontal,
  194 + height: totalMain + padding.vertical);
135 break; 195 break;
136 case Axis.horizontal: 196 case Axis.horizontal:
137 box = constraints.constrainRect( 197 box = constraints.constrainRect(
138 - width: _totalMain + padding.horizontal,  
139 - height: _totalCross + padding.vertical); 198 + width: totalMain + padding.horizontal,
  199 + height: totalCross + padding.vertical);
140 break; 200 break;
141 } 201 }
142 } 202 }
@@ -164,7 +224,7 @@ class GridView extends MultiChildWidget { @@ -164,7 +224,7 @@ class GridView extends MultiChildWidget {
164 ..drawRect( 224 ..drawRect(
165 box.left + 225 box.left +
166 padding.left + 226 padding.left +
167 - (_childCrossAxis + crossAxisSpacing) * c - 227 + (_context.childCrossAxis + crossAxisSpacing) * c -
168 crossAxisSpacing, 228 crossAxisSpacing,
169 box.bottom + padding.bottom, 229 box.bottom + padding.bottom,
170 math.max(crossAxisSpacing, 1), 230 math.max(crossAxisSpacing, 1),
@@ -172,6 +232,16 @@ class GridView extends MultiChildWidget { @@ -172,6 +232,16 @@ class GridView extends MultiChildWidget {
172 ..fillPath(); 232 ..fillPath();
173 break; 233 break;
174 case Axis.horizontal: 234 case Axis.horizontal:
  235 + context.canvas
  236 + ..drawRect(
  237 + box.left + padding.left,
  238 + box.bottom +
  239 + padding.bottom +
  240 + (_context.childCrossAxis + crossAxisSpacing) * c -
  241 + crossAxisSpacing,
  242 + box.width - padding.horizontal,
  243 + math.max(crossAxisSpacing, 1))
  244 + ..fillPath();
175 break; 245 break;
176 } 246 }
177 } 247 }
@@ -184,13 +254,23 @@ class GridView extends MultiChildWidget { @@ -184,13 +254,23 @@ class GridView extends MultiChildWidget {
184 box.left + padding.left, 254 box.left + padding.left,
185 box.bottom + 255 box.bottom +
186 padding.bottom + 256 padding.bottom +
187 - (_childMainAxis + mainAxisSpacing) * c - 257 + (_context.childMainAxis + mainAxisSpacing) * c -
188 mainAxisSpacing, 258 mainAxisSpacing,
189 box.width - padding.horizontal, 259 box.width - padding.horizontal,
190 math.max(mainAxisSpacing, 1)) 260 math.max(mainAxisSpacing, 1))
191 ..fillPath(); 261 ..fillPath();
192 break; 262 break;
193 case Axis.horizontal: 263 case Axis.horizontal:
  264 + context.canvas
  265 + ..drawRect(
  266 + box.left +
  267 + padding.left +
  268 + (_context.childMainAxis + mainAxisSpacing) * c -
  269 + mainAxisSpacing,
  270 + box.bottom + padding.bottom,
  271 + math.max(mainAxisSpacing, 1),
  272 + box.height - padding.vertical)
  273 + ..fillPath();
194 break; 274 break;
195 } 275 }
196 } 276 }
@@ -205,9 +285,26 @@ class GridView extends MultiChildWidget { @@ -205,9 +285,26 @@ class GridView extends MultiChildWidget {
205 context.canvas 285 context.canvas
206 ..saveContext() 286 ..saveContext()
207 ..setTransform(mat); 287 ..setTransform(mat);
208 - for (Widget child in children) { 288 +
  289 + for (Widget child
  290 + in children.sublist(_context.firstChild, _context.lastChild)) {
209 child.paint(context); 291 child.paint(context);
210 } 292 }
211 context.canvas.restoreContext(); 293 context.canvas.restoreContext();
212 } 294 }
  295 +
  296 + @override
  297 + bool get canSpan => true;
  298 +
  299 + @override
  300 + void restoreContext(WidgetContext context) {
  301 + if (context is _GridViewContext) {
  302 + _context.firstChild = context.lastChild;
  303 + }
  304 + }
  305 +
  306 + @override
  307 + WidgetContext saveContext() {
  308 + return _context;
  309 + }
213 } 310 }