Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
dart_pdf
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
David PHAM-VAN
2021-03-14 09:46:26 -0300
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
a62bbce11814cd2cc761139260f884de9022d5f2
a62bbce1
1 parent
9d98db64
Add PieChart
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
347 additions
and
82 deletions
demo/analysis_options.yaml
demo/lib/report.dart
pdf/CHANGELOG.md
pdf/lib/src/widgets/chart/legend.dart
pdf/lib/src/widgets/chart/pie_chart.dart
pdf/lib/widgets.dart
pdf/pubspec.yaml
demo/analysis_options.yaml
View file @
a62bbce
include
:
package:pedantic/analysis_options.yaml
analyzer
:
strong-mode
:
implicit-dynamic
:
false
errors
:
missing_required_param
:
warning
missing_return
:
warning
linter
:
rules
:
-
always_put_control_body_on_new_line
-
avoid_as
-
avoid_bool_literals_in_conditional_expressions
-
avoid_classes_with_only_static_members
-
avoid_field_initializers_in_const_classes
-
avoid_function_literals_in_foreach_calls
-
avoid_renaming_method_parameters
-
avoid_returning_null_for_void
-
avoid_slow_async_io
-
avoid_unused_constructor_parameters
-
avoid_void_async
-
await_only_futures
-
camel_case_types
-
cancel_subscriptions
-
control_flow_in_finally
-
directives_ordering
-
empty_statements
-
flutter_style_todos
-
hash_and_equals
-
implementation_imports
-
iterable_contains_unrelated_type
-
list_remove_unrelated_type
-
no_adjacent_strings_in_list
-
non_constant_identifier_names
-
omit_local_variable_types
-
overridden_fields
-
package_api_docs
-
package_names
-
package_prefixed_library_names
-
prefer_asserts_in_initializer_lists
-
prefer_const_constructors
-
prefer_const_constructors_in_immutables
-
prefer_const_declarations
-
prefer_const_literals_to_create_immutables
-
prefer_final_locals
-
prefer_foreach
-
prefer_if_elements_to_conditional_expressions
-
prefer_initializing_formals
-
prefer_inlined_adds
-
prefer_typing_uninitialized_variables
-
prefer_void_to_null
-
sort_constructors_first
-
sort_pub_dependencies
-
sort_unnamed_constructors_first
-
test_types_in_equals
-
throw_in_finally
-
unnecessary_brace_in_string_interps
-
unnecessary_getters_setters
-
unnecessary_null_aware_assignments
-
unnecessary_overrides
-
unnecessary_parenthesis
-
unnecessary_statements
-
use_full_hex_values_for_flutter_colors
...
...
demo/lib/report.dart
View file @
a62bbce
...
...
@@ -25,29 +25,40 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async {
const
tableHeaders
=
[
'Category'
,
'Budget'
,
'Expense'
,
'Result'
];
const
dataTable
=
[
[
'Phone'
,
80
,
95
,
-
15
],
[
'Internet'
,
250
,
230
,
20
],
[
'Electricity'
,
300
,
375
,
-
75
],
[
'Movies'
,
85
,
80
,
5
],
[
'Food'
,
300
,
350
,
-
50
],
[
'Fuel'
,
650
,
550
,
100
],
[
'Insurance'
,
250
,
310
,
-
60
],
[
'Phone'
,
80
,
95
],
[
'Internet'
,
250
,
230
],
[
'Electricity'
,
300
,
375
],
[
'Movies'
,
85
,
80
],
[
'Food'
,
300
,
350
],
[
'Fuel'
,
650
,
550
],
[
'Insurance'
,
250
,
310
],
];
// Some summary maths
final
budget
=
dataTable
.
map
((
e
)
=>
e
[
1
]
as
num
)
.
reduce
((
value
,
element
)
=>
value
+
element
);
final
expense
=
dataTable
.
map
((
e
)
=>
e
[
2
]
as
num
)
.
reduce
((
value
,
element
)
=>
value
+
element
);
final
baseColor
=
PdfColors
.
cyan
;
// Create a PDF document.
final
document
=
pw
.
Document
();
final
theme
=
pw
.
ThemeData
.
withFont
(
base:
pw
.
Font
.
ttf
(
await
rootBundle
.
load
(
'assets/open-sans.ttf'
)),
bold:
pw
.
Font
.
ttf
(
await
rootBundle
.
load
(
'assets/open-sans-bold.ttf'
)),
);
// Add page to the PDF
document
.
addPage
(
pw
.
Page
(
pageFormat:
pageFormat
,
theme:
pw
.
ThemeData
.
withFont
(
base:
pw
.
Font
.
ttf
(
await
rootBundle
.
load
(
'assets/open-sans.ttf'
)),
bold:
pw
.
Font
.
ttf
(
await
rootBundle
.
load
(
'assets/open-sans-bold.ttf'
)),
),
theme:
theme
,
build:
(
context
)
{
// Top bar chart
final
chart1
=
pw
.
Chart
(
left:
pw
.
Container
(
alignment:
pw
.
Alignment
.
topCenter
,
...
...
@@ -117,6 +128,7 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async {
],
);
// Left curved line chart
final
chart2
=
pw
.
Chart
(
grid:
pw
.
CartesianGrid
(
xAxis:
pw
.
FixedAxis
([
0
,
1
,
2
,
3
,
4
,
5
,
6
]),
...
...
@@ -143,10 +155,19 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async {
],
);
// Data table
final
table
=
pw
.
Table
.
fromTextArray
(
border:
null
,
headers:
tableHeaders
,
data:
dataTable
,
data:
List
<
List
<
dynamic
>>.
generate
(
dataTable
.
length
,
(
index
)
=>
<
dynamic
>[
dataTable
[
index
][
0
],
dataTable
[
index
][
1
],
dataTable
[
index
][
2
],
(
dataTable
[
index
][
1
]
as
num
)
-
(
dataTable
[
index
][
2
]
as
num
),
],
),
headerStyle:
pw
.
TextStyle
(
color:
PdfColors
.
white
,
fontWeight:
pw
.
FontWeight
.
bold
,
...
...
@@ -162,8 +183,11 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async {
),
),
),
cellAlignment:
pw
.
Alignment
.
centerRight
,
cellAlignments:
{
0
:
pw
.
Alignment
.
centerLeft
},
);
// Page layout
return
pw
.
Column
(
children:
[
pw
.
Text
(
'Budget Report'
,
...
...
@@ -223,7 +247,7 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async {
),
),
pw
.
Text
(
'Budget was originally
\$
1915. A total of
\$
1990 was spent on the month of January which exceeded the overall budget by
\$
75
'
,
'Budget was originally
\$
$budget
. A total of
\$
$expense
was spent on the month of January which exceeded the overall budget by
\$
${expense - budget}
'
,
textAlign:
pw
.
TextAlign
.
justify
,
)
],
...
...
@@ -237,6 +261,55 @@ Future<Uint8List> generateReport(PdfPageFormat pageFormat) async {
),
);
// Second page with a pie chart
document
.
addPage
(
pw
.
Page
(
pageFormat:
pageFormat
,
theme:
theme
,
build:
(
context
)
{
const
chartColors
=
[
PdfColors
.
blue300
,
PdfColors
.
green300
,
PdfColors
.
amber300
,
PdfColors
.
pink300
,
PdfColors
.
cyan300
,
PdfColors
.
purple300
,
PdfColors
.
lime300
,
];
return
pw
.
SizedBox
(
height:
400
,
child:
pw
.
Chart
(
title:
pw
.
Text
(
'Expense breakdown'
,
style:
pw
.
TextStyle
(
color:
baseColor
,
fontSize:
20
,
),
),
grid:
pw
.
PieGrid
(),
datasets:
List
<
pw
.
Dataset
>.
generate
(
dataTable
.
length
,
(
index
)
{
final
data
=
dataTable
[
index
];
final
color
=
chartColors
[
index
%
chartColors
.
length
];
final
textColor
=
color
.
luminance
<
0.2
?
PdfColors
.
white
:
PdfColors
.
black
;
final
value
=
(
data
[
2
]
as
num
).
toDouble
();
final
pct
=
(
value
/
expense
*
100
).
round
();
return
pw
.
PieDataSet
(
legend:
'
${data[0]}
\n
$pct
%'
,
value:
value
,
color:
color
,
legendStyle:
pw
.
TextStyle
(
fontSize:
10
,
color:
textColor
),
);
}),
),
);
},
),
);
// Return the PDF file content
return
document
.
save
();
}
...
...
pdf/CHANGELOG.md
View file @
a62bbce
# Changelog
## 3.
0.2
## 3.
1.0
-
Fix some linting issues
-
Add PdfPage.rotate attribute
-
Add RadialGrid for charts with polar coordinates
-
Add PieChart
## 3.0.1
...
...
pdf/lib/src/widgets/chart/legend.dart
View file @
a62bbce
...
...
@@ -35,6 +35,7 @@ class ChartLegend extends StatelessWidget {
this
.
direction
=
Axis
.
vertical
,
this
.
decoration
,
this
.
padding
=
const
EdgeInsets
.
all
(
5
),
this
.
maxWidth
=
200
,
});
final
TextStyle
?
textStyle
;
...
...
@@ -47,6 +48,8 @@ class ChartLegend extends StatelessWidget {
final
EdgeInsets
padding
;
final
double
maxWidth
;
Widget
_buildLegend
(
Context
context
,
Dataset
dataset
)
{
final
style
=
Theme
.
of
(
context
).
defaultTextStyle
.
merge
(
textStyle
);
...
...
@@ -59,10 +62,14 @@ class ChartLegend extends StatelessWidget {
margin:
const
EdgeInsets
.
only
(
right:
5
),
child:
dataset
.
legendShape
(),
),
Text
(
dataset
.
legend
!,
style:
textStyle
,
)
ConstrainedBox
(
constraints:
BoxConstraints
(
maxWidth:
maxWidth
),
child:
Text
(
dataset
.
legend
!,
style:
textStyle
,
softWrap:
false
,
),
),
],
);
}
...
...
pdf/lib/src/widgets/chart/pie_chart.dart
0 → 100644
View file @
a62bbce
// ignore_for_file: public_member_api_docs
import
'dart:math'
;
import
'package:pdf/pdf.dart'
;
import
'package:pdf/widgets.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
class
PieGrid
extends
ChartGrid
{
PieGrid
();
late
PdfRect
gridBox
;
late
double
total
;
late
double
unit
;
late
double
pieSize
;
@override
void
layout
(
Context
context
,
BoxConstraints
constraints
,
{
bool
parentUsesSize
=
false
})
{
super
.
layout
(
context
,
constraints
,
parentUsesSize:
parentUsesSize
);
final
datasets
=
Chart
.
of
(
context
).
datasets
;
final
size
=
constraints
.
biggest
;
gridBox
=
PdfRect
(
0
,
0
,
size
.
x
,
size
.
y
);
total
=
0.0
;
for
(
final
dataset
in
datasets
)
{
assert
(
dataset
is
PieDataSet
,
'Use only PieDataSet with a PieGrid'
);
if
(
dataset
is
PieDataSet
)
{
total
+=
dataset
.
value
;
}
}
unit
=
pi
/
total
*
2
;
var
angle
=
0.0
;
for
(
final
dataset
in
datasets
)
{
if
(
dataset
is
PieDataSet
)
{
dataset
.
angleStart
=
angle
;
angle
+=
dataset
.
value
*
unit
;
dataset
.
angleEnd
=
angle
;
}
}
pieSize
=
min
(
gridBox
.
width
/
2
,
gridBox
.
height
/
2
);
var
reduce
=
false
;
do
{
reduce
=
false
;
for
(
final
dataset
in
datasets
)
{
if
(
dataset
is
PieDataSet
)
{
dataset
.
layout
(
context
,
BoxConstraints
.
tight
(
gridBox
.
size
));
assert
(
dataset
.
box
!=
null
);
if
(
pieSize
>
20
&&
(
dataset
.
box
!.
width
>
gridBox
.
width
||
dataset
.
box
!.
height
>
gridBox
.
height
))
{
pieSize
-=
10
;
reduce
=
true
;
break
;
}
}
}
}
while
(
reduce
);
}
@override
PdfPoint
toChart
(
PdfPoint
p
)
{
return
p
;
}
void
clip
(
Context
context
,
PdfPoint
size
)
{}
@override
void
paint
(
Context
context
)
{
super
.
paint
(
context
);
final
datasets
=
Chart
.
of
(
context
).
datasets
;
context
.
canvas
..
saveContext
()
..
setTransform
(
Matrix4
.
translationValues
(
box
!.
width
/
2
,
box
!.
height
/
2
,
0
),
);
for
(
var
dataSet
in
datasets
)
{
if
(
dataSet
is
PieDataSet
)
{
dataSet
.
paintBackground
(
context
);
}
}
for
(
var
dataSet
in
datasets
)
{
if
(
dataSet
is
PieDataSet
)
{
dataSet
.
paint
(
context
);
}
}
for
(
var
dataSet
in
datasets
)
{
if
(
dataSet
is
PieDataSet
)
{
dataSet
.
paintLegend
(
context
);
}
}
context
.
canvas
.
restoreContext
();
}
}
enum
PieLegendPosition
{
none
,
auto
,
inside
}
class
PieDataSet
extends
Dataset
{
PieDataSet
({
required
this
.
value
,
String
?
legend
,
required
PdfColor
color
,
this
.
borderColor
=
PdfColors
.
white
,
this
.
borderWidth
=
1.5
,
bool
?
drawBorder
,
this
.
drawSurface
=
true
,
this
.
surfaceOpacity
=
1
,
this
.
offset
=
0
,
this
.
legendStyle
,
this
.
legendPosition
=
PieLegendPosition
.
auto
,
})
:
drawBorder
=
drawBorder
??
borderColor
!=
null
&&
color
!=
borderColor
,
assert
((
drawBorder
??
borderColor
!=
null
&&
color
!=
borderColor
)
||
drawSurface
),
super
(
legend:
legend
,
color:
color
,
);
final
double
value
;
late
double
angleStart
;
late
double
angleEnd
;
final
bool
drawBorder
;
final
PdfColor
?
borderColor
;
final
double
borderWidth
;
final
bool
drawSurface
;
final
double
surfaceOpacity
;
final
double
offset
;
final
TextStyle
?
legendStyle
;
final
PieLegendPosition
legendPosition
;
@override
void
layout
(
Context
context
,
BoxConstraints
constraints
,
{
bool
parentUsesSize
=
false
})
{
// final size = constraints.biggest;
// ignore: avoid_as
final
grid
=
Chart
.
of
(
context
).
grid
as
PieGrid
;
final
len
=
grid
.
pieSize
+
offset
;
box
=
PdfRect
(-
len
,
-
len
,
len
*
2
,
len
*
2
);
}
void
_shape
(
Context
context
)
{
// ignore: avoid_as
final
grid
=
Chart
.
of
(
context
).
grid
as
PieGrid
;
final
bisect
=
(
angleStart
+
angleEnd
)
/
2
;
final
cx
=
sin
(
bisect
)
*
offset
;
final
cy
=
cos
(
bisect
)
*
offset
;
final
sx
=
cx
+
sin
(
angleStart
)
*
grid
.
pieSize
;
final
sy
=
cy
+
cos
(
angleStart
)
*
grid
.
pieSize
;
final
ex
=
cx
+
sin
(
angleEnd
)
*
grid
.
pieSize
;
final
ey
=
cy
+
cos
(
angleEnd
)
*
grid
.
pieSize
;
context
.
canvas
..
moveTo
(
cx
,
cy
)
..
lineTo
(
sx
,
sy
)
..
bezierArc
(
sx
,
sy
,
grid
.
pieSize
,
grid
.
pieSize
,
ex
,
ey
,
large:
angleEnd
-
angleStart
>
pi
);
}
@override
void
paintBackground
(
Context
context
)
{
super
.
paint
(
context
);
if
(
drawSurface
)
{
_shape
(
context
);
if
(
surfaceOpacity
!=
1
)
{
context
.
canvas
..
saveContext
()
..
setGraphicState
(
PdfGraphicState
(
opacity:
surfaceOpacity
),
);
}
context
.
canvas
..
setFillColor
(
color
)
..
fillPath
();
if
(
surfaceOpacity
!=
1
)
{
context
.
canvas
.
restoreContext
();
}
}
}
@override
void
paint
(
Context
context
)
{
super
.
paint
(
context
);
if
(
drawBorder
)
{
_shape
(
context
);
context
.
canvas
..
setLineWidth
(
borderWidth
)
..
setLineJoin
(
PdfLineJoin
.
round
)
..
setStrokeColor
(
borderColor
??
color
)
..
strokePath
(
close:
true
);
}
}
void
paintLegend
(
Context
context
)
{
if
(
legendPosition
!=
PieLegendPosition
.
none
&&
legend
!=
null
)
{
// ignore: avoid_as
final
grid
=
Chart
.
of
(
context
).
grid
as
PieGrid
;
final
bisect
=
(
angleStart
+
angleEnd
)
/
2
;
final
o
=
grid
.
pieSize
*
2
/
3
;
final
cx
=
sin
(
bisect
)
*
(
offset
+
o
);
final
cy
=
cos
(
bisect
)
*
(
offset
+
o
);
Widget
.
draw
(
Text
(
legend
!,
style:
legendStyle
,
textAlign:
TextAlign
.
center
),
offset:
PdfPoint
(
cx
,
cy
),
context:
context
,
alignment:
Alignment
.
center
,
constraints:
const
BoxConstraints
(
maxWidth:
200
,
maxHeight:
200
),
);
}
}
}
...
...
pdf/lib/widgets.dart
View file @
a62bbce
...
...
@@ -28,6 +28,7 @@ export 'src/widgets/chart/grid_cartesian.dart';
export
'src/widgets/chart/grid_radial.dart'
;
export
'src/widgets/chart/legend.dart'
;
export
'src/widgets/chart/line_chart.dart'
;
export
'src/widgets/chart/pie_chart.dart'
;
export
'src/widgets/clip.dart'
;
export
'src/widgets/container.dart'
;
export
'src/widgets/content.dart'
;
...
...
pdf/pubspec.yaml
View file @
a62bbce
...
...
@@ -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
:
3.
0.2
version
:
3.
1.0
environment
:
sdk
:
"
>=2.12.0-0
<3.0.0"
...
...
Please
register
or
login
to post a comment