David PHAM-VAN

Add Stack Widget

... ... @@ -3,6 +3,7 @@
* Improve font bounds calculation
* Add RichText Widget
* Fix MultiPage max height
* Add Stack Widget
# 1.3.2
* Update Readme
... ...
... ... @@ -34,6 +34,7 @@ part 'widgets/geometry.dart';
part 'widgets/grid_view.dart';
part 'widgets/image.dart';
part 'widgets/placeholders.dart';
part 'widgets/stack.dart';
part 'widgets/table.dart';
part 'widgets/text.dart';
part 'widgets/theme.dart';
... ...
... ... @@ -32,6 +32,13 @@ class BoxConstraints {
minHeight = height != null ? height : 0.0,
maxHeight = height != null ? height : double.infinity;
/// Creates box constraints that is respected only by the given size.
BoxConstraints.tight(PdfPoint size)
: minWidth = size.x,
maxWidth = size.x,
minHeight = size.y,
maxHeight = size.y;
/// Creates box constraints that expand to fill another box constraints.
const BoxConstraints.expand({double width, double height})
: minWidth = width != null ? width : double.infinity,
... ...
/*
* Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of widget;
/// How to size the non-positioned children of a [Stack].
enum StackFit { loose, expand, passthrough }
/// Whether overflowing children should be clipped, or their overflow be
/// visible.
enum Overflow { visible, clip }
/// A widget that controls where a child of a [Stack] is positioned.
class Positioned extends SingleChildWidget {
Positioned({
this.left,
this.top,
this.right,
this.bottom,
@required Widget child,
}) : super(child: child);
final double left;
final double top;
final double right;
final double bottom;
double get width => box?.width;
double get height => box?.height;
}
/// A widget that positions its children relative to the edges of its box.
class Stack extends MultiChildWidget {
Stack({
this.alignment = Alignment.topLeft,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
List<Widget> children = const <Widget>[],
}) : super(children: children);
/// How to align the non-positioned and partially-positioned children in the
/// stack.
final Alignment alignment;
/// How to size the non-positioned children in the stack.
final StackFit fit;
/// Whether overflowing children should be clipped.
final Overflow overflow;
@override
void layout(Context context, BoxConstraints constraints,
{bool parentUsesSize = false}) {
final int childCount = children.length;
bool hasNonPositionedChildren = false;
if (childCount == 0) {
box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
return;
}
double width = constraints.minWidth;
double height = constraints.minHeight;
BoxConstraints nonPositionedConstraints;
assert(fit != null);
switch (fit) {
case StackFit.loose:
nonPositionedConstraints = constraints.loosen();
break;
case StackFit.expand:
nonPositionedConstraints = BoxConstraints.tight(constraints.biggest);
break;
case StackFit.passthrough:
nonPositionedConstraints = constraints;
break;
}
assert(nonPositionedConstraints != null);
for (Widget child in children) {
if (!(child is Positioned)) {
hasNonPositionedChildren = true;
child.layout(context, nonPositionedConstraints, parentUsesSize: true);
final PdfRect childSize = child.box;
width = math.max(width, childSize.width);
height = math.max(height, childSize.height);
}
}
if (hasNonPositionedChildren) {
box = PdfRect.fromPoints(PdfPoint.zero, PdfPoint(width, height));
assert(box.width == constraints.constrainWidth(width));
assert(box.height == constraints.constrainHeight(height));
} else {
box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
}
for (Widget child in children) {
if (!(child is Positioned)) {
child.box = PdfRect.fromPoints(
alignment.inscribe(child.box.size, box).offset, child.box.size);
} else {
final Positioned positioned = child;
BoxConstraints childConstraints = const BoxConstraints();
if (positioned.left != null && positioned.right != null)
childConstraints = childConstraints.tighten(
width: box.width - positioned.right - positioned.left);
else if (positioned.width != null)
childConstraints = childConstraints.tighten(width: positioned.width);
if (positioned.top != null && positioned.bottom != null)
childConstraints = childConstraints.tighten(
height: box.height - positioned.bottom - positioned.top);
else if (positioned.height != null)
childConstraints =
childConstraints.tighten(height: positioned.height);
positioned.layout(context, childConstraints, parentUsesSize: true);
double x;
if (positioned.left != null) {
x = positioned.left;
} else if (positioned.right != null) {
x = box.width - positioned.right - positioned.width;
} else {
x = alignment.inscribe(positioned.box.size, box).x;
}
double y;
if (positioned.bottom != null) {
y = positioned.bottom;
} else if (positioned.top != null) {
y = box.height - positioned.top - positioned.height;
} else {
y = alignment.inscribe(positioned.box.size, box).y;
}
positioned.box =
PdfRect.fromPoints(PdfPoint(x, y), positioned.box.size);
}
}
}
@override
void paint(Context context) {
super.paint(context);
final Matrix4 mat = Matrix4.identity();
mat.translate(box.x, box.y);
context.canvas
..saveContext()
..setTransform(mat);
if (overflow == Overflow.clip) {
context.canvas
..drawRect(0, 0, box.width, box.height)
..clipPath();
}
for (Widget child in children) {
child.paint(context);
}
context.canvas.restoreContext();
}
}
... ...
... ... @@ -133,32 +133,48 @@ void main() {
<String>['York Steak House', 'Outi Vuorinen', 'Finland'],
<String>['Weathervane', 'Else Jeremiassen', 'Iceland'],
]),
CustomPaint(
size: const PdfPoint(50, 50),
painter: (PdfGraphics canvas, PdfPoint size) {
canvas
..setColor(PdfColor.indigo)
..drawRRect(0, 0, size.x, size.y, 10, 10)
..fillPath();
}),
RichText(
text: TextSpan(
text: 'Hello ',
style: Theme.of(context).defaultTextStyle,
children: <TextSpan>[
TextSpan(
text: 'bold',
style: Theme.of(context)
.defaultTextStyleBold
.copyWith(fontSize: 20, color: PdfColor.blue)),
const TextSpan(
text: ' world!',
),
],
),
)
]));
pdf.addPage(Page(
pageFormat: const PdfPageFormat(400.0, 200.0),
margin: const EdgeInsets.all(10.0),
build: (Context context) => Stack(overflow: Overflow.visible,
// fit: StackFit.expand,
// alignment: Alignment.bottomRight,
children: <Widget>[
Positioned(
right: 10,
top: 10,
child: CustomPaint(
size: const PdfPoint(50, 50),
painter: (PdfGraphics canvas, PdfPoint size) {
canvas
..setColor(PdfColor.indigo)
..drawRRect(0, 0, size.x, size.y, 10, 10)
..fillPath();
})),
Positioned(
left: 10,
bottom: 10,
child: RichText(
text: TextSpan(
text: 'Hello ',
style: Theme.of(context).defaultTextStyle,
children: <TextSpan>[
TextSpan(
text: 'bold',
style: Theme.of(context)
.defaultTextStyleBold
.copyWith(
fontSize: 20, color: PdfColor.blue)),
const TextSpan(
text: ' world!',
),
],
),
))
])));
final File file = File('widgets.pdf');
file.writeAsBytesSync(pdf.document.save());
});
... ...