DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

How to take screenshots and generate PDFs in Dart and Flutter

How to Take Screenshots and Generate PDFs in Dart and Flutter

The standard Flutter approach to PDF generation is the pdf package — you build documents by constructing a widget tree in Dart, laying out text and images programmatically. It works well for simple documents but falls apart with complex HTML templates, CSS layouts, or anything that needs to match a web-rendered design exactly.

Here's the simpler path: send your HTML to PageBolt, get back a pixel-perfect PDF. One HTTP call, binary response.

Dart (server-side / CLI)

No dependencies beyond dart:io and dart:convert:

import 'dart:convert';
import 'dart:io';

Future<Uint8List> screenshot(String url) async {
  final apiKey = Platform.environment['PAGEBOLT_API_KEY']!;
  final client = HttpClient();

  final request = await client.postUrl(
    Uri.parse('https://pagebolt.dev/api/v1/screenshot'),
  );
  request.headers.set('x-api-key', apiKey);
  request.headers.set('content-type', 'application/json');
  request.add(utf8.encode(jsonEncode({
    'url': url,
    'fullPage': true,
    'blockBanners': true,
  })));

  final response = await request.close();
  final bytes = await consolidateHttpClientResponseBytes(response);
  client.close();
  return bytes;
}

void main() async {
  final image = await screenshot('https://example.com');
  await File('screenshot.png').writeAsBytes(image);
  print('Screenshot saved (${image.length} bytes)');
}
Enter fullscreen mode Exit fullscreen mode

Flutter — using http package

Add to pubspec.yaml:

dependencies:
  http: ^1.2.0
Enter fullscreen mode Exit fullscreen mode
import 'package:http/http.dart' as http;
import 'dart:convert';

class PageBoltClient {
  static const _baseUrl = 'https://pagebolt.dev/api/v1';
  final String _apiKey;

  PageBoltClient(this._apiKey);

  Future<Uint8List> screenshot(String url) async {
    final response = await http.post(
      Uri.parse('$_baseUrl/screenshot'),
      headers: {
        'x-api-key': _apiKey,
        'Content-Type': 'application/json',
      },
      body: jsonEncode({
        'url': url,
        'fullPage': true,
        'blockBanners': true,
      }),
    );
    if (response.statusCode != 200) {
      throw Exception('PageBolt error ${response.statusCode}: ${response.body}');
    }
    return response.bodyBytes;
  }

  Future<Uint8List> pdfFromUrl(String url) async {
    final response = await http.post(
      Uri.parse('$_baseUrl/pdf'),
      headers: {'x-api-key': _apiKey, 'Content-Type': 'application/json'},
      body: jsonEncode({'url': url, 'blockBanners': true}),
    );
    return response.bodyBytes;
  }

  Future<Uint8List> pdfFromHtml(String html) async {
    final response = await http.post(
      Uri.parse('$_baseUrl/pdf'),
      headers: {'x-api-key': _apiKey, 'Content-Type': 'application/json'},
      body: jsonEncode({'html': html}),
    );
    return response.bodyBytes;
  }
}
Enter fullscreen mode Exit fullscreen mode

Flutter — share PDF via Share sheet

import 'package:share_plus/share_plus.dart';
import 'package:path_provider/path_provider.dart';

Future<void> sharePdf(String invoiceHtml) async {
  final pagebolt = PageBoltClient(const String.fromEnvironment('PAGEBOLT_API_KEY'));
  final pdfBytes = await pagebolt.pdfFromHtml(invoiceHtml);

  final dir = await getTemporaryDirectory();
  final file = File('${dir.path}/invoice.pdf');
  await file.writeAsBytes(pdfBytes);

  await Share.shareXFiles(
    [XFile(file.path, mimeType: 'application/pdf')],
    subject: 'Your Invoice',
  );
}
Enter fullscreen mode Exit fullscreen mode

Flutter — display PDF inline

Using flutter_pdfview or syncfusion_flutter_pdfviewer:

import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';

class InvoiceScreen extends StatefulWidget {
  final String invoiceHtml;
  const InvoiceScreen({required this.invoiceHtml, super.key});

  @override
  State<InvoiceScreen> createState() => _InvoiceScreenState();
}

class _InvoiceScreenState extends State<InvoiceScreen> {
  Uint8List? _pdfBytes;

  @override
  void initState() {
    super.initState();
    _load();
  }

  Future<void> _load() async {
    final pagebolt = PageBoltClient(const String.fromEnvironment('PAGEBOLT_API_KEY'));
    final bytes = await pagebolt.pdfFromHtml(widget.invoiceHtml);
    setState(() => _pdfBytes = bytes);
  }

  @override
  Widget build(BuildContext context) {
    if (_pdfBytes == null) return const CircularProgressIndicator();
    return SfPdfViewer.memory(_pdfBytes!);
  }
}
Enter fullscreen mode Exit fullscreen mode

No layout engine, no pdf package widget tree. The HTML template renders exactly as it does in a browser — fonts, CSS grid, images included.


Try it free — 100 requests/month, no credit card. → Get started in 2 minutes

Top comments (0)