Hello I actually figure it out by adding an "image" of the chat i want inside the pdf. You need these packages to accomplish that
flutter_email_sender: ^5.0.2
fl_chart: ^0.46.0
screenshot: ^1.2.3
pdf: ^3.3.0
path_provider: ^2.0.9
permission_handler: ^9.2.0
Some of them are needed for my example below. My example creates a pdf (saves it to the device) and opens the email to sent it to a user.
you are also going to need these permissions in your manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
pdf_page.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'dart:io';
import 'package:flutter_email_sender/flutter_email_sender.dart';
import 'package:screenshot/screenshot.dart';
import 'package:test_project_pdf/chart_for_pdf.dart';
class PdfPage extends StatelessWidget {
const PdfPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: () async {
if (await Permission.storage.isGranted) {
await _pdfResults(context);
} else {
await [Permission.storage].request();
if (await Permission.storage.isGranted) {
await _pdfResults(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("Need permissions"),
));
}
}
},
child: Center(
child: Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(32)),
child: const Text(
"Send e-mail",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18),
)),
),
);
}
Future _pdfResults(context) async {
var pdf = pw.Document();
ScreenshotController screenshotController = ScreenshotController();
final bytes = await screenshotController.captureFromWidget(MediaQuery(data: const MediaQueryData(), child: ChartForPdf()));
pdf.addPage(
pw.MultiPage(
pageFormat: PdfPageFormat.a4,
margin: const pw.EdgeInsets.all(32),
build: (pw.Context context) {
return <pw.Widget>[
pw.Column(
children: [
pw.Center(
child: pw.Container(
height: 700,
width: 1080,
child: pw.Expanded(
child: pw.Image(pw.MemoryImage(bytes)),
),
),
),
],
),
];
},
),
);
await savePdf(pdf, context);
}
Future<String> savePdf(pw.Document pdf, var context) async {
var path;
late File file;
Directory directory;
if (Platform.isAndroid) {
path = (await getExternalStorageDirectory())!.path;
file = File("$path/best_pdf.pdf");
} else if (Platform.isIOS) {
path = await getApplicationDocumentsDirectory();
directory = await Directory("${path.path}/best_pdfs").create();
file = File("${directory.path}/best_pdf.pdf");
}
if (await file.exists()) {
try {
await file.delete();
} on Exception catch (e) {
print(e);
}
}
await file.writeAsBytes(await pdf.save());
await send(file.path, context);
return file.path;
}
Future<void> send(String path, var context) async {
final Email email = Email(
subject: "Mood Chart",
recipients: [""],
attachmentPaths: [path],
isHTML: false,
);
try {
await FlutterEmailSender.send(email);
// platformResponse = 'Επιτυχία';
} on PlatformException catch (error) {
if (error.message == "No email clients found!") {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("Device has no email"),
duration: Duration(seconds: 2)));
}
}
}
}
chart_for_pdf.dart
import 'dart:math';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
class ChartForPdf extends StatelessWidget {
final Color barBackgroundColor = const Color(0xff72d8bf);
final Duration animDuration = const Duration(milliseconds: 250);
int touchedIndex = -1;
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1,
child: Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
color: const Color(0xff81e5cd),
child: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
const Text(
'Mingguan',
style: TextStyle(
color: Color(0xff0f4a3c),
fontSize: 24,
fontWeight: FontWeight.bold),
),
const SizedBox(
height: 4,
),
const Text(
'Grafik konsumsi kalori',
style: TextStyle(
color: Color(0xff379982),
fontSize: 18,
fontWeight: FontWeight.bold),
),
const SizedBox(
height: 38,
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: BarChart( mainBarData(),
swapAnimationDuration: animDuration,
),
),
),
const SizedBox(
height: 12,
),
],
),
),
],
),
),
);
}
BarChartGroupData makeGroupData(
int x,
double y, {
bool isTouched = false,
Color barColor = Colors.white,
double width = 22,
List<int> showTooltips = const [],
}) {
return BarChartGroupData(
x: x,
barRods: [
BarChartRodData(
toY: isTouched ? y + 1 : y,
colors: isTouched ? [Colors.yellow] : [barColor],
width: width,
borderSide: isTouched
? BorderSide(color: Colors.yellow, width: 1)
: const BorderSide(color: Colors.white, width: 0),
backDrawRodData: BackgroundBarChartRodData(
show: true,
toY: 20,
colors: [barBackgroundColor],
),
),
],
showingTooltipIndicators: showTooltips,
);
}
List<BarChartGroupData> showingGroups() => List.generate(7, (i) {
switch (i) {
case 0:
return makeGroupData(0, 5, isTouched: i == touchedIndex);
case 1:
return makeGroupData(1, 6.5, isTouched: i == touchedIndex);
case 2:
return makeGroupData(2, 5, isTouched: i == touchedIndex);
case 3:
return makeGroupData(3, 7.5, isTouched: i == touchedIndex);
case 4:
return makeGroupData(4, 9, isTouched: i == touchedIndex);
case 5:
return makeGroupData(5, 11.5, isTouched: i == touchedIndex);
case 6:
return makeGroupData(6, 6.5, isTouched: i == touchedIndex);
default:
return throw Error();
}
});
BarChartData mainBarData() {
return BarChartData(
barTouchData: BarTouchData(
touchTooltipData: BarTouchTooltipData(
tooltipBgColor: Colors.blueGrey,
getTooltipItem: (group, groupIndex, rod, rodIndex) {
String weekDay;
switch (group.x.toInt()) {
case 0:
weekDay = 'Monday';
break;
case 1:
weekDay = 'Tuesday';
break;
case 2:
weekDay = 'Wednesday';
break;
case 3:
weekDay = 'Thursday';
break;
case 4:
weekDay = 'Friday';
break;
case 5:
weekDay = 'Saturday';
break;
case 6:
weekDay = 'Sunday';
break;
default:
throw Error();
}
return BarTooltipItem(
weekDay + '\n',
const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
children: <TextSpan>[
TextSpan(
text: (rod.toY - 1).toString(),
style: const TextStyle(
color: Colors.yellow,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
],
);
}),
),
titlesData: FlTitlesData(
show: true,
rightTitles: SideTitles(showTitles: false),
topTitles: SideTitles(showTitles: false),
bottomTitles: SideTitles(
showTitles: true,
getTextStyles: (context, value) => const TextStyle(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14),
margin: 16,
getTitles: (double value) {
switch (value.toInt()) {
case 0:
return 'M';
case 1:
return 'T';
case 2:
return 'W';
case 3:
return 'T';
case 4:
return 'F';
case 5:
return 'S';
case 6:
return 'S';
default:
return '';
}
},
),
leftTitles: SideTitles(
showTitles: false,
),
),
borderData: FlBorderData(
show: false,
),
barGroups: showingGroups(),
gridData: FlGridData(show: false),
);
}
BarChartData randomData() {
return BarChartData(
barTouchData: BarTouchData(
enabled: false,
),
titlesData: FlTitlesData(
show: true,
bottomTitles: SideTitles(
showTitles: true,
getTextStyles: (context, value) => const TextStyle(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14),
margin: 16,
getTitles: (double value) {
switch (value.toInt()) {
case 0:
return 'M';
case 1:
return 'T';
case 2:
return 'W';
case 3:
return 'T';
case 4:
return 'F';
case 5:
return 'S';
case 6:
return 'S';
default:
return '';
}
},
),
leftTitles: SideTitles(
showTitles: false,
),
topTitles: SideTitles(
showTitles: false,
),
rightTitles: SideTitles(
showTitles: false,
)),
borderData: FlBorderData(
show: false,
),
barGroups: List.generate(7, (i) {
switch (i) {
case 0:
return makeGroupData(0, Random().nextInt(15).toDouble() + 6,
barColor: Colors.blue);
case 1:
return makeGroupData(1, Random().nextInt(15).toDouble() + 6,
barColor: Colors.amber);
case 2:
return makeGroupData(2, Random().nextInt(15).toDouble() + 6,
barColor: Colors.deepOrange);
case 3:
return makeGroupData(3, Random().nextInt(15).toDouble() + 6,
barColor: Colors.pink);
case 4:
return makeGroupData(4, Random().nextInt(15).toDouble() + 6,
barColor: Colors.red);
case 5:
return makeGroupData(5, Random().nextInt(15).toDouble() + 6,
barColor: Colors.green);
case 6:
return makeGroupData(6, Random().nextInt(15).toDouble() + 6,
barColor: Colors.blueGrey);
default:
return throw Error();
}
}),
gridData: FlGridData(show: false),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:test_project_pdf/pdf_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Scaffold(
body: Center (child: PdfPage()),
),
);
}
}