David PHAM-VAN

Allow MultiPage to relayout individual pages with support for flex

# Changelog
## 1.9.0
- Allow MultiPage to relayout individual pages with support for flex
## 1.8.1
- Fix Wrap break condition
... ...
... ... @@ -50,15 +50,11 @@ class NewPage extends Widget {
class _MultiPageWidget {
const _MultiPageWidget({
@required this.child,
@required this.x,
@required this.y,
@required this.constraints,
@required this.widgetContext,
});
final Widget child;
final double x;
final double y;
final BoxConstraints constraints;
final WidgetContext widgetContext;
}
... ... @@ -80,18 +76,21 @@ class _MultiPageInstance {
}
class MultiPage extends Page {
MultiPage(
{PageTheme pageTheme,
PdfPageFormat pageFormat,
BuildListCallback build,
this.crossAxisAlignment = CrossAxisAlignment.start,
this.header,
this.footer,
ThemeData theme,
this.maxPages = 20,
PageOrientation orientation,
EdgeInsets margin})
: _buildList = build,
MultiPage({
PageTheme pageTheme,
PdfPageFormat pageFormat,
BuildListCallback build,
this.mainAxisAlignment = MainAxisAlignment.start,
this.crossAxisAlignment = CrossAxisAlignment.start,
this.header,
this.footer,
ThemeData theme,
this.maxPages = 20,
PageOrientation orientation,
EdgeInsets margin,
}) : _buildList = build,
assert(mainAxisAlignment != null),
assert(crossAxisAlignment != null),
assert(maxPages != null && maxPages > 0),
super(
pageTheme: pageTheme,
... ... @@ -108,6 +107,8 @@ class MultiPage extends Page {
final BuildCallback footer;
final MainAxisAlignment mainAxisAlignment;
final List<_MultiPageInstance> _pages = <_MultiPageInstance>[];
final int maxPages;
... ... @@ -202,7 +203,7 @@ class MultiPage extends Page {
}());
offsetStart = pageHeight -
(_mustRotate ? pageHeightMargin - margin.bottom : _margin.top);
(_mustRotate ? pageHeightMargin - _margin.bottom : _margin.top);
offsetEnd =
_mustRotate ? pageHeightMargin - _margin.left : _margin.bottom;
... ... @@ -268,8 +269,6 @@ class MultiPage extends Page {
_pages.last.widgets.add(
_MultiPageWidget(
child: child,
x: _margin.left,
y: offsetStart - child.box.height,
constraints: localConstraints,
widgetContext: widgetContext?.clone(),
),
... ... @@ -289,8 +288,6 @@ class MultiPage extends Page {
_pages.last.widgets.add(
_MultiPageWidget(
child: child,
x: _margin.left,
y: offsetStart - child.box.height,
constraints: constraints,
widgetContext: child is SpanningWidget && canSpan
? child.saveContext().clone()
... ... @@ -307,8 +304,22 @@ class MultiPage extends Page {
@override
void postProcess(Document document) {
final EdgeInsets _margin = margin;
final bool _mustRotate = mustRotate;
final double pageHeight =
_mustRotate ? pageFormat.width : pageFormat.height;
final double pageWidth = _mustRotate ? pageFormat.height : pageFormat.width;
final double pageHeightMargin =
_mustRotate ? _margin.horizontal : _margin.vertical;
final double pageWidthMargin =
_mustRotate ? _margin.vertical : _margin.horizontal;
final double availableWidth = pageWidth - pageWidthMargin;
for (_MultiPageInstance page in _pages) {
double offsetStart = pageHeight -
(_mustRotate ? pageHeightMargin - _margin.bottom : _margin.top);
double offsetEnd =
_mustRotate ? pageHeightMargin - _margin.left : _margin.bottom;
if (pageTheme.buildBackground != null) {
final Widget child = pageTheme.buildBackground(page.context);
if (child != null) {
... ... @@ -320,16 +331,25 @@ class MultiPage extends Page {
}
}
int totalFlex = 0;
double allocatedSize = 0;
Widget lastFlexChild;
for (_MultiPageWidget widget in page.widgets) {
final Widget child = widget.child;
if (child is SpanningWidget && child.canSpan) {
final WidgetContext context = child.saveContext();
context.apply(widget.widgetContext);
final int flex = child is Flexible ? child.flex : 0;
if (flex > 0) {
totalFlex += flex;
lastFlexChild = child;
} else {
if (child is SpanningWidget && child.canSpan) {
final WidgetContext context = child.saveContext();
context.apply(widget.widgetContext);
}
child.layout(page.context, widget.constraints, parentUsesSize: false);
assert(child.box != null);
allocatedSize += child.box.height;
}
child.layout(page.context, widget.constraints, parentUsesSize: false);
assert(child.box != null);
_paintChild(
page.context, widget.child, widget.x, widget.y, pageFormat.height);
}
if (header != null) {
... ... @@ -338,6 +358,7 @@ class MultiPage extends Page {
headerWidget.layout(page.context, page.constraints,
parentUsesSize: false);
assert(headerWidget.box != null);
offsetStart -= headerWidget.box.height;
_paintChild(page.context, headerWidget, _margin.left,
page.offsetStart - headerWidget.box.height, pageFormat.height);
}
... ... @@ -349,11 +370,114 @@ class MultiPage extends Page {
footerWidget.layout(page.context, page.constraints,
parentUsesSize: false);
assert(footerWidget.box != null);
offsetEnd += footerWidget.box.height;
_paintChild(page.context, footerWidget, _margin.left, _margin.bottom,
pageFormat.height);
}
}
final double freeSpace =
math.max(0, offsetStart - offsetEnd - allocatedSize);
final double spacePerFlex =
totalFlex > 0 ? (freeSpace / totalFlex) : double.nan;
double allocatedFlexSpace = 0;
double leadingSpace = 0;
double betweenSpace = 0;
if (totalFlex == 0) {
final int totalChildren = page.widgets.length;
switch (mainAxisAlignment) {
case MainAxisAlignment.start:
leadingSpace = 0.0;
betweenSpace = 0.0;
break;
case MainAxisAlignment.end:
leadingSpace = freeSpace;
betweenSpace = 0.0;
break;
case MainAxisAlignment.center:
leadingSpace = freeSpace / 2.0;
betweenSpace = 0.0;
break;
case MainAxisAlignment.spaceBetween:
leadingSpace = 0.0;
betweenSpace =
totalChildren > 1 ? freeSpace / (totalChildren - 1) : 0.0;
break;
case MainAxisAlignment.spaceAround:
betweenSpace = totalChildren > 0 ? freeSpace / totalChildren : 0.0;
leadingSpace = betweenSpace / 2.0;
break;
case MainAxisAlignment.spaceEvenly:
betweenSpace =
totalChildren > 0 ? freeSpace / (totalChildren + 1) : 0.0;
leadingSpace = betweenSpace;
break;
}
}
for (_MultiPageWidget widget in page.widgets) {
final Widget child = widget.child;
final int flex = child is Flexible ? child.flex : 0;
final FlexFit fit = child is Flexible ? child.fit : FlexFit.loose;
if (flex > 0) {
assert(child is! SpanningWidget);
final double maxChildExtent = child == lastFlexChild
? (freeSpace - allocatedFlexSpace)
: spacePerFlex * flex;
double minChildExtent;
switch (fit) {
case FlexFit.tight:
assert(maxChildExtent < double.infinity);
minChildExtent = maxChildExtent;
break;
case FlexFit.loose:
minChildExtent = 0.0;
break;
}
assert(minChildExtent != null);
final BoxConstraints innerConstraints = BoxConstraints(
minWidth: widget.constraints.maxWidth,
maxWidth: widget.constraints.maxWidth,
minHeight: minChildExtent,
maxHeight: maxChildExtent);
child.layout(page.context, innerConstraints, parentUsesSize: false);
assert(child.box != null);
final double childSize = child.box.height;
assert(childSize <= maxChildExtent);
allocatedSize += childSize;
allocatedFlexSpace += maxChildExtent;
}
}
double pos = offsetStart - leadingSpace;
for (_MultiPageWidget widget in page.widgets) {
pos -= widget.child.box.height;
double x;
switch (crossAxisAlignment) {
case CrossAxisAlignment.start:
x = 0;
break;
case CrossAxisAlignment.end:
x = availableWidth - widget.child.box.width;
break;
case CrossAxisAlignment.center:
x = availableWidth / 2 - widget.child.box.width / 2;
break;
case CrossAxisAlignment.stretch:
x = 0;
break;
}
_paintChild(page.context, widget.child, _margin.left + x, pos,
pageFormat.height);
pos -= betweenSpace;
}
if (pageTheme.buildForeground != null) {
final Widget child = pageTheme.buildForeground(page.context);
if (child != null) {
... ...
... ... @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl
homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf
repository: https://github.com/DavBfr/dart_pdf
issue_tracker: https://github.com/DavBfr/dart_pdf/issues
version: 1.8.1
version: 1.9.0
environment:
sdk: ">=2.3.0 <3.0.0"
... ...
... ... @@ -93,6 +93,24 @@ void main() {
);
});
test('MultiPage Spacer', () {
pdf.addPage(
MultiPage(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
build: (Context context) => <Widget>[
for (int i = 0; i < 60; i++) Text('Begin $i'),
Spacer(), // Defaults to a flex of one.
Text('Middle'),
// Gives twice the space between Middle and End than Begin and Middle.
Spacer(flex: 2),
// Expanded(flex: 2, child: SizedBox.shrink()),
Text('End'),
],
),
);
});
tearDownAll(() {
final File file = File('widgets-flex.pdf');
file.writeAsBytesSync(pdf.save());
... ...