flutter_jolt 0.1.0 copy "flutter_jolt: ^0.1.0" to clipboard
flutter_jolt: ^0.1.0 copied to clipboard

Rust-powered JavaScript runtime for Flutter. JSC on Apple, QuickJS elsewhere, host JS on web.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_jolt/flutter_jolt.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await RustLib.init();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Jolt Example',
      theme: ThemeData(
        colorSchemeSeed: Colors.deepPurple,
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late final JoltRuntime _runtime;
  final List<_ShowcaseResult> _results = [];
  bool _running = false;

  @override
  void initState() {
    super.initState();
    _runtime = JoltRuntime();
  }

  Future<void> _runShowcases() async {
    setState(() {
      _results.clear();
      _running = true;
    });

    final showcases = <_Showcase>[
      _Showcase('Basic Arithmetic', '2 + 2'),
      _Showcase('String Operations', '"hello".toUpperCase() + " WORLD"'),
      _Showcase('Array Methods', '[1,2,3].map(x => x * x).join(", ")'),
      _Showcase('JSON Round-trip',
          'JSON.stringify({name: "Jolt", version: 1, platforms: ["iOS","Android","macOS"]})'),
      _Showcase('Closures & IIFE',
          '(() => { let sum = 0; for (let i = 1; i <= 100; i++) sum += i; return sum; })()'),
      _Showcase('RegExp', '"2026-03-12".replace(/(\\d{4})-(\\d{2})-(\\d{2})/, "\$3/\$2/\$1")'),
      _Showcase('Error Handling',
          'try { undefined.property } catch(e) { "Caught: " + e.message }'),
      _Showcase('Destructuring & Spread',
          '(() => { const a = [1,2,3]; const b = [0, ...a, 4]; return b.length; })()'),
      _Showcase('Template Literals',
          '(() => { const lang = "JavaScript"; return `Running \${lang} from Dart via Rust`; })()'),
      _Showcase('Math', 'Math.round(Math.PI * 1000) / 1000'),
    ];

    for (final s in showcases) {
      try {
        final value = await _runtime.eval(code: s.code);
        setState(() => _results.add(_ShowcaseResult(
              title: s.title,
              code: s.code,
              result: _formatJsValue(value),
            )));
      } catch (e) {
        setState(() => _results.add(_ShowcaseResult(
              title: s.title,
              code: s.code,
              result: 'Error: $e',
              isError: true,
            )));
      }
    }

    // Globals showcase
    try {
      await _runtime.setGlobal(
        name: 'greeting',
        value: const JsValue.string('Hello from Dart'),
      );
      final value = await _runtime.eval(code: 'greeting + "!"');
      setState(() => _results.add(_ShowcaseResult(
            title: 'Dart → JS Globals',
            code: 'setGlobal("greeting", "Hello from Dart") → eval(\'greeting + "!"\')',
            result: _formatJsValue(value),
          )));
    } catch (e) {
      setState(() => _results.add(_ShowcaseResult(
            title: 'Dart → JS Globals',
            code: 'setGlobal + eval',
            result: 'Error: $e',
            isError: true,
          )));
    }

    // callFunction showcase
    try {
      await _runtime.eval(
          code: 'function add(a, b) { return a + b; }');
      final value = await _runtime.callFunction(
        name: 'add',
        args: [const JsValue.int(17), const JsValue.int(25)],
      );
      setState(() => _results.add(_ShowcaseResult(
            title: 'Call JS Function',
            code: 'add(17, 25)',
            result: _formatJsValue(value),
          )));
    } catch (e) {
      setState(() => _results.add(_ShowcaseResult(
            title: 'Call JS Function',
            code: 'add(17, 25)',
            result: 'Error: $e',
            isError: true,
          )));
    }

    setState(() => _running = false);
  }

  String _formatJsValue(JsValue value) {
    return switch (value) {
      JsValue_Undefined() => 'undefined',
      JsValue_Null() => 'null',
      JsValue_Bool(:final field0) => '$field0',
      JsValue_Int(:final field0) => '$field0',
      JsValue_Float(:final field0) => '$field0',
      JsValue_String(:final field0) => field0,
      JsValue_Array(:final field0) =>
        '[${field0.map(_formatJsValue).join(', ')}]',
      JsValue_Object(:final field0) =>
        '{${field0.map((e) => '${e.key}: ${_formatJsValue(e.value)}').join(', ')}}',
      JsValue_Bytes(:final field0) => 'Uint8List(${field0.length})',
    };
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Jolt'),
        actions: [
          if (_running)
            const Padding(
              padding: EdgeInsets.only(right: 16),
              child: SizedBox(
                width: 20,
                height: 20,
                child: CircularProgressIndicator(strokeWidth: 2),
              ),
            ),
        ],
      ),
      body: _results.isEmpty
          ? Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.code, size: 64, color: theme.colorScheme.primary),
                  const SizedBox(height: 16),
                  Text('JavaScript runtime for Flutter',
                      style: theme.textTheme.titleMedium),
                  const SizedBox(height: 8),
                  Text('Powered by Rust',
                      style: theme.textTheme.bodyMedium
                          ?.copyWith(color: theme.colorScheme.outline)),
                ],
              ),
            )
          : ListView.separated(
              padding: const EdgeInsets.all(16),
              itemCount: _results.length,
              separatorBuilder: (_, __) => const SizedBox(height: 12),
              itemBuilder: (context, index) {
                final r = _results[index];
                return Card(
                  child: Padding(
                    padding: const EdgeInsets.all(16),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(r.title,
                            style: theme.textTheme.titleSmall?.copyWith(
                                color: theme.colorScheme.primary)),
                        const SizedBox(height: 8),
                        Container(
                          width: double.infinity,
                          padding: const EdgeInsets.all(10),
                          decoration: BoxDecoration(
                            color: theme.colorScheme.surfaceContainerHighest,
                            borderRadius: BorderRadius.circular(8),
                          ),
                          child: Text(r.code,
                              style: theme.textTheme.bodySmall?.copyWith(
                                  fontFamily: 'monospace',
                                  color: theme.colorScheme.onSurfaceVariant)),
                        ),
                        const SizedBox(height: 8),
                        Row(
                          children: [
                            Icon(
                              r.isError ? Icons.error_outline : Icons.arrow_forward,
                              size: 16,
                              color: r.isError
                                  ? theme.colorScheme.error
                                  : theme.colorScheme.tertiary,
                            ),
                            const SizedBox(width: 8),
                            Expanded(
                              child: Text(
                                r.result,
                                style: theme.textTheme.bodyMedium?.copyWith(
                                  fontFamily: 'monospace',
                                  fontWeight: FontWeight.w600,
                                  color: r.isError
                                      ? theme.colorScheme.error
                                      : theme.colorScheme.onSurface,
                                ),
                              ),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                );
              },
            ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _running ? null : _runShowcases,
        icon: const Icon(Icons.play_arrow),
        label: const Text('Run'),
      ),
    );
  }
}

class _Showcase {
  final String title;
  final String code;
  const _Showcase(this.title, this.code);
}

class _ShowcaseResult {
  final String title;
  final String code;
  final String result;
  final bool isError;
  const _ShowcaseResult({
    required this.title,
    required this.code,
    required this.result,
    this.isError = false,
  });
}
1
likes
120
points
68
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Rust-powered JavaScript runtime for Flutter. JSC on Apple, QuickJS elsewhere, host JS on web.

Topics

#javascript #runtime #ffi #rust

License

MIT (license)

Dependencies

flutter, flutter_rust_bridge, freezed_annotation, plugin_platform_interface

More

Packages that depend on flutter_jolt

Packages that implement flutter_jolt