vault_kit 1.0.4 copy "vault_kit: ^1.0.4" to clipboard
vault_kit: ^1.0.4 copied to clipboard

Secure credential storage for Flutter using Android Keystore (AES-256-GCM) and iOS Keychain.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:vault_kit/vault_kit.dart';

void main() => runApp(const VaultKitExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'VaultKit Example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
        useMaterial3: true,
      ),
      home: const VaultKitExampleScreen(),
    );
  }
}

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

  @override
  State<VaultKitExampleScreen> createState() => _VaultKitExampleScreenState();
}

class _VaultKitExampleScreenState extends State<VaultKitExampleScreen> {
  final _vault = VaultKit();

  final _tokenController = TextEditingController();
  final _usernameController = TextEditingController();
  final _passwordController = TextEditingController();

  String _log = 'Logs will appear here...';
  bool _hasToken = false;
  bool _hasCredential = false;

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

  @override
  void dispose() {
    _tokenController.dispose();
    _usernameController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  // -------------------------------------------------------
  // ๐Ÿ” Check stored values on launch
  // -------------------------------------------------------

  Future<void> _checkStoredValues() async {
    final hasToken = await _vault.has(key: 'auth_token');
    final hasCredentials = await _vault.has(key: 'user_credentials'); // ๐Ÿ‘ˆ
    setState(() {
      _hasToken = hasToken;
      _hasCredential = hasCredentials;
    });
  }

  // -------------------------------------------------------
  // ๐Ÿ’พ Save
  // -------------------------------------------------------

  Future<void> _saveToken() async {
    final value = _tokenController.text.trim();
    if (value.isEmpty) {
      _appendLog('โŒ Token field is empty');
      return;
    }
    try {
      await _vault.save(key: 'auth_token', value: value);
      _appendLog('โœ… Token saved successfully');
      _tokenController.clear();
      await _checkStoredValues();
    } on PlatformException catch (e) {
      _appendLog('โŒ [${e.code}] ${e.message}');
    }
  }

  Future<void> _saveUserCredentials() async {
    final username = _usernameController.text;
    final password = _passwordController.text;
    if (username.isEmpty || password.isEmpty) {
      _appendLog('โŒ Username and password are required');
      return;
    }
    try {
      await _vault.save(
        key: 'user_credentials',
        value: UserCredentialsModel(userName: username, password: password)
            .toJson(),
      );
      _appendLog('โœ… Credentials saved successfully');
      _usernameController.clear();
      _passwordController.clear();
      await _checkStoredValues();
    } on PlatformException catch (e) {
      _appendLog('โŒ [${e.code}] ${e.message}');
    }
  }

  // -------------------------------------------------------
  // ๐Ÿ“ฆ Fetch
  // -------------------------------------------------------

  Future<String?> _fetchToken() async {
    try {
      final token = await _vault.fetch<String>(key: 'auth_token');
      _appendLog(token != null ? '๐Ÿ”“ Token: $token' : 'โš ๏ธ No token stored');
      return token;
    } on PlatformException catch (e) {
      _appendLog('โŒ [${e.code}] ${e.message}');
      rethrow;
    }
  }

  Future<UserCredentialsModel?> _fetchUserCredentials() async {
    _checkStoredValues(); // Ensure we have the latest stored status
    if (_hasCredential == false) {
      _appendLog('โš ๏ธ No credentials stored');
      return null;
    }
    try {
      final result = await _vault.fetch<UserCredentialsModel?>(
        key: 'user_credentials',
        fromJson: (p) =>
            UserCredentialsModel.fromJson(p as Map<String, dynamic>),
      );
      if (result == null) {
        _appendLog('โš ๏ธ No credentials stored');
        return null;
      } else {
        _appendLog('๐Ÿ”“ Username: ${result.userName}');
        _appendLog('๐Ÿ”“ Password: ${result.password}');
        return result;
      }
    } on PlatformException catch (e) {
      _appendLog('โŒ [${e.code}] ${e.message}');
      rethrow;
    }
  }

  // -------------------------------------------------------
  // ๐Ÿ—‘ Delete
  // -------------------------------------------------------

  Future<void> _deleteToken() async {
    try {
      await _checkStoredValues();
      if (_hasToken == false) {
        _appendLog('โš ๏ธ No token to delete');
        return;
      }
      await _vault.delete(key: 'auth_token');
      _appendLog('๐Ÿ—‘ Token deleted');
    } on PlatformException catch (e) {
      _appendLog('โŒ [${e.code}] ${e.message}');
    }
  }

  Future<void> _deleteUserCredentials() async {
    try {
      await _vault.delete(key: 'user_credentials');
      _appendLog('๐Ÿ—‘ Credentials deleted');
      await _checkStoredValues();
    } on PlatformException catch (e) {
      _appendLog('โŒ [${e.code}] ${e.message}');
    }
  }

  // -------------------------------------------------------
  // ๐Ÿงน Clear All
  // -------------------------------------------------------

  Future<void> _clearAll() async {
    try {
      await _vault.clearAll();
      _appendLog('๐Ÿงน All credentials cleared');
      await _checkStoredValues();
    } on PlatformException catch (e) {
      _appendLog('โŒ [${e.code}] ${e.message}');
    }
  }

  // -------------------------------------------------------
  // ๐Ÿ›  Helpers
  // -------------------------------------------------------

  void _appendLog(String message) {
    setState(() {
      _log =
          '${DateTime.now().toLocal().toString().substring(11, 19)} โ€” $message\n$_log';
    });
  }

  // -------------------------------------------------------
  // ๐ŸŽจ UI
  // -------------------------------------------------------

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('๐Ÿ” VaultKit Example'),
        centerTitle: true,
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // โ”€โ”€ Token Section โ”€โ”€
            _SectionCard(
              title: '๐Ÿช™ Auth Token',
              stored: _hasToken,
              child: Column(
                children: [
                  TextField(
                    controller: _tokenController,
                    decoration: const InputDecoration(
                      labelText: 'Enter token',
                      border: OutlineInputBorder(),
                      prefixIcon: Icon(Icons.token),
                    ),
                  ),
                  const SizedBox(height: 12),
                  Row(
                    children: [
                      Expanded(
                        child: FilledButton.icon(
                          onPressed: _saveToken,
                          icon: const Icon(Icons.save),
                          label: const Text('Save'),
                        ),
                      ),
                      const SizedBox(width: 8),
                      Expanded(
                        child: FilledButton.icon(
                          onPressed: _fetchToken,
                          icon: const Icon(Icons.download),
                          label: const Text('Fetch'),
                        ),
                      ),
                      const SizedBox(width: 8),
                      IconButton.filled(
                        onPressed: _deleteToken,
                        icon: const Icon(Icons.delete),
                        color: Colors.white,
                        style:
                            IconButton.styleFrom(backgroundColor: Colors.red),
                      ),
                    ],
                  ),
                ],
              ),
            ),

            const SizedBox(height: 16),

            // โ”€โ”€ User Credentials Section โ”€โ”€
            _SectionCard(
              title: '๐Ÿ‘ค User Credentials',
              stored: _hasCredential,
              child: Column(
                children: [
                  TextField(
                    controller: _usernameController,
                    decoration: const InputDecoration(
                      labelText: 'Enter username',
                      border: OutlineInputBorder(),
                      prefixIcon: Icon(Icons.person),
                    ),
                  ),
                  const SizedBox(height: 12),
                  TextField(
                    controller: _passwordController,
                    obscureText: true,
                    decoration: const InputDecoration(
                      labelText: 'Enter password',
                      border: OutlineInputBorder(),
                      prefixIcon: Icon(Icons.lock),
                    ),
                  ),
                  const SizedBox(height: 12),
                  Row(
                    children: [
                      Expanded(
                        child: FilledButton.icon(
                          onPressed: _saveUserCredentials,
                          icon: const Icon(Icons.save),
                          label: const Text('Save'),
                        ),
                      ),
                      const SizedBox(width: 8),
                      Expanded(
                        child: FilledButton.icon(
                          onPressed: _fetchUserCredentials,
                          icon: const Icon(Icons.download),
                          label: const Text('Fetch'),
                        ),
                      ),
                      const SizedBox(width: 8),
                      IconButton.filled(
                        onPressed: _deleteUserCredentials,
                        icon: const Icon(Icons.delete),
                        color: Colors.white,
                        style:
                            IconButton.styleFrom(backgroundColor: Colors.red),
                      ),
                    ],
                  ),
                ],
              ),
            ),
            const SizedBox(height: 16),

            // โ”€โ”€ Clear All โ”€โ”€
            FilledButton.icon(
              onPressed: _clearAll,
              icon: const Icon(Icons.delete_sweep),
              label: const Text('Clear All โ€” Simulate Logout'),
              style: FilledButton.styleFrom(
                backgroundColor: Colors.red,
                padding: const EdgeInsets.symmetric(vertical: 14),
              ),
            ),

            const SizedBox(height: 24),

            // โ”€โ”€ Log Output โ”€โ”€
            Container(
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: Colors.black87,
                borderRadius: BorderRadius.circular(12),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      const Text(
                        '๐Ÿ“‹ Logs',
                        style: TextStyle(
                          color: Colors.white70,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      GestureDetector(
                        onTap: () => setState(() => _log = 'Logs cleared.'),
                        child: const Text(
                          'Clear logs',
                          style: TextStyle(color: Colors.white38, fontSize: 12),
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  Text(
                    _log,
                    style: const TextStyle(
                      color: Colors.greenAccent,
                      fontFamily: 'monospace',
                      fontSize: 12,
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// -------------------------------------------------------
// ๐ŸŽจ Section Card Widget
// -------------------------------------------------------

class _SectionCard extends StatelessWidget {
  final String title;
  final bool stored;
  final Widget child;

  const _SectionCard({
    required this.title,
    required this.stored,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(
                  title,
                  style: const TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                  decoration: BoxDecoration(
                    color:
                        stored ? Colors.green.shade100 : Colors.grey.shade200,
                    borderRadius: BorderRadius.circular(20),
                  ),
                  child: Text(
                    stored ? 'โœ… Stored' : 'โšช Empty',
                    style: TextStyle(
                      fontSize: 12,
                      color: stored ? Colors.green.shade800 : Colors.grey,
                      fontWeight: FontWeight.w500,
                    ),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 16),
            child,
          ],
        ),
      ),
    );
  }
}

class UserCredentialsModel {
  const UserCredentialsModel({required this.userName, required this.password});

  final String userName;
  final String password;

  Map<String, dynamic> toJson() {
    return {
      'username': userName,
      'password': password,
    };
  }

  factory UserCredentialsModel.fromJson(Map<String, dynamic> json) {
    return UserCredentialsModel(
      userName: json['username'],
      password: json['password'],
    );
  }
}
1
likes
150
points
171
downloads

Publisher

verified publishershiba-n8n.cloud

Weekly Downloads

Secure credential storage for Flutter using Android Keystore (AES-256-GCM) and iOS Keychain.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on vault_kit

Packages that implement vault_kit