Showing
7 changed files
with
347 additions
and
82 deletions
1 | include: package:pedantic/analysis_options.yaml | 1 | include: package:pedantic/analysis_options.yaml |
2 | - | ||
3 | -analyzer: | ||
4 | - strong-mode: | ||
5 | - implicit-dynamic: false | ||
6 | - errors: | ||
7 | - missing_required_param: warning | ||
8 | - missing_return: warning | ||
9 | - | ||
10 | -linter: | ||
11 | - rules: | ||
12 | - - always_put_control_body_on_new_line | ||
13 | - - avoid_as | ||
14 | - - avoid_bool_literals_in_conditional_expressions | ||
15 | - - avoid_classes_with_only_static_members | ||
16 | - - avoid_field_initializers_in_const_classes | ||
17 | - - avoid_function_literals_in_foreach_calls | ||
18 | - - avoid_renaming_method_parameters | ||
19 | - - avoid_returning_null_for_void | ||
20 | - - avoid_slow_async_io | ||
21 | - - avoid_unused_constructor_parameters | ||
22 | - - avoid_void_async | ||
23 | - - await_only_futures | ||
24 | - - camel_case_types | ||
25 | - - cancel_subscriptions | ||
26 | - - control_flow_in_finally | ||
27 | - - directives_ordering | ||
28 | - - empty_statements | ||
29 | - - flutter_style_todos | ||
30 | - - hash_and_equals | ||
31 | - - implementation_imports | ||
32 | - - iterable_contains_unrelated_type | ||
33 | - - list_remove_unrelated_type | ||
34 | - - no_adjacent_strings_in_list | ||
35 | - - non_constant_identifier_names | ||
36 | - - omit_local_variable_types | ||
37 | - - overridden_fields | ||
38 | - - package_api_docs | ||
39 | - - package_names | ||
40 | - - package_prefixed_library_names | ||
41 | - - prefer_asserts_in_initializer_lists | ||
42 | - - prefer_const_constructors | ||
43 | - - prefer_const_constructors_in_immutables | ||
44 | - - prefer_const_declarations | ||
45 | - - prefer_const_literals_to_create_immutables | ||
46 | - - prefer_final_locals | ||
47 | - - prefer_foreach | ||
48 | - - prefer_if_elements_to_conditional_expressions | ||
49 | - - prefer_initializing_formals | ||
50 | - - prefer_inlined_adds | ||
51 | - - prefer_typing_uninitialized_variables | ||
52 | - - prefer_void_to_null | ||
53 | - - sort_constructors_first | ||
54 | - - sort_pub_dependencies | ||
55 | - - sort_unnamed_constructors_first | ||
56 | - - test_types_in_equals | ||
57 | - - throw_in_finally | ||
58 | - - unnecessary_brace_in_string_interps | ||
59 | - - unnecessary_getters_setters | ||
60 | - - unnecessary_null_aware_assignments | ||
61 | - - unnecessary_overrides | ||
62 | - - unnecessary_parenthesis | ||
63 | - - unnecessary_statements | ||
64 | - - use_full_hex_values_for_flutter_colors |
@@ -25,29 +25,40 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | @@ -25,29 +25,40 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | ||
25 | const tableHeaders = ['Category', 'Budget', 'Expense', 'Result']; | 25 | const tableHeaders = ['Category', 'Budget', 'Expense', 'Result']; |
26 | 26 | ||
27 | const dataTable = [ | 27 | const dataTable = [ |
28 | - ['Phone', 80, 95, -15], | ||
29 | - ['Internet', 250, 230, 20], | ||
30 | - ['Electricity', 300, 375, -75], | ||
31 | - ['Movies', 85, 80, 5], | ||
32 | - ['Food', 300, 350, -50], | ||
33 | - ['Fuel', 650, 550, 100], | ||
34 | - ['Insurance', 250, 310, -60], | 28 | + ['Phone', 80, 95], |
29 | + ['Internet', 250, 230], | ||
30 | + ['Electricity', 300, 375], | ||
31 | + ['Movies', 85, 80], | ||
32 | + ['Food', 300, 350], | ||
33 | + ['Fuel', 650, 550], | ||
34 | + ['Insurance', 250, 310], | ||
35 | ]; | 35 | ]; |
36 | 36 | ||
37 | + // Some summary maths | ||
38 | + final budget = dataTable | ||
39 | + .map((e) => e[1] as num) | ||
40 | + .reduce((value, element) => value + element); | ||
41 | + final expense = dataTable | ||
42 | + .map((e) => e[2] as num) | ||
43 | + .reduce((value, element) => value + element); | ||
44 | + | ||
37 | final baseColor = PdfColors.cyan; | 45 | final baseColor = PdfColors.cyan; |
38 | 46 | ||
39 | // Create a PDF document. | 47 | // Create a PDF document. |
40 | final document = pw.Document(); | 48 | final document = pw.Document(); |
41 | 49 | ||
50 | + final theme = pw.ThemeData.withFont( | ||
51 | + base: pw.Font.ttf(await rootBundle.load('assets/open-sans.ttf')), | ||
52 | + bold: pw.Font.ttf(await rootBundle.load('assets/open-sans-bold.ttf')), | ||
53 | + ); | ||
54 | + | ||
42 | // Add page to the PDF | 55 | // Add page to the PDF |
43 | document.addPage( | 56 | document.addPage( |
44 | pw.Page( | 57 | pw.Page( |
45 | pageFormat: pageFormat, | 58 | pageFormat: pageFormat, |
46 | - theme: pw.ThemeData.withFont( | ||
47 | - base: pw.Font.ttf(await rootBundle.load('assets/open-sans.ttf')), | ||
48 | - bold: pw.Font.ttf(await rootBundle.load('assets/open-sans-bold.ttf')), | ||
49 | - ), | 59 | + theme: theme, |
50 | build: (context) { | 60 | build: (context) { |
61 | + // Top bar chart | ||
51 | final chart1 = pw.Chart( | 62 | final chart1 = pw.Chart( |
52 | left: pw.Container( | 63 | left: pw.Container( |
53 | alignment: pw.Alignment.topCenter, | 64 | alignment: pw.Alignment.topCenter, |
@@ -117,6 +128,7 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | @@ -117,6 +128,7 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | ||
117 | ], | 128 | ], |
118 | ); | 129 | ); |
119 | 130 | ||
131 | + // Left curved line chart | ||
120 | final chart2 = pw.Chart( | 132 | final chart2 = pw.Chart( |
121 | grid: pw.CartesianGrid( | 133 | grid: pw.CartesianGrid( |
122 | xAxis: pw.FixedAxis([0, 1, 2, 3, 4, 5, 6]), | 134 | xAxis: pw.FixedAxis([0, 1, 2, 3, 4, 5, 6]), |
@@ -143,10 +155,19 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | @@ -143,10 +155,19 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | ||
143 | ], | 155 | ], |
144 | ); | 156 | ); |
145 | 157 | ||
158 | + // Data table | ||
146 | final table = pw.Table.fromTextArray( | 159 | final table = pw.Table.fromTextArray( |
147 | border: null, | 160 | border: null, |
148 | headers: tableHeaders, | 161 | headers: tableHeaders, |
149 | - data: dataTable, | 162 | + data: List<List<dynamic>>.generate( |
163 | + dataTable.length, | ||
164 | + (index) => <dynamic>[ | ||
165 | + dataTable[index][0], | ||
166 | + dataTable[index][1], | ||
167 | + dataTable[index][2], | ||
168 | + (dataTable[index][1] as num) - (dataTable[index][2] as num), | ||
169 | + ], | ||
170 | + ), | ||
150 | headerStyle: pw.TextStyle( | 171 | headerStyle: pw.TextStyle( |
151 | color: PdfColors.white, | 172 | color: PdfColors.white, |
152 | fontWeight: pw.FontWeight.bold, | 173 | fontWeight: pw.FontWeight.bold, |
@@ -162,8 +183,11 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | @@ -162,8 +183,11 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | ||
162 | ), | 183 | ), |
163 | ), | 184 | ), |
164 | ), | 185 | ), |
186 | + cellAlignment: pw.Alignment.centerRight, | ||
187 | + cellAlignments: {0: pw.Alignment.centerLeft}, | ||
165 | ); | 188 | ); |
166 | 189 | ||
190 | + // Page layout | ||
167 | return pw.Column( | 191 | return pw.Column( |
168 | children: [ | 192 | children: [ |
169 | pw.Text('Budget Report', | 193 | pw.Text('Budget Report', |
@@ -223,7 +247,7 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | @@ -223,7 +247,7 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | ||
223 | ), | 247 | ), |
224 | ), | 248 | ), |
225 | pw.Text( | 249 | pw.Text( |
226 | - 'Budget was originally \$1915. A total of \$1990 was spent on the month of January which exceeded the overall budget by \$75', | 250 | + 'Budget was originally \$$budget. A total of \$$expense was spent on the month of January which exceeded the overall budget by \$${expense - budget}', |
227 | textAlign: pw.TextAlign.justify, | 251 | textAlign: pw.TextAlign.justify, |
228 | ) | 252 | ) |
229 | ], | 253 | ], |
@@ -237,6 +261,55 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | @@ -237,6 +261,55 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | ||
237 | ), | 261 | ), |
238 | ); | 262 | ); |
239 | 263 | ||
264 | + // Second page with a pie chart | ||
265 | + document.addPage( | ||
266 | + pw.Page( | ||
267 | + pageFormat: pageFormat, | ||
268 | + theme: theme, | ||
269 | + build: (context) { | ||
270 | + const chartColors = [ | ||
271 | + PdfColors.blue300, | ||
272 | + PdfColors.green300, | ||
273 | + PdfColors.amber300, | ||
274 | + PdfColors.pink300, | ||
275 | + PdfColors.cyan300, | ||
276 | + PdfColors.purple300, | ||
277 | + PdfColors.lime300, | ||
278 | + ]; | ||
279 | + | ||
280 | + return pw.SizedBox( | ||
281 | + height: 400, | ||
282 | + child: pw.Chart( | ||
283 | + title: pw.Text( | ||
284 | + 'Expense breakdown', | ||
285 | + style: pw.TextStyle( | ||
286 | + color: baseColor, | ||
287 | + fontSize: 20, | ||
288 | + ), | ||
289 | + ), | ||
290 | + grid: pw.PieGrid(), | ||
291 | + datasets: List<pw.Dataset>.generate(dataTable.length, (index) { | ||
292 | + final data = dataTable[index]; | ||
293 | + final color = chartColors[index % chartColors.length]; | ||
294 | + final textColor = | ||
295 | + color.luminance < 0.2 ? PdfColors.white : PdfColors.black; | ||
296 | + | ||
297 | + final value = (data[2] as num).toDouble(); | ||
298 | + final pct = (value / expense * 100).round(); | ||
299 | + | ||
300 | + return pw.PieDataSet( | ||
301 | + legend: '${data[0]}\n$pct%', | ||
302 | + value: value, | ||
303 | + color: color, | ||
304 | + legendStyle: pw.TextStyle(fontSize: 10, color: textColor), | ||
305 | + ); | ||
306 | + }), | ||
307 | + ), | ||
308 | + ); | ||
309 | + }, | ||
310 | + ), | ||
311 | + ); | ||
312 | + | ||
240 | // Return the PDF file content | 313 | // Return the PDF file content |
241 | return document.save(); | 314 | return document.save(); |
242 | } | 315 | } |
1 | # Changelog | 1 | # Changelog |
2 | 2 | ||
3 | -## 3.0.2 | 3 | +## 3.1.0 |
4 | 4 | ||
5 | - Fix some linting issues | 5 | - Fix some linting issues |
6 | - Add PdfPage.rotate attribute | 6 | - Add PdfPage.rotate attribute |
7 | - Add RadialGrid for charts with polar coordinates | 7 | - Add RadialGrid for charts with polar coordinates |
8 | +- Add PieChart | ||
8 | 9 | ||
9 | ## 3.0.1 | 10 | ## 3.0.1 |
10 | 11 |
@@ -35,6 +35,7 @@ class ChartLegend extends StatelessWidget { | @@ -35,6 +35,7 @@ class ChartLegend extends StatelessWidget { | ||
35 | this.direction = Axis.vertical, | 35 | this.direction = Axis.vertical, |
36 | this.decoration, | 36 | this.decoration, |
37 | this.padding = const EdgeInsets.all(5), | 37 | this.padding = const EdgeInsets.all(5), |
38 | + this.maxWidth = 200, | ||
38 | }); | 39 | }); |
39 | 40 | ||
40 | final TextStyle? textStyle; | 41 | final TextStyle? textStyle; |
@@ -47,6 +48,8 @@ class ChartLegend extends StatelessWidget { | @@ -47,6 +48,8 @@ class ChartLegend extends StatelessWidget { | ||
47 | 48 | ||
48 | final EdgeInsets padding; | 49 | final EdgeInsets padding; |
49 | 50 | ||
51 | + final double maxWidth; | ||
52 | + | ||
50 | Widget _buildLegend(Context context, Dataset dataset) { | 53 | Widget _buildLegend(Context context, Dataset dataset) { |
51 | final style = Theme.of(context).defaultTextStyle.merge(textStyle); | 54 | final style = Theme.of(context).defaultTextStyle.merge(textStyle); |
52 | 55 | ||
@@ -59,10 +62,14 @@ class ChartLegend extends StatelessWidget { | @@ -59,10 +62,14 @@ class ChartLegend extends StatelessWidget { | ||
59 | margin: const EdgeInsets.only(right: 5), | 62 | margin: const EdgeInsets.only(right: 5), |
60 | child: dataset.legendShape(), | 63 | child: dataset.legendShape(), |
61 | ), | 64 | ), |
62 | - Text( | ||
63 | - dataset.legend!, | ||
64 | - style: textStyle, | ||
65 | - ) | 65 | + ConstrainedBox( |
66 | + constraints: BoxConstraints(maxWidth: maxWidth), | ||
67 | + child: Text( | ||
68 | + dataset.legend!, | ||
69 | + style: textStyle, | ||
70 | + softWrap: false, | ||
71 | + ), | ||
72 | + ), | ||
66 | ], | 73 | ], |
67 | ); | 74 | ); |
68 | } | 75 | } |
pdf/lib/src/widgets/chart/pie_chart.dart
0 → 100644
1 | +// ignore_for_file: public_member_api_docs | ||
2 | + | ||
3 | +import 'dart:math'; | ||
4 | + | ||
5 | +import 'package:pdf/pdf.dart'; | ||
6 | +import 'package:pdf/widgets.dart'; | ||
7 | +import 'package:vector_math/vector_math_64.dart'; | ||
8 | + | ||
9 | +class PieGrid extends ChartGrid { | ||
10 | + PieGrid(); | ||
11 | + | ||
12 | + late PdfRect gridBox; | ||
13 | + | ||
14 | + late double total; | ||
15 | + | ||
16 | + late double unit; | ||
17 | + | ||
18 | + late double pieSize; | ||
19 | + | ||
20 | + @override | ||
21 | + void layout(Context context, BoxConstraints constraints, | ||
22 | + {bool parentUsesSize = false}) { | ||
23 | + super.layout(context, constraints, parentUsesSize: parentUsesSize); | ||
24 | + | ||
25 | + final datasets = Chart.of(context).datasets; | ||
26 | + final size = constraints.biggest; | ||
27 | + | ||
28 | + gridBox = PdfRect(0, 0, size.x, size.y); | ||
29 | + | ||
30 | + total = 0.0; | ||
31 | + | ||
32 | + for (final dataset in datasets) { | ||
33 | + assert(dataset is PieDataSet, 'Use only PieDataSet with a PieGrid'); | ||
34 | + if (dataset is PieDataSet) { | ||
35 | + total += dataset.value; | ||
36 | + } | ||
37 | + } | ||
38 | + | ||
39 | + unit = pi / total * 2; | ||
40 | + var angle = 0.0; | ||
41 | + | ||
42 | + for (final dataset in datasets) { | ||
43 | + if (dataset is PieDataSet) { | ||
44 | + dataset.angleStart = angle; | ||
45 | + angle += dataset.value * unit; | ||
46 | + dataset.angleEnd = angle; | ||
47 | + } | ||
48 | + } | ||
49 | + | ||
50 | + pieSize = min(gridBox.width / 2, gridBox.height / 2); | ||
51 | + var reduce = false; | ||
52 | + | ||
53 | + do { | ||
54 | + reduce = false; | ||
55 | + for (final dataset in datasets) { | ||
56 | + if (dataset is PieDataSet) { | ||
57 | + dataset.layout(context, BoxConstraints.tight(gridBox.size)); | ||
58 | + assert(dataset.box != null); | ||
59 | + if (pieSize > 20 && | ||
60 | + (dataset.box!.width > gridBox.width || | ||
61 | + dataset.box!.height > gridBox.height)) { | ||
62 | + pieSize -= 10; | ||
63 | + reduce = true; | ||
64 | + break; | ||
65 | + } | ||
66 | + } | ||
67 | + } | ||
68 | + } while (reduce); | ||
69 | + } | ||
70 | + | ||
71 | + @override | ||
72 | + PdfPoint toChart(PdfPoint p) { | ||
73 | + return p; | ||
74 | + } | ||
75 | + | ||
76 | + void clip(Context context, PdfPoint size) {} | ||
77 | + | ||
78 | + @override | ||
79 | + void paint(Context context) { | ||
80 | + super.paint(context); | ||
81 | + | ||
82 | + final datasets = Chart.of(context).datasets; | ||
83 | + | ||
84 | + context.canvas | ||
85 | + ..saveContext() | ||
86 | + ..setTransform( | ||
87 | + Matrix4.translationValues(box!.width / 2, box!.height / 2, 0), | ||
88 | + ); | ||
89 | + | ||
90 | + for (var dataSet in datasets) { | ||
91 | + if (dataSet is PieDataSet) { | ||
92 | + dataSet.paintBackground(context); | ||
93 | + } | ||
94 | + } | ||
95 | + | ||
96 | + for (var dataSet in datasets) { | ||
97 | + if (dataSet is PieDataSet) { | ||
98 | + dataSet.paint(context); | ||
99 | + } | ||
100 | + } | ||
101 | + | ||
102 | + for (var dataSet in datasets) { | ||
103 | + if (dataSet is PieDataSet) { | ||
104 | + dataSet.paintLegend(context); | ||
105 | + } | ||
106 | + } | ||
107 | + | ||
108 | + context.canvas.restoreContext(); | ||
109 | + } | ||
110 | +} | ||
111 | + | ||
112 | +enum PieLegendPosition { none, auto, inside } | ||
113 | + | ||
114 | +class PieDataSet extends Dataset { | ||
115 | + PieDataSet({ | ||
116 | + required this.value, | ||
117 | + String? legend, | ||
118 | + required PdfColor color, | ||
119 | + this.borderColor = PdfColors.white, | ||
120 | + this.borderWidth = 1.5, | ||
121 | + bool? drawBorder, | ||
122 | + this.drawSurface = true, | ||
123 | + this.surfaceOpacity = 1, | ||
124 | + this.offset = 0, | ||
125 | + this.legendStyle, | ||
126 | + this.legendPosition = PieLegendPosition.auto, | ||
127 | + }) : drawBorder = drawBorder ?? borderColor != null && color != borderColor, | ||
128 | + assert((drawBorder ?? borderColor != null && color != borderColor) || | ||
129 | + drawSurface), | ||
130 | + super( | ||
131 | + legend: legend, | ||
132 | + color: color, | ||
133 | + ); | ||
134 | + | ||
135 | + final double value; | ||
136 | + | ||
137 | + late double angleStart; | ||
138 | + | ||
139 | + late double angleEnd; | ||
140 | + | ||
141 | + final bool drawBorder; | ||
142 | + final PdfColor? borderColor; | ||
143 | + final double borderWidth; | ||
144 | + | ||
145 | + final bool drawSurface; | ||
146 | + | ||
147 | + final double surfaceOpacity; | ||
148 | + | ||
149 | + final double offset; | ||
150 | + | ||
151 | + final TextStyle? legendStyle; | ||
152 | + | ||
153 | + final PieLegendPosition legendPosition; | ||
154 | + | ||
155 | + @override | ||
156 | + void layout(Context context, BoxConstraints constraints, | ||
157 | + {bool parentUsesSize = false}) { | ||
158 | + // final size = constraints.biggest; | ||
159 | + | ||
160 | + // ignore: avoid_as | ||
161 | + final grid = Chart.of(context).grid as PieGrid; | ||
162 | + final len = grid.pieSize + offset; | ||
163 | + | ||
164 | + box = PdfRect(-len, -len, len * 2, len * 2); | ||
165 | + } | ||
166 | + | ||
167 | + void _shape(Context context) { | ||
168 | + // ignore: avoid_as | ||
169 | + final grid = Chart.of(context).grid as PieGrid; | ||
170 | + | ||
171 | + final bisect = (angleStart + angleEnd) / 2; | ||
172 | + | ||
173 | + final cx = sin(bisect) * offset; | ||
174 | + final cy = cos(bisect) * offset; | ||
175 | + | ||
176 | + final sx = cx + sin(angleStart) * grid.pieSize; | ||
177 | + final sy = cy + cos(angleStart) * grid.pieSize; | ||
178 | + final ex = cx + sin(angleEnd) * grid.pieSize; | ||
179 | + final ey = cy + cos(angleEnd) * grid.pieSize; | ||
180 | + | ||
181 | + context.canvas | ||
182 | + ..moveTo(cx, cy) | ||
183 | + ..lineTo(sx, sy) | ||
184 | + ..bezierArc(sx, sy, grid.pieSize, grid.pieSize, ex, ey, | ||
185 | + large: angleEnd - angleStart > pi); | ||
186 | + } | ||
187 | + | ||
188 | + @override | ||
189 | + void paintBackground(Context context) { | ||
190 | + super.paint(context); | ||
191 | + | ||
192 | + if (drawSurface) { | ||
193 | + _shape(context); | ||
194 | + if (surfaceOpacity != 1) { | ||
195 | + context.canvas | ||
196 | + ..saveContext() | ||
197 | + ..setGraphicState( | ||
198 | + PdfGraphicState(opacity: surfaceOpacity), | ||
199 | + ); | ||
200 | + } | ||
201 | + | ||
202 | + context.canvas | ||
203 | + ..setFillColor(color) | ||
204 | + ..fillPath(); | ||
205 | + | ||
206 | + if (surfaceOpacity != 1) { | ||
207 | + context.canvas.restoreContext(); | ||
208 | + } | ||
209 | + } | ||
210 | + } | ||
211 | + | ||
212 | + @override | ||
213 | + void paint(Context context) { | ||
214 | + super.paint(context); | ||
215 | + | ||
216 | + if (drawBorder) { | ||
217 | + _shape(context); | ||
218 | + context.canvas | ||
219 | + ..setLineWidth(borderWidth) | ||
220 | + ..setLineJoin(PdfLineJoin.round) | ||
221 | + ..setStrokeColor(borderColor ?? color) | ||
222 | + ..strokePath(close: true); | ||
223 | + } | ||
224 | + } | ||
225 | + | ||
226 | + void paintLegend(Context context) { | ||
227 | + if (legendPosition != PieLegendPosition.none && legend != null) { | ||
228 | + // ignore: avoid_as | ||
229 | + final grid = Chart.of(context).grid as PieGrid; | ||
230 | + | ||
231 | + final bisect = (angleStart + angleEnd) / 2; | ||
232 | + | ||
233 | + final o = grid.pieSize * 2 / 3; | ||
234 | + final cx = sin(bisect) * (offset + o); | ||
235 | + final cy = cos(bisect) * (offset + o); | ||
236 | + | ||
237 | + Widget.draw( | ||
238 | + Text(legend!, style: legendStyle, textAlign: TextAlign.center), | ||
239 | + offset: PdfPoint(cx, cy), | ||
240 | + context: context, | ||
241 | + alignment: Alignment.center, | ||
242 | + constraints: const BoxConstraints(maxWidth: 200, maxHeight: 200), | ||
243 | + ); | ||
244 | + } | ||
245 | + } | ||
246 | +} |
@@ -28,6 +28,7 @@ export 'src/widgets/chart/grid_cartesian.dart'; | @@ -28,6 +28,7 @@ export 'src/widgets/chart/grid_cartesian.dart'; | ||
28 | export 'src/widgets/chart/grid_radial.dart'; | 28 | export 'src/widgets/chart/grid_radial.dart'; |
29 | export 'src/widgets/chart/legend.dart'; | 29 | export 'src/widgets/chart/legend.dart'; |
30 | export 'src/widgets/chart/line_chart.dart'; | 30 | export 'src/widgets/chart/line_chart.dart'; |
31 | +export 'src/widgets/chart/pie_chart.dart'; | ||
31 | export 'src/widgets/clip.dart'; | 32 | export 'src/widgets/clip.dart'; |
32 | export 'src/widgets/container.dart'; | 33 | export 'src/widgets/container.dart'; |
33 | export 'src/widgets/content.dart'; | 34 | export 'src/widgets/content.dart'; |
@@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | ||
4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf | 4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf |
5 | repository: https://github.com/DavBfr/dart_pdf | 5 | repository: https://github.com/DavBfr/dart_pdf |
6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues | 6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues |
7 | -version: 3.0.2 | 7 | +version: 3.1.0 |
8 | 8 | ||
9 | environment: | 9 | environment: |
10 | sdk: ">=2.12.0-0 <3.0.0" | 10 | sdk: ">=2.12.0-0 <3.0.0" |
-
Please register or login to post a comment