joseph-grabinger / flutter_to_pdf

Create PDFs but work with normal Flutter Widgets.
MIT License
21 stars 17 forks source link

Example using `GlobalKey` inside `CaptureWrapper` #82

Closed joseph-grabinger closed 2 months ago

joseph-grabinger commented 3 months ago

Hey @Vera-Spoettl, this example works for me (ran on macOS) with Flutter version 3.22.3.

Please run the example and switch to the "Charts & Custom Paint" tab, where I replaced the CustomPaint example with a simple Text widget using a GlobalKey.

Here my flutter doctor:

[!] Flutter (Channel stable, 3.22.3, on macOS 14.6 23G80 darwin-x64, locale de-DE)
[✓] Android toolchain - develop for Android devices (Android SDK version 32.0.0)
[!] Xcode - develop for iOS and macOS (Xcode 15.4)
    ! CocoaPods 1.11.3 out of date (1.13.0 is recommended).
        CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/platform-plugins
      To upgrade see https://guides.cocoapods.org/using/getting-started.html#updating-cocoapods for instructions.
[✓] Chrome - develop for the web
[!] Android Studio (version unknown)
    ✗ Unable to determine Android Studio version.
    ✗ Unable to find bundled Java version.
[✓] Android Studio (version 2022.1)
[✓] VS Code (version 1.92.0)
[✓] Connected device (4 available)
[✓] Network resources
joseph-grabinger commented 3 months ago

According to the docs:

[GlobalKeys] should usually be long-lived objects owned by a [State] object, for example

See: https://github.com/flutter/flutter/commit/d2c47acdaa3e30e43087018f24e97ad4b182229c#diff-134bc36bd7c045a92ffff29f7efdaa7d375384911475a40343814c76c87b17cbR120-R121

I minimized the example in 6af8fa7.

What I found out is the following:

void main() => runApp(const Demo());

class Demo extends StatefulWidget { const Demo({super.key});

@override State createState() => _DemoState(); }

class _DemoState extends State { final ExportDelegate exportDelegate = ExportDelegate();

Future saveFile(document, String name) async {...}

@override Widget build(BuildContext context) => MaterialApp( theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, tabBarTheme: const TabBarTheme( labelColor: Colors.black87, ), ), home: Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.primary, title: const Text('Flutter to PDF - Demo'), ), bottomSheet: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( onPressed: () async { final ExportOptions overrideOptions = ExportOptions( textFieldOptions: TextFieldOptions.uniform( interactive: false, ), checkboxOptions: CheckboxOptions.uniform( interactive: false, ), ); final pdf = await exportDelegate.exportToPdfDocument( 'Demo', overrideOptions: overrideOptions); saveFile(pdf, 'static-example'); }, child: const Row( children: [ Text('Export as static'), Icon(Icons.save_alt_outlined), ], ), ), TextButton( onPressed: () async { final pdf = await exportDelegate .exportToPdfDocument('Demo'); saveFile(pdf, 'interactive-example'); }, child: const Row( children: [ Text('Export as interactive'), Icon(Icons.save_alt_outlined), ], ), ), ], ), body: ExportFrame( frameId: 'Demo', exportDelegate: exportDelegate, child: CaptureWrapper( key: const Key('CaptureWraperKey'), child: Text("Hello World", key: GlobalKey()), ), ), ), ); }


However, extracting everything inside the `CaptureWrapper` into a standalone `StatefulWidget` makes it work (not completely sure why since `Demo ` is also  a `StatefulWidget`): 

```dart
import 'package:flutter/material.dart';
import 'package:flutter_to_pdf/flutter_to_pdf.dart';

void main() => runApp(const Demo());

class Demo extends StatefulWidget {
  const Demo({super.key});

  @override
  State<Demo> createState() => _DemoState();
}

class _DemoState extends State<Demo> {
  final ExportDelegate exportDelegate = ExportDelegate();

  Future<void> saveFile(document, String name) async {...}

  @override
  Widget build(BuildContext context) => MaterialApp(
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          useMaterial3: true,
          tabBarTheme: const TabBarTheme(
            labelColor: Colors.black87,
          ),
        ),
        home: Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.primary,
            title: const Text('Flutter to PDF - Demo'),
          ),
          bottomSheet: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              TextButton(
                onPressed: () async {
                  final ExportOptions overrideOptions = ExportOptions(
                    textFieldOptions: TextFieldOptions.uniform(
                      interactive: false,
                    ),
                    checkboxOptions: CheckboxOptions.uniform(
                      interactive: false,
                    ),
                  );
                  final pdf = await exportDelegate.exportToPdfDocument(
                      'Demo',
                      overrideOptions: overrideOptions);
                  saveFile(pdf, 'static-example');
                },
                child: const Row(
                  children: [
                    Text('Export as static'),
                    Icon(Icons.save_alt_outlined),
                  ],
                ),
              ),
              TextButton(
                onPressed: () async {
                  final pdf = await exportDelegate
                      .exportToPdfDocument('Demo');
                  saveFile(pdf, 'interactive-example');
                },
                child: const Row(
                  children: [
                    Text('Export as interactive'),
                    Icon(Icons.save_alt_outlined),
                  ],
                ),
              ),
            ],
          ),
          body: ExportFrame(
            frameId: 'Demo',
            exportDelegate: exportDelegate,
            child: const Body(),
          ),
        ),
      );
}

class Body extends StatefulWidget {
  const Body({super.key});

  @override
  State<Body> createState() => _BodyState();
}

class _BodyState extends State<Body> {
  final globalKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return CaptureWrapper(
        key: const Key('CaptureWraperKey'),
        child: Text("Hello World", key: globalKey),
    );
  }
}