mobile_proof_plugin 0.2.0 copy "mobile_proof_plugin: ^0.2.0" to clipboard
mobile_proof_plugin: ^0.2.0 copied to clipboard

Flutter plugin for TLSN-based mobile proofs with proxy/MPC auto-fallback, WebView capture, and custom provider flows.

example/lib/main.dart

import 'dart:async';

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'mobile_proof_plugin example',
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFF2563EB),
        useMaterial3: true,
      ),
      home: const ExampleHomePage(),
    );
  }
}

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

  @override
  State<ExampleHomePage> createState() => _ExampleHomePageState();
}

class _ExampleHomePageState extends State<ExampleHomePage> {
  static const String _providerRegistryAssetPath =
      'packages/mobile_proof_plugin/assets/providers.json';

  final TextEditingController _verifierUrlController = TextEditingController(
    text: 'http://10.0.2.2:7047',
  );
  bool _running = false;
  String _status = 'Ready';
  ProofMode _proofMode = ProofMode.auto;

  /// Set after a successful run: the mode that actually produced the proof,
  /// derived from `artifact.payload['proof']['mode']`.
  String? _completedMode;

  /// When fallback occurred, the per-attempt timeline derived from
  /// `artifact.payload['modeAttempts']`. Each entry has mode/result/durationMs
  /// and possibly errorCode.
  List<Map<String, Object?>>? _modeAttempts;

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

  Future<void> _runProof() async {
    final verifierUrl = Uri.tryParse(_verifierUrlController.text.trim());
    if (verifierUrl == null) {
      setState(() {
        _status = 'Invalid verifier URL';
      });
      return;
    }

    setState(() {
      _running = true;
      _status = 'Starting provider flow...';
      _completedMode = null;
      _modeAttempts = null;
    });

    final client = MobileProofClient(bridge: MethodChannelNativeBridge());
    final StreamSubscription<SessionProgress> progressSub =
        client.subscribeProgress().listen((progress) {
      if (!mounted) return;
      setState(() {
        _status = '[${progress.phase.name}] ${progress.message}';
      });
    });

    try {
      final artifact = await client.attestProvider(
        context: context,
        providerId: 'kaggle.current_user.v1',
        providerRegistryAssetPath: _providerRegistryAssetPath,
        proofMode: _proofMode,
        transportConfig: TransportConfig(
          deploymentMode: DeploymentMode.hosted,
          verifierUrl: verifierUrl,
          trustedNotaryKeys: const <String>[
            // Replace with production notary keys.
            'REPLACE_WITH_BASE64_NOTARY_PUBLIC_KEY'
          ],
          enforceNativeCore: false,
          enableLogging: true,
          includeProofTelemetry: true,
        ),
      );
      if (!mounted) return;
      final proofMode = artifact.proofMode?.name;
      final attemptsRaw = artifact.payload['modeAttempts'];
      setState(() {
        _completedMode = proofMode;
        _modeAttempts = (attemptsRaw is List)
            ? attemptsRaw
                .whereType<Map<String, Object?>>()
                .toList(growable: false)
            : null;
        _status = 'Proof generated successfully';
      });
    } on ProofException catch (error) {
      if (!mounted) return;
      setState(() {
        _status = 'Proof failed: [${error.code.name}] ${error.message}';
      });
    } finally {
      await progressSub.cancel();
      await client.dispose();
      if (mounted) {
        setState(() {
          _running = false;
        });
      }
    }
  }

  String _formatAttempts() {
    final attempts = _modeAttempts;
    if (attempts == null || attempts.isEmpty) return '';
    return attempts.map((a) {
      final mode = a['mode']?.toString() ?? '?';
      final result = a['result']?.toString() ?? '?';
      final ms = a['durationMs'];
      final code = a['errorCode']?.toString();
      final tail = code != null ? ' ($code)' : '';
      return '$mode: $result, ${ms}ms$tail';
    }).join(' → ');
  }

  @override
  Widget build(BuildContext context) {
    final completedMode = _completedMode;
    final attemptsLine = _formatAttempts();
    return Scaffold(
      appBar: AppBar(title: const Text('mobile_proof_plugin')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            TextField(
              controller: _verifierUrlController,
              decoration: const InputDecoration(
                border: OutlineInputBorder(),
                labelText: 'Verifier URL',
              ),
            ),
            const SizedBox(height: 12),
            Row(
              children: <Widget>[
                const Text('Proof mode:'),
                const SizedBox(width: 12),
                DropdownButton<ProofMode>(
                  value: _proofMode,
                  onChanged: _running
                      ? null
                      : (m) {
                          if (m == null) return;
                          setState(() => _proofMode = m);
                        },
                  items: const <DropdownMenuItem<ProofMode>>[
                    DropdownMenuItem(
                      value: ProofMode.auto,
                      child: Text('Auto (proxy → mpc fallback)'),
                    ),
                    DropdownMenuItem(
                      value: ProofMode.proxy,
                      child: Text('Proxy (no fallback)'),
                    ),
                    DropdownMenuItem(
                      value: ProofMode.mpc,
                      child: Text('MPC (no fallback)'),
                    ),
                  ],
                ),
              ],
            ),
            const SizedBox(height: 12),
            FilledButton(
              onPressed: _running ? null : _runProof,
              child: Text(_running ? 'Running...' : 'Run Provider Attestation'),
            ),
            const SizedBox(height: 12),
            Text('Status: $_status'),
            if (completedMode != null) ...<Widget>[
              const SizedBox(height: 12),
              Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: Theme.of(context).colorScheme.primaryContainer,
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Text(
                      'Completed via: $completedMode',
                      style: Theme.of(context).textTheme.titleMedium,
                    ),
                    if (attemptsLine.isNotEmpty) ...<Widget>[
                      const SizedBox(height: 6),
                      Text('Timeline: $attemptsLine'),
                    ],
                  ],
                ),
              ),
            ],
          ],
        ),
      ),
    );
  }
}
0
likes
130
points
170
downloads

Documentation

Documentation
API reference

Publisher

verified publisherburnt.com

Weekly Downloads

Flutter plugin for TLSN-based mobile proofs with proxy/MPC auto-fallback, WebView capture, and custom provider flows.

Homepage
Repository (GitHub)
View/report issues

Topics

#tlsn #mpc #webview #privacy #proof

License

MIT (license)

Dependencies

device_info_plus, flutter, http, webview_flutter, webview_flutter_wkwebview

More

Packages that depend on mobile_proof_plugin

Packages that implement mobile_proof_plugin