Skip to main content

tk_ota_push 0.2.2 copy "tk_ota_push: ^0.2.2" to clipboard
tk_ota_push: ^0.2.2 copied to clipboard

Sovereign OTA code push for Flutter with a 3-step Remote Config Gatekeeper, bank-grade security shield, and multiplatform deployment CLI.

example/lib/main.dart

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 1. Initialiser le SDK mobile avec les politiques de sécurité bancaires
  TkOtaPush.init(
    appReleaseVersion: '1.0.0+1',
    requireWifiForSilentUpdate: false, // true pour forcer le Wi-Fi en mode éco-data
    minBatteryPercentageForSilentUpdate: 20,
    securityPolicy: const OtaSecurityPolicy(
      blockRootedOrJailbrokenDevices: false, // Activer en production bancaire
      expectedApkSignaturesSha256: {
        // Empreinte SHA-256 du certificat officiel de la banque
        '6b6929c8ff345cf008496328d7bff081a8ccfb89498bf9916ed668be3c3dedcf',
      },
      pinnedServerCertSha256Fingerprints: {
        // Empreinte TLS Let's Encrypt / Serveur souverain
        '42d889f8f4b6bf9d3abb767d3b5f215e0009e4d26cb7b958497e8b291e1b96dc',
      },
    ),
    onRollbackDetected: (event) async {
      debugPrint('⚠️ ROLLBACK DÉTECTÉ : #${event.previousPatchNumber} -> #${event.restoredPatchNumber}');
      // Nettoyer le cache local (SQLite / SharedPreferences / Hive)
    },
    onSecurityViolation: (verdict) async {
      debugPrint('🚨 ALERTE SÉCURITÉ MOBILE : ${verdict.reason}');
    },
  );

  runApp(const TkOtaPushExampleApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'tk_ota_push Démo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF0F766E), // Teal FinTech
          brightness: Brightness.light,
        ),
      ),
      home: const OtaDashboardScreen(),
    );
  }
}

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

  @override
  State<OtaDashboardScreen> createState() => _OtaDashboardScreenState();
}

class _OtaDashboardScreenState extends State<OtaDashboardScreen> {
  final TkOtaPush _ota = TkOtaPush.instance;
  int? _currentPatch;
  String _statusMessage = 'Prêt';
  bool _isLoading = false;

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

  Future<void> _loadCurrentPatch() async {
    final patch = await _ota.currentPatchNumber();
    setState(() => _currentPatch = patch);
  }

  /// Démonstration 1 : Simulation Gatekeeper Silencieux
  Future<void> _testSilentGatekeeper() async {
    setState(() {
      _isLoading = true;
      _statusMessage = 'Évaluation Gatekeeper (Mode Silencieux)...';
    });

    final config = OtaRemoteConfig(
      targetReleaseVersion: '1.0.0+1',
      patchAvailable: true,
      latestPatchNumber: (_currentPatch ?? 0) + 1,
      forceUpdate: false,
      releaseNotes: 'Optimisation de la vitesse de transaction.',
    );

    final outcome = await _ota.syncWithRemoteConfig(config);

    setState(() {
      _isLoading = false;
      _statusMessage = outcome.message ?? 'Synchronisation terminée.';
    });
    await _loadCurrentPatch();
  }

  /// Démonstration 2 : Simulation Gatekeeper Critique Forcé
  Future<void> _testCriticalGatekeeper() async {
    final config = OtaRemoteConfig(
      targetReleaseVersion: '1.0.0+1',
      patchAvailable: true,
      latestPatchNumber: (_currentPatch ?? 0) + 1,
      forceUpdate: true,
      releaseNotes: 'Correctif de sécurité critique obligatoire.',
    );

    await _showCriticalUpdateDialog(config);
  }

  /// Affiche la boîte de dialogue bloquante de mise à jour critique
  Future<void> _showCriticalUpdateDialog(OtaRemoteConfig config) async {
    return showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (ctx) {
        var downloading = false;
        return StatefulBuilder(
          builder: (context, setModalState) {
            return AlertDialog(
              icon: const Icon(Icons.security_update_warning, size: 48, color: Colors.amber),
              title: const Text('Mise à Jour Critique Requise'),
              content: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    config.releaseNotes ?? 'Un correctif critique de sécurité doit être installé.',
                    style: const TextStyle(fontSize: 14),
                  ),
                  const SizedBox(height: 16),
                  if (downloading) ...[
                    const LinearProgressIndicator(),
                    const SizedBox(height: 8),
                    const Center(child: Text('Téléchargement du patch souverain...')),
                  ],
                ],
              ),
              actions: [
                if (!downloading)
                  FilledButton.icon(
                    icon: const Icon(Icons.download),
                    label: const Text('Mettre à jour maintenant'),
                    onPressed: () async {
                      setModalState(() => downloading = true);
                      await _ota.checkAndUpdate();
                      if (context.mounted) {
                        Navigator.of(context).pop();
                        ScaffoldMessenger.of(context).showSnackBar(
                          const SnackBar(
                            content: Text('Patch installé ! Redémarrez l\'application.'),
                            backgroundColor: Colors.green,
                          ),
                        );
                        _loadCurrentPatch();
                      }
                    },
                  ),
              ],
            );
          },
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('tk_ota_push Démo'),
        actions: [
          IconButton(
            tooltip: 'Canal QA / Staging',
            icon: Icon(_ota.track == UpdateTrack.staging ? Icons.science : Icons.verified_user),
            onPressed: () {
              setState(() {
                if (_ota.track == UpdateTrack.staging) {
                  _ota.disableQaMode();
                } else {
                  _ota.enableQaMode();
                }
              });
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Canal actif : ${_ota.track.name}')),
              );
            },
          ),
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Carte Statut Courant
            Card(
              elevation: 2,
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  children: [
                    const Text('STATUT DU MOTEUR OTA', style: TextStyle(fontWeight: FontWeight.bold, letterSpacing: 1.2)),
                    const Divider(),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        const Text('Version Installée :'),
                        Text(_ota.appReleaseVersion, style: const TextStyle(fontWeight: FontWeight.bold)),
                      ],
                    ),
                    const SizedBox(height: 8),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        const Text('Numéro de Patch Actif :'),
                        Chip(
                          avatar: const Icon(Icons.tag, size: 16),
                          label: Text(_currentPatch == null ? 'Binaire Base (Patch 0)' : 'Patch #$_currentPatch'),
                          backgroundColor: _currentPatch == null ? Colors.grey.shade200 : Colors.teal.shade100,
                        ),
                      ],
                    ),
                    const SizedBox(height: 8),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        const Text('Canal Déployé :'),
                        Text(_ota.track.name.toUpperCase(), style: const TextStyle(color: Colors.teal, fontWeight: FontWeight.bold)),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 24),

            // Actions de Démonstration Gatekeeper
            const Text('TESTS DU REMOTE CONFIG GATEKEEPER', style: TextStyle(fontWeight: FontWeight.bold)),
            const SizedBox(height: 8),
            ElevatedButton.icon(
              icon: const Icon(Icons.cloud_sync),
              label: const Text('Tester Mode Silencieux (Eco-Data)'),
              onPressed: _isLoading ? null : _testSilentGatekeeper,
            ),
            const SizedBox(height: 8),
            FilledButton.tonalIcon(
              icon: const Icon(Icons.warning_amber),
              label: const Text('Tester Mode Critique Forcé (Modale Bloquante)'),
              onPressed: _isLoading ? null : _testCriticalGatekeeper,
            ),
            const SizedBox(height: 16),

            // Boîte de log / résultat
            Expanded(
              child: Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: const Color(0x0A000000),
                  borderRadius: BorderRadius.circular(8),
                  border: Border.all(color: Colors.black12),
                ),
                child: SingleChildScrollView(
                  child: Text(
                    _statusMessage,
                    style: const TextStyle(fontFamily: 'monospace', fontSize: 13),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
1
likes
155
points
--
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Sovereign OTA code push for Flutter with a 3-step Remote Config Gatekeeper, bank-grade security shield, and multiplatform deployment CLI.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

args, crypto, http, path, shorebird_code_push, yaml

More

Packages that depend on tk_ota_push