Skip to content

Reduce freezes on web. #1855

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

ksokolovskyi
Copy link

Fixes #1528
Fixes #1480
Fixes #1193
Fixes #1114

Description

This PR adds enableEventLoopBalancing parameter to Document.save() function in order to yield periodically during processing and improve event loop responsiveness when running on the main isolate (e.g., on Web).

Sample PDF

Sample PDF generation code
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;

void main() {
  runApp(const App());
}

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

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  final _status = ValueNotifier('Idle');

  @override
  void dispose() {
    _status.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            spacing: 50,
            children: [
              _EndlessAnimation(),
              ValueListenableBuilder(
                valueListenable: _status,
                builder: (context, status, _) => Text(status),
              ),
              ElevatedButton(
                child: const Text('Generate'),
                onPressed: () {
                  _generatePdf();
                },
              ),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> _generatePdf() async {
    final qr = await rootBundle.load('assets/qr.jpg');
    final qrBytes = qr.buffer.asUint8List();

    _status.value = 'Generating...';

    final pdf = pw.Document();

    for (var i = 0; i < 30; i++) {
      pdf.addPage(
        pw.Page(
          pageFormat: PdfPageFormat.a4,
          build: (pw.Context context) {
            return pw.Column(
              mainAxisAlignment: pw.MainAxisAlignment.center,
              crossAxisAlignment: pw.CrossAxisAlignment.stretch,
              children: [
                for (var i = 0; i < 8; i++)
                  pw.Row(
                    children: [
                      pw.Expanded(
                        child: _buildLabel(qrBytes),
                      ),
                      pw.Expanded(
                        child: _buildLabel(qrBytes),
                      ),
                    ],
                  )
              ],
            );
          },
        ),
      );
    }

    final total = Stopwatch()..start();

    await pdf.save(enableEventLoopBalancing: true);
    // await pdf.save();

    total.stop();
    print('total: ${total.elapsedMilliseconds}ms');

    _status.value = 'Done!';
  }

  pw.Widget _buildLabel(Uint8List qrBytes) {
    return pw.Container(
      decoration: pw.BoxDecoration(
        border: pw.Border.all(
          width: 3,
          color: PdfColors.blue,
        ),
      ),
      child: pw.Row(
        children: [
          pw.Expanded(
            child: pw.Column(
              mainAxisAlignment: pw.MainAxisAlignment.center,
              children: [
                pw.Text(
                  'Flutter',
                  style: pw.TextStyle(
                    color: PdfColor.fromInt(0xFF13B9FD),
                    fontSize: 20,
                    fontWeight: pw.FontWeight.bold,
                  ),
                ),
                pw.Text(
                  'Build for any screen',
                  style: pw.TextStyle(
                    color: PdfColors.black,
                    fontSize: 12,
                    fontWeight: pw.FontWeight.normal,
                  ),
                ),
              ],
            ),
          ),
          pw.Expanded(
            child: pw.Padding(
              padding: pw.EdgeInsets.all(8),
              child: pw.Column(
                mainAxisAlignment: pw.MainAxisAlignment.center,
                children: [
                  pw.Image(pw.MemoryImage(qrBytes), width: 60, height: 60),
                  pw.Text('Scan me'),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _EndlessAnimation extends StatefulWidget {
  const _EndlessAnimation();

  @override
  State<_EndlessAnimation> createState() => __EndlessAnimationState();
}

class __EndlessAnimationState extends State<_EndlessAnimation>
    with SingleTickerProviderStateMixin {
  late final _controller = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 3),
  );

  @override
  void initState() {
    super.initState();
    _controller.repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: AnimatedBuilder(
        animation: _controller,
        builder: (context, child) {
          return Transform.rotate(
            angle: math.pi * 2 * _controller.value,
            child: child,
          );
        },
        child: Container(
          width: 150,
          height: 150,
          color: Colors.red,
        ),
      ),
    );
  }
}
Web Before (No WASM) Web After (No WASM)
~553ms ~921ms
web_before.mov
web_after.mov
Web Before (WASM) Web After (WASM)
~433ms ~795ms
web_wasm_before.mov
web_wasm_after.mov

@ksokolovskyi
Copy link
Author

Hi @DavBfr, could you please look at this PR when you have time?
Thanks in advance!

@ksokolovskyi ksokolovskyi changed the title Reduce freezes on web Reduce freezes on web. Jun 23, 2025
@ksokolovskyi
Copy link
Author

Hi @DavBfr, would you mind taking a look when you're free? Thanks!

@shahmirzali49
Copy link

@ksokolovskyi thank you for PR. This PR only for Web or works on other platforms too? for example desktop windows/macos

@ksokolovskyi
Copy link
Author

Hi @shahmirzali49, this PR is focused mainly on web, as there is no isolates support there.

On desktop, consider using isolates. The example can be found here: https://github.com/DavBfr/dart_pdf/blob/master/pdf/test/isolate_test.dart

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants