typst_flutter 2.2.0 copy "typst_flutter: ^2.2.0" to clipboard
typst_flutter: ^2.2.0 copied to clipboard

Embed the Typst typesetting compiler natively in Flutter via Rust FFI. Compile Typst markup to PDF or rendered images on all platforms except web — no WASM, no server required.

example/lib/main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:printing/printing.dart';
import 'package:typst_flutter/typst_flutter.dart';

/// The main entry point for the example app.
void main() {
  runApp(
    MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: const Color(0xFF1E1E2E),
        primaryColor: const Color(0xFF89B4FA),
      ),
      home: const ExampleApp(),
    ),
  );
}

/// Example app demonstrating the typst_flutter package.
class ExampleApp extends StatefulWidget {
  /// Creates the example app.
  const ExampleApp({super.key});

  @override
  State<ExampleApp> createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  final _controller = TextEditingController(
    text: r'''
#set page(width: 148mm, height: 210mm, margin: 1cm)
#set text(font: "Libertinus Serif", size: 12pt)

= Hello Typst!

This document was compiled *natively* inside a Flutter app using
the Typst compiler via Rust FFI.

== Features
- *Fast*: Sub-100ms compilation
- *Beautiful*: Professional typography bundled in
- *Native*: No WASM or WebView overhead
- *FFI*: Peak performance via Rust

== Math Support
Typst has first-class math support. Here is the quadratic formula:

$ x = (-b plus.minus sqrt(b^2 - 4a c)) / (2a) $

And some calculus:
$ integral_0^infinity e^(-x^2) dif x = sqrt(pi) / 2 $

== Date Injection
Today's date injected from Flutter: #datetime.today().display()
''',
  );

  String _currentSource = '';
  TypstCompiler? _compiler;

  @override
  void initState() {
    super.initState();
    _currentSource = _controller.text;
    unawaited(_initCompiler());
  }

  Future<void> _initCompiler() async {
    final compiler = await TypstCompiler.create();
    if (!mounted) {
      compiler.dispose();
      return;
    }
    setState(() {
      _compiler = compiler;
    });
  }

  @override
  void dispose() {
    _compiler?.dispose();
    super.dispose();
  }

  void _compile() {
    setState(() {
      _currentSource = _controller.text;
    });
  }

  Future<void> _showSvgOutput() async {
    if (_compiler == null) return;
    final doc = await _compiler!.compile(source: _controller.text);
    if (!mounted) {
      doc.dispose();
      return;
    }

    await Navigator.of(context).push(
      MaterialPageRoute<void>(
        builder: (context) => Scaffold(
          appBar: AppBar(title: const Text('Interactive SVG Output')),
          body: InteractiveViewer(
            minScale: 0.5,
            maxScale: 5,
            child: ListView.separated(
              padding: const EdgeInsets.all(16),
              itemCount: doc.pageCount,
              separatorBuilder: (context, index) => const SizedBox(height: 16),
              itemBuilder: (context, index) => Center(
                child: Card(
                  elevation: 4,
                  clipBehavior: Clip.antiAlias,
                  child: TypstView(document: doc, pageIndex: index),
                ),
              ),
            ),
          ),
        ),
      ),
    );
    doc.dispose();
  }

  Future<void> _showPdfPreview() async {
    if (_compiler == null) return;
    final doc = await _compiler!.compile(source: _controller.text);
    final pdfBytes = await doc.exportPdf();
    doc.dispose();

    await Printing.layoutPdf(onLayout: (format) async => pdfBytes);
  }

  Future<void> _savePdf() async {
    if (_compiler == null) return;
    final doc = await _compiler!.compile(source: _controller.text);
    final pdfBytes = await doc.exportPdf();
    doc.dispose();

    // Printing.sharePdf handles the native iOS "Save to Files" and
    // Android Share Sheet (which includes Save to Drive/Downloads)
    // without requiring explicit storage permissions.
    await Printing.sharePdf(bytes: pdfBytes, filename: 'typst_document.pdf');
  }

  @override
  Widget build(BuildContext context) {
    if (_compiler == null) {
      return const Scaffold(
        backgroundColor: Color(0xFF1E1E2E),
        body: Center(
          child: CircularProgressIndicator(color: Color(0xFF89B4FA)),
        ),
      );
    }

    return TypstCompilerProvider(
      compiler: _compiler!,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Typst Flutter'),
          backgroundColor: const Color(0xFF1E1E2E),
          foregroundColor: const Color(0xFFCDD6F4),
          actions: [
            PopupMenuButton<String>(
              onSelected: (val) {
                if (val == 'svg') unawaited(_showSvgOutput());
                if (val == 'pdf') unawaited(_showPdfPreview());
                if (val == 'save') unawaited(_savePdf());
              },
              itemBuilder: (context) => [
                const PopupMenuItem(
                  value: 'svg',
                  child: ListTile(
                    leading: Icon(Icons.zoom_in),
                    title: Text('Interactive SVG'),
                  ),
                ),
                const PopupMenuItem(
                  value: 'pdf',
                  child: ListTile(
                    leading: Icon(Icons.picture_as_pdf),
                    title: Text('Native PDF Preview'),
                  ),
                ),
                const PopupMenuItem(
                  value: 'save',
                  child: ListTile(
                    leading: Icon(Icons.save_alt),
                    title: Text('Save / Share PDF'),
                  ),
                ),
              ],
            ),
          ],
        ),
        body: Column(
          children: [
            Expanded(
              child: Padding(
                padding: const EdgeInsets.all(8),
                child: TextField(
                  controller: _controller,
                  maxLines: null,
                  expands: true,
                  style: const TextStyle(
                    fontFamily: 'monospace',
                    fontSize: 13,
                    color: Color(0xFFCDD6F4),
                  ),
                  decoration: const InputDecoration(
                    border: OutlineInputBorder(),
                    fillColor: Color(0xFF1E1E2E),
                    filled: true,
                    hintText: 'Enter Typst markup…',
                  ),
                ),
              ),
            ),
            const Divider(height: 1, color: Color(0xFF45475A)),
            Expanded(
              child: ColoredBox(
                color: const Color(0xFF181825),
                child: TypstDocumentViewer(
                  source: _currentSource,
                  loadingBuilder: (context) => const Center(
                    child: CircularProgressIndicator(color: Color(0xFF89B4FA)),
                  ),
                ),
              ),
            ),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          backgroundColor: const Color(0xFF89B4FA),
          onPressed: _compile,
          child: const Icon(Icons.play_arrow, color: Color(0xFF1E1E2E)),
        ),
      ),
    );
  }
}
0
likes
160
points
304
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Embed the Typst typesetting compiler natively in Flutter via Rust FFI. Compile Typst markup to PDF or rendered images on all platforms except web — no WASM, no server required.

Repository (GitHub)
View/report issues

Topics

#pdf #typst #document #typesetting #ffi

License

Apache-2.0 (license)

Dependencies

archive, crypto, equatable, flutter, flutter_rust_bridge, flutter_svg, http, meta, path

More

Packages that depend on typst_flutter

Packages that implement typst_flutter