David PHAM-VAN

Add Partitions Widget

@@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
12 - Add Opacity Widget 12 - Add Opacity Widget
13 - Fix Text height with TrueType fonts 13 - Fix Text height with TrueType fonts
14 - Convert Flex to a SpanningWidget 14 - Convert Flex to a SpanningWidget
  15 +- Add Partitions Widget
15 16
16 ## 1.4.1 17 ## 1.4.1
17 18
@@ -41,6 +41,7 @@ part 'widgets/image.dart'; @@ -41,6 +41,7 @@ part 'widgets/image.dart';
41 part 'widgets/multi_page.dart'; 41 part 'widgets/multi_page.dart';
42 part 'widgets/page.dart'; 42 part 'widgets/page.dart';
43 part 'widgets/page_theme.dart'; 43 part 'widgets/page_theme.dart';
  44 +part 'widgets/partitions.dart';
44 part 'widgets/placeholders.dart'; 45 part 'widgets/placeholders.dart';
45 part 'widgets/progress.dart'; 46 part 'widgets/progress.dart';
46 part 'widgets/qrcode.dart'; 47 part 'widgets/qrcode.dart';
  1 +/*
  2 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +// ignore_for_file: omit_local_variable_types
  18 +
  19 +part of widget;
  20 +
  21 +class Partition implements SpanningWidget {
  22 + Partition({
  23 + @required this.child,
  24 + this.width,
  25 + int flex = 1,
  26 + }) : flex = width == null ? flex : 0,
  27 + assert(flex != null),
  28 + assert(child != null);
  29 +
  30 + final double width;
  31 +
  32 + final int flex;
  33 +
  34 + final SpanningWidget child;
  35 +
  36 + @override
  37 + PdfRect get box => child.box;
  38 +
  39 + @override
  40 + set box(PdfRect value) => child.box = value;
  41 +
  42 + @override
  43 + bool get canSpan => child.canSpan;
  44 +
  45 + @override
  46 + void debugPaint(Context context) {
  47 + child.debugPaint(context);
  48 + }
  49 +
  50 + @override
  51 + void layout(Context context, BoxConstraints constraints,
  52 + {bool parentUsesSize = false}) {
  53 + child.layout(context, constraints, parentUsesSize: parentUsesSize);
  54 + }
  55 +
  56 + @override
  57 + void paint(Context context) {
  58 + child.paint(context);
  59 + }
  60 +
  61 + @override
  62 + void restoreContext(WidgetContext context) {
  63 + child.restoreContext(context);
  64 + }
  65 +
  66 + @override
  67 + WidgetContext saveContext() {
  68 + return child.saveContext();
  69 + }
  70 +
  71 + @override
  72 + bool get hasMoreWidgets => child.hasMoreWidgets;
  73 +}
  74 +
  75 +class _PartitionsContext extends WidgetContext {
  76 + _PartitionsContext(int count) : partitionContext = List<WidgetContext>(count);
  77 +
  78 + final List<WidgetContext> partitionContext;
  79 +
  80 + @override
  81 + void apply(WidgetContext other) {
  82 + if (other is _PartitionsContext) {
  83 + for (int index = 0; index < partitionContext.length; index++) {
  84 + partitionContext[index]?.apply(other.partitionContext[index]);
  85 + }
  86 + }
  87 + }
  88 +
  89 + @override
  90 + WidgetContext clone() {
  91 + final _PartitionsContext context =
  92 + _PartitionsContext(partitionContext.length);
  93 + for (int index = 0; index < partitionContext.length; index++) {
  94 + context.partitionContext[index] = partitionContext[index].clone();
  95 + }
  96 +
  97 + return context;
  98 + }
  99 +}
  100 +
  101 +class Partitions extends Widget implements SpanningWidget {
  102 + Partitions({
  103 + this.children,
  104 + this.mainAxisSize = MainAxisSize.max,
  105 + }) : _context = _PartitionsContext(children.length),
  106 + super();
  107 +
  108 + final List<Partition> children;
  109 +
  110 + final _PartitionsContext _context;
  111 +
  112 + final MainAxisSize mainAxisSize;
  113 +
  114 + @override
  115 + bool get canSpan => children.any((Partition part) => part.canSpan);
  116 +
  117 + @override
  118 + bool get hasMoreWidgets =>
  119 + !children.any((Partition part) => !part.hasMoreWidgets);
  120 +
  121 + @override
  122 + void layout(Context context, BoxConstraints constraints,
  123 + {bool parentUsesSize = false}) {
  124 + assert(constraints != null);
  125 +
  126 + // Determine used flex factor, size inflexible items, calculate free space.
  127 + final double maxMainSize = constraints.maxWidth;
  128 + final bool canFlex = maxMainSize < double.infinity;
  129 + double allocatedSize = 0; // Sum of the sizes of the non-flexible children.
  130 + int totalFlex = 0;
  131 + final List<double> widths = List<double>.filled(children.length, 0);
  132 +
  133 + // Calculate fixed width columns
  134 + int index = 0;
  135 + for (Partition child in children) {
  136 + if (child.flex > 0) {
  137 + assert(() {
  138 + if (!canFlex) {
  139 + throw Exception(
  140 + 'Partition children have non-zero flex but incoming width constraints are unbounded.');
  141 + } else {
  142 + return true;
  143 + }
  144 + }());
  145 + totalFlex += child.flex;
  146 + } else {
  147 + allocatedSize += child.width;
  148 + widths[index] = child.width;
  149 + }
  150 + index++;
  151 + }
  152 +
  153 + // Distribute free space to flexible children, and determine baseline.
  154 + if (totalFlex > 0 && canFlex) {
  155 + final double freeSpace =
  156 + math.max(0, (canFlex ? maxMainSize : 0.0) - allocatedSize);
  157 + final double spacePerFlex = freeSpace / totalFlex;
  158 +
  159 + index = 0;
  160 + for (Partition child in children) {
  161 + if (child.flex > 0) {
  162 + final double childExtent = spacePerFlex * child.flex;
  163 + allocatedSize += childExtent;
  164 + widths[index] = childExtent;
  165 + }
  166 + index++;
  167 + }
  168 + }
  169 +
  170 + // Layout the columns and compute the total height
  171 + double totalHeight = 0;
  172 + index = 0;
  173 + for (Partition child in children) {
  174 + if (widths[index] > 0) {
  175 + final BoxConstraints innerConstraints = BoxConstraints(
  176 + minWidth: widths[index],
  177 + maxWidth: widths[index],
  178 + maxHeight: constraints.maxHeight);
  179 +
  180 + child.layout(context, innerConstraints);
  181 + assert(child.box != null);
  182 + totalHeight = math.max(totalHeight, child.box.height);
  183 + }
  184 + index++;
  185 + }
  186 +
  187 + // Update Y positions
  188 + index = 0;
  189 + allocatedSize = 0;
  190 + for (Partition child in children) {
  191 + if (widths[index] > 0) {
  192 + final double offsetY = totalHeight - child.box.height;
  193 + child.box = PdfRect.fromPoints(
  194 + PdfPoint(allocatedSize, offsetY), child.box.size);
  195 + totalHeight = math.max(totalHeight, child.box.height);
  196 + allocatedSize += widths[index];
  197 + }
  198 + index++;
  199 + }
  200 +
  201 + box = PdfRect(0, 0, allocatedSize, totalHeight);
  202 + }
  203 +
  204 + @override
  205 + void paint(Context context) {
  206 + super.paint(context);
  207 +
  208 + final Matrix4 mat = Matrix4.identity();
  209 + mat.translate(box.x, box.y);
  210 + context.canvas
  211 + ..saveContext()
  212 + ..setTransform(mat);
  213 + for (Partition child in children) {
  214 + child.paint(context);
  215 + }
  216 + context.canvas.restoreContext();
  217 + }
  218 +
  219 + @override
  220 + void restoreContext(WidgetContext context) {
  221 + _context.apply(context);
  222 + int index = 0;
  223 + for (final Partition child in children) {
  224 + child.restoreContext(_context.partitionContext[index]);
  225 + index++;
  226 + }
  227 + }
  228 +
  229 + @override
  230 + WidgetContext saveContext() {
  231 + int index = 0;
  232 + for (final Partition child in children) {
  233 + _context.partitionContext[index] = child.saveContext();
  234 + index++;
  235 + }
  236 + return _context;
  237 + }
  238 +}
@@ -35,6 +35,7 @@ import 'widget_container_test.dart' as widget_container; @@ -35,6 +35,7 @@ import 'widget_container_test.dart' as widget_container;
35 import 'widget_flex_test.dart' as widget_flex; 35 import 'widget_flex_test.dart' as widget_flex;
36 import 'widget_grid_view_test.dart' as widget_grid_view; 36 import 'widget_grid_view_test.dart' as widget_grid_view;
37 import 'widget_multipage_test.dart' as widget_multipage; 37 import 'widget_multipage_test.dart' as widget_multipage;
  38 +import 'widget_partitions_test.dart' as widget_partitions;
38 import 'widget_table_test.dart' as widget_table; 39 import 'widget_table_test.dart' as widget_table;
39 import 'widget_test.dart' as widget; 40 import 'widget_test.dart' as widget;
40 import 'widget_text_test.dart' as widget_text; 41 import 'widget_text_test.dart' as widget_text;
@@ -62,6 +63,7 @@ void main() { @@ -62,6 +63,7 @@ void main() {
62 widget_flex.main(); 63 widget_flex.main();
63 widget_grid_view.main(); 64 widget_grid_view.main();
64 widget_multipage.main(); 65 widget_multipage.main();
  66 + widget_partitions.main();
65 widget_table.main(); 67 widget_table.main();
66 widget_text.main(); 68 widget_text.main();
67 widget_theme.main(); 69 widget_theme.main();
  1 +/*
  2 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +// ignore_for_file: omit_local_variable_types
  18 +
  19 +import 'dart:io';
  20 +
  21 +import 'package:pdf/widgets.dart';
  22 +import 'package:test/test.dart';
  23 +
  24 +Document pdf;
  25 +
  26 +void main() {
  27 + setUpAll(() {
  28 + Document.debug = true;
  29 + pdf = Document();
  30 + });
  31 +
  32 + test('Partitions Widget', () {
  33 + pdf.addPage(
  34 + MultiPage(
  35 + build: (Context context) => <Widget>[
  36 + Partitions(
  37 + children: <Partition>[
  38 + Partition(
  39 + flex: 1618,
  40 + child: Column(
  41 + children: List<Widget>.generate(100, (int i) => Text('$i')),
  42 + ),
  43 + ),
  44 + Partition(
  45 + flex: 1000,
  46 + // width: 100,
  47 + child: Column(
  48 + mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  49 + children: List<Widget>.generate(20, (int i) => Text('$i')),
  50 + ),
  51 + ),
  52 + ],
  53 + ),
  54 + ],
  55 + ),
  56 + );
  57 + });
  58 +
  59 + tearDownAll(() {
  60 + final File file = File('widgets-partitions.pdf');
  61 + file.writeAsBytesSync(pdf.save());
  62 + });
  63 +}
No preview for this file type