Closed Vera-Spoettl closed 5 months ago
I gave it a try today.
I added a widget for "wrapping" unsupported flutter widgets, that I want to capture and printed:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class CaptureWrapper extends StatelessWidget {
final Widget child;
const CaptureWrapper({
super.key,
required this.child,
});
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: child,
);
}
}
I expanded export_instance.dart with this case:
case CaptureWrapper:
if (context != null) {
if (widget.key == null) {
throw Exception('Capture must have a key to be exported');
}
Element? contextElement =
findElement(context, (CaptureWrapper e) => e.key == widget.key);
assert(contextElement != null);
RenderRepaintBoundary? boundary;
RenderObject? renderObject = contextElement!.renderObject;
if (renderObject is RenderRepaintBoundary) {
boundary = renderObject;
} else {
renderObject?.visitChildren((child) {
if (child is RenderRepaintBoundary) {
boundary = child;
}
});
}
assert(boundary != null);
final ui.Image uiImage = await boundary!.toImage(pixelRatio: 2.0);
final pngBytes =
await uiImage.toByteData(format: ImageByteFormat.png);
return [
await Image.memory(Uint8List.view(pngBytes!.buffer)).toPdfWidget()
];
}
return [];
Up to now, it seems to work with fl_chart and CustomPainter widgets.
Unfortunately I'm not experienced enough in flutter to know if this is an elegant, efficient, ... solution. But I'm open to discussions. :-)
Hey, thanks for your detailed explanation :)
So I think supporting fl_charts
boils down to supporting flutters CustomPainter
.
The straight forward way to do this would be to create an extension
on CustomPaint
to access the paint(Canvas canvas, Size size)
method. The problem with this approach is that having access to the paint method itself doesn't help much. One would need access to the actual Canvas
methods used (e.g. canvas.drawColor(...)
, canvas.drawArc(...)
), since these could be converted to their pdf
equivalent.
But since this is not possible, the only way (I could think of) to support it would be your approach by converting said widgets to images. π
Feel free to open a pull request so I can take a closer look and actually run and test your solution. Also since your approach seems very suitable, a PR would be best for further discussion.
Thanks for your involvement! π₯
I'll prepare a PR but it'll take me some days (lots of things on my plate atm). π
I was wondering, if I could use PictureRecorder to use for CustomPaint stuff. Maybe I can give it a try ...
Hi @joseph-grabinger ,
I have a very strange behaviour of the CaptureWrapper which occurs only in my code but not in your example and I can't find the point that causes the bug:
When I give a GlobalKey to a captured widget, I get the following error during runtime:
ββββββββ Exception caught by widgets library βββββββββββββββββββββββββββββββββββ
The following assertion was thrown building CaptureWrapper-[<'someFrame'>]:
'package:flutter/src/widgets/framework.dart': Failed assertion: line 2116 pos 12: '_elements.contains(element)': is not true.
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=2_bug.yml
The relevant error-causing widget was:
CaptureWrapper-[<'someFrame'>] CaptureWrapper:file:///Users/vsz/Development/projects/stuff/report_generator/lib/main.dart:31:15
followed by
ββββββββ Exception caught by widgets library βββββββββββββββββββββββββββββββββββ
Duplicate GlobalKey detected in widget tree.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Here is my minimal code example:
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter_to_pdf/flutter_to_pdf.dart';
import 'package:printing/printing.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatefulWidget {
const MainApp({super.key});
@override
State<MainApp> createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
final ExportDelegate exportDelegate = ExportDelegate();
GlobalKey scatterChartKey = GlobalKey();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ExportFrame(
frameId: 'someFrameId',
exportDelegate: exportDelegate,
child: Column(
children: [
const SizedBox(height: 10),
CaptureWrapper(
key: const Key('someFrame'),
child: SizedBox(
width: 400,
height: 200,
child: ScatterChart(
key: scatterChartKey,
ScatterChartData(
scatterSpots: [
ScatterSpot(1, 1),
ScatterSpot(1, 2),
ScatterSpot(2, 3),
ScatterSpot(3, 2),
ScatterSpot(4, 5),
ScatterSpot(5, 4),
ScatterSpot(6, 6),
],
titlesData: const FlTitlesData(
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
),
),
),
),
),
ElevatedButton(
onPressed: () async {
final ExportOptions overrideOptions = ExportOptions(
textFieldOptions: TextFieldOptions.uniform(
interactive: false,
),
checkboxOptions: CheckboxOptions.uniform(
interactive: false,
),
);
final pdf = await exportDelegate.exportToPdfDocument(
'someFrameId',
overrideOptions: overrideOptions,
);
Printing.layoutPdf(onLayout: (format) => pdf.save());
},
child: const Text('Export'),
),
],
),
),
),
);
}
}
and my pubspec.yaml
name: report_generator
description: "A new Flutter project."
publish_to: "none"
version: 0.1.0
environment:
sdk: ">=3.4.4 <4.0.0"
dependencies:
fl_chart: ^0.68.0
flutter:
sdk: flutter
flutter_to_pdf: ^0.2.1
printing: ^5.13.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
uses-material-design: true
When I replace the chart from your example app with the following code:
return ScatterChart(
key: GlobalKey(),
chartRendererKey: rendererKey,
ScatterChartData(
scatterSpots: [
ScatterSpot(1, 1),
ScatterSpot(1, 2),
ScatterSpot(2, 3),
ScatterSpot(3, 2),
ScatterSpot(4, 5),
ScatterSpot(5, 4),
ScatterSpot(6, 6),
],
titlesData: const FlTitlesData(
topTitles:
AxisTitles(sideTitles: SideTitles(showTitles: false)))),
);
everything works still fine.
Do you have any idea? What's going on here? I think I already spent 2 days now on trying many things to make it work but besides isolating the problem to this minimal example, I didn't make any progress ... :-(
BTW: just tried the same with a Text widget and a GlobalKey and had the same problem, so it doesn't seem to be a problem of ScatterChart.
Does the error happen once you export a frame or already before exporting (when rendering the widget on screen) ?
It happens during export.
Gesendet von Outlook fΓΌr iOShttps://aka.ms/o0ukef
Von: Joseph Grabinger @.> Gesendet: Saturday, August 3, 2024 8:28:40 PM An: joseph-grabinger/flutter_to_pdf @.> Cc: Vera SpΓΆttl-Zeisberg @.>; Author @.> Betreff: Re: [joseph-grabinger/flutter_to_pdf] Support "unsupported" widgets e.g. Painter, Charts, ... (Issue #62)
Does the error happen once you export a frame or already before exporting (when rendering the widget on screen) ?
β Reply to this email directly, view it on GitHubhttps://github.com/joseph-grabinger/flutter_to_pdf/issues/62#issuecomment-2267095151, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AXW4SU6DOMRLUVXCKVACLXDZPUONRAVCNFSM6AAAAABFTC3WJCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRXGA4TKMJVGE. You are receiving this because you authored the thread.Message ID: @.***>
I was able to reproduce the issue!
It seems that the issue only occurs if a widget inside of a CaptureWrapper
uses a GlobalKey
outside of the CaptureWrapper
's scope.
This works:
CaptureWrapper(
key: const Key('CustomPaint'),
child: CustomPaint(
key: GlobalKey(),
size: const Size(300, 300),
painter: HousePainter(),
),
),
But this doesn't work:
final gKey = GlabalKey();
CaptureWrapper(
key: const Key('CustomPaint'),
child: CustomPaint(
key: gKey,
size: const Size(300, 300),
painter: HousePainter(),
),
),
Not completely sure why this is the case :/
Unfortunately, in my minimal example moving the GlobalKey into a child widget of CaptureWrapper makes no difference. In both cases, I get the error. And it makes no difference if it is a complex widget like the ScatterChart or a quite simple one like placeholder:
This seems to be the place where the problem appears:
API-Documentation of GlobalKey says: "Widgets that have global keys reparent their subtrees when they are moved from one location in the tree to another location in the tree."
I guess this is somehow the problem. But I don't understand and can't explain it yet.
And it makes no difference if it is a complex widget like the ScatterChart or a quite simple one like placeholder
I agree that it doesn't depend on the widget itself but rather on a GlobalKey
being present.
However, your example with the Placeholder(key: GlobalKey())
(or also a Text("someText", key: GlobalKey())
) doesn't produce any errors for me and the entire widget is exported correctly.
Since there is seems to be some difference in the output.... What flutter version are you using?
For me the examples run just fine on Flutter (Channel stable, 3.22.0, on macOS 14.6 23G80 darwin-x64, locale de-DE)
Very strange behaviour!
Thatβs my flutter doctor
[β] Flutter (Channel stable, 3.22.3, on macOS 14.5 23F79 darwin-arm64, locale de-DE) [β] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [β] Xcode - develop for iOS and macOS (Xcode 15.4) [β] Chrome - develop for the web [β] Android Studio (version 2023.1) [β] VS Code (version 1.91.1) [β] Connected device (4 available) [β] Network resources
β’ No issues found!
Not much different. Guess the MacOS version shouldnβt cause the problem. Iβll try to downgrade to Flutter 3.22 later this afternoon and let you know the result.
Von: Joseph Grabinger @.> Datum: Sonntag, 4. August 2024 um 12:52 An: joseph-grabinger/flutter_to_pdf @.> Cc: Vera SpΓΆttl-Zeisberg @.>, Author @.> Betreff: Re: [joseph-grabinger/flutter_to_pdf] Support "unsupported" widgets e.g. Painter, Charts, ... (Issue #62)
And it makes no difference if it is a complex widget like the ScatterChart or a quite simple one like placeholder
I agree that it doesn't depend on the widget itself but rather on a GlobalKey being present.
However, your example with the Placeholder(key: GlobalKey()) (or also a Text("someText", key: GlobalKey())) doesn't produce any errors for me and the entire widget is exported correctly.
Since there is seems to be some difference in the output.... What flutter version are you using?
For me the examples run just fine on Flutter (Channel stable, 3.22.0, on macOS 14.6 23G80 darwin-x64, locale de-DE)
β Reply to this email directly, view it on GitHubhttps://github.com/joseph-grabinger/flutter_to_pdf/issues/62#issuecomment-2267498020, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AXW4SUY4OM3MEOXQI5OAZULZPYBVXAVCNFSM6AAAAABFTC3WJCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRXGQ4TQMBSGA. You are receiving this because you authored the thread.Message ID: @.***>
Okay I just upgraded to Flutter 3.22.3
and now I have the same issue.
So it seems that there has been some kind of change to the GlobalKey
between Flutter 3.22.0
and 3.22.3
.
What's the best way to handle this? I can't find anything in the Flutter change logs related to GlobalKey. I could downgrade to 3.22 but I guess that just postpones the problem to the next update of Flutter.
What's the best way to handle this?
I created a separate Issue (#81) so we could move the discussion to there.
I'll create a branch with an example that runs on my machine, since I am now also running Flutter 3.22.3
. You could then check-out the branch and we'll go from there.
Great package! Unfortunately, I have to use quite some "unsupported" widgets like charts from fl_chart, diagrams painted via custom_painter, ... . I'm wondering, if it would be possible (and with normal effort realizable) to support these widgets via screenshot and image. Have you ever spend a thought on this or do you have a better idea? Thanks, Vera