Adding graph in pdf using flutter

Viewed 505

I am working on project which requires to add bar chart in the pdf(if possible customizable pdf). I am trying to add using below code but it didn't work

 void _printScreen() {
      doc.addPage(pw.MultiPage(
          pageFormat: PdfPageFormat.letter
              .copyWith(marginBottom: 1.5 * PdfPageFormat.cm),
          header: (pw.Context context) {
            if (context.pageNumber == 1) {
              return null;
            }
            return pw.Container(
                alignment: pw.Alignment.centerRight,
                margin:
                    const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm),
                padding:
                    const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm),
                decoration: const pw.BoxDecoration(
                  border: pw.Border(
                      bottom: pw.BorderSide(
                    color: PdfColors.grey,
                    width: 0.5,
                  )),
                ),
                child: pw.Text('Report',
                    style: pw.Theme.of(context)
                        .defaultTextStyle
                        .copyWith(color: PdfColors.grey)));
          },
          footer: (pw.Context context) {
            return pw.Container(
                alignment: pw.Alignment.centerRight,
                margin: const pw.EdgeInsets.only(top: 1.0 * PdfPageFormat.cm),
                child: pw.Text(
                    'Page ${context.pageNumber} of ${context.pagesCount}',
                    style: pw.Theme.of(context)
                        .defaultTextStyle
                        .copyWith(color: PdfColors.grey)));
          },
          build: (pw.Context context) => [
                pw.Center(
                  child: pw.Expanded(
                    child: pw.Image(image),
                  ),
                )
              ]));

      //save PDF
      final String dir = (await getExternalStorageDirectory()).path;
      final String path =
          '$dir/myreport${DateTime.now().toIso8601String()}.pdf';
      final File file = File(path);
      await file.writeAsBytes(await doc.save());
      snackbarMessage(
          message: "Report Saved Successfully.",
          icon: FontAwesomeIcons.infoCircle);
      return doc.save();
    });
  }

if anyone implemeted something like this(Adding graph in pdf) please share your answer..Thank you in advance :)

1 Answers

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()),
      ),
    );
  }
}
Related