David PHAM-VAN

Convert Wrap to SpanningWidget

... ... @@ -55,9 +55,11 @@ class MultiPage extends Page {
this.header,
this.footer,
Theme theme,
this.maxPages = 20,
PageOrientation orientation = PageOrientation.natural,
EdgeInsets margin})
: _buildList = build,
assert(maxPages != null && maxPages > 0),
super(
pageFormat: pageFormat,
margin: margin,
... ... @@ -74,6 +76,8 @@ class MultiPage extends Page {
final List<_MultiPageInstance> _pages = <_MultiPageInstance>[];
final int maxPages;
void _paintChild(
Context context, Widget child, double x, double y, double pageHeight) {
if (mustRotate) {
... ... @@ -126,9 +130,9 @@ class MultiPage extends Page {
assert(() {
// Detect too big widgets
if (sameCount++ > 20) {
if (sameCount++ > maxPages) {
throw Exception(
'This widget created more than 20 pages. This may be an issue in the widget or the document.');
'This widget created more than $maxPages pages. This may be an issue in the widget or the document.');
}
return true;
}());
... ... @@ -188,7 +192,7 @@ class MultiPage extends Page {
// What to do if the widget is too big for the page?
if (offsetStart - child.box.height < offsetEnd) {
// If it is not a multi=page widget and its height
// If it is not a multi-page widget and its height
// is smaller than a full new page, we schedule a new page creation
if (child.box.height <= pageHeight - pageHeightMargin &&
!(child is SpanningWidget)) {
... ...
... ... @@ -37,8 +37,16 @@ class _RunMetrics {
final int childCount;
}
class _WrapContext extends WidgetContext {
int firstChild = 0;
int lastChild = 0;
@override
String toString() => 'WrapContext first:$firstChild last:$lastChild';
}
/// A widget that displays its children in multiple horizontal or vertical runs.
class Wrap extends MultiChildWidget {
class Wrap extends MultiChildWidget implements SpanningWidget {
/// Creates a wrap layout.
Wrap({
... ... @@ -83,7 +91,10 @@ class Wrap extends MultiChildWidget {
bool get textDirection => false;
bool _hasVisualOverflow = false;
@override
bool get canSpan => _context.lastChild < children.length;
final _WrapContext _context = _WrapContext();
bool get _debugHasNecessaryDirections {
assert(direction != null);
... ... @@ -192,9 +203,8 @@ class Wrap extends MultiChildWidget {
void layout(Context context, BoxConstraints constraints,
{bool parentUsesSize = false}) {
assert(_debugHasNecessaryDirections);
_hasVisualOverflow = false;
if (children.isEmpty) {
if (children.isEmpty || _context.firstChild >= children.length) {
box = PdfRect.fromPoints(PdfPoint.zero, constraints.smallest);
return;
}
... ... @@ -234,7 +244,7 @@ class Wrap extends MultiChildWidget {
double runCrossAxisExtent = 0.0;
int childCount = 0;
for (Widget child in children) {
for (Widget child in children.sublist(_context.firstChild)) {
child.layout(context, childConstraints, parentUsesSize: true);
final double childMainAxisExtent = _getMainAxisExtent(child);
... ... @@ -297,9 +307,6 @@ class Wrap extends MultiChildWidget {
break;
}
_hasVisualOverflow = containerMainAxisExtent < mainAxisExtent ||
containerCrossAxisExtent < crossAxisExtent;
final double crossAxisFreeSpace =
math.max(0.0, containerCrossAxisExtent - crossAxisExtent);
double runLeadingSpace = 0.0;
... ... @@ -333,7 +340,7 @@ class Wrap extends MultiChildWidget {
? containerCrossAxisExtent - runLeadingSpace
: runLeadingSpace;
int currentWidget = 0;
_context.lastChild = _context.firstChild;
for (int i = 0; i < runCount; ++i) {
final _RunMetrics metrics = runMetrics[i];
final double runMainAxisExtent = metrics.mainAxisExtent;
... ... @@ -377,6 +384,12 @@ class Wrap extends MultiChildWidget {
crossAxisOffset -= runCrossAxisExtent;
}
if (crossAxisOffset < -.01 ||
crossAxisOffset + runCrossAxisExtent > containerCrossAxisExtent) {
break;
}
int currentWidget = _context.lastChild;
for (Widget child in children.sublist(currentWidget)) {
final int runIndex = childRunMetrics[child];
if (runIndex != i) {
... ... @@ -407,6 +420,8 @@ class Wrap extends MultiChildWidget {
} else {
crossAxisOffset += runCrossAxisExtent + runBetweenSpace;
}
_context.lastChild = currentWidget;
}
}
... ... @@ -416,19 +431,26 @@ class Wrap extends MultiChildWidget {
context.canvas.saveContext();
if (_hasVisualOverflow) {
context.canvas
..drawRect(box.left, box.bottom, box.width, box.height)
..clipPath();
}
final Matrix4 mat = Matrix4.identity();
mat.translate(box.x, box.y);
context.canvas.setTransform(mat);
for (Widget child in children) {
for (Widget child
in children.sublist(_context.firstChild, _context.lastChild)) {
child.paint(context);
}
context.canvas.restoreContext();
}
@override
void restoreContext(WidgetContext context) {
if (context is _WrapContext) {
_context.firstChild = context.lastChild;
}
}
@override
WidgetContext saveContext() {
return _context;
}
}
... ...
... ... @@ -254,6 +254,36 @@ void main() {
);
});
test('Wrap Widget Multipage', () {
final math.Random rnd = math.Random(42);
pdf.addPage(
MultiPage(
pageFormat: const PdfPageFormat(200, 200),
margin: const EdgeInsets.all(10),
build: (Context context) => <Widget>[
Wrap(
direction: Axis.vertical,
verticalDirection: VerticalDirection.up,
alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 10,
runSpacing: 10,
children: List<Widget>.generate(
17,
(int n) => Container(
width: rnd.nextDouble() * 100,
height: rnd.nextDouble() * 100,
alignment: Alignment.center,
color: PdfColors.blue800,
child: Text('$n'),
)),
)
],
),
);
});
test('Wrap Widget Empty', () {
pdf.addPage(Page(build: (Context context) => Wrap()));
});
... ...