baiomy 0.0.1 copy "baiomy: ^0.0.1" to clipboard
baiomy: ^0.0.1 copied to clipboard

built once, used everywhere.

๐Ÿš€ Baiomy #

Built once, used everywhere.

A powerful, all-in-one Flutter toolkit for local storage, password encryption, Egyptian ID parsing, input validation, utilities, and widgets โ€” all behind a single import.

import 'package:baiomy/baiomy.dart';

๐Ÿ“ฆ What's Inside #

Module Classes / APIs
๐Ÿ—‚๏ธ Local Storage BaiomySharedPrefs ยท BaiomySecureStorage ยท StorageException
๐Ÿ” Password Encryption BaiomyPasswordEncryption ยท PasswordHasher ยท EncryptedPayload ยท HashedPassword ยท CryptoException
๐ŸŒ Egyptian ID Parser BaiomyEgyptianIdParser
๐Ÿงฉ Extensions BuildContextExtension ยท FormAutoScroll ยท EmailValidator ยท PasswordValidator ยท NotesValidator ยท DomainValidator
๐Ÿ› ๏ธ Utils BaiomyInputFormatters ยท inputDecoration()
๐ŸŽจ Widgets AppToasts ยท AvatarGlow ยท ConditionalBuilder ยท CustomSizedBox ยท CustomValueListenable ยท LoadingItem

๐Ÿ“ฅ Installation #

dependencies:
  baiomy:
    git:
      url: https://github.com/mohamedelbaiomy/baiomy.git
flutter pub get

โšก Setup โ€” once in main.dart #

import 'package:baiomy/baiomy.dart';

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

  // Required before using BaiomySharedPrefs
  await BaiomySharedPrefs.instance.init();

  // Required before using BaiomyPasswordEncryption
  BaiomyPasswordEncryption.instance.configure(keyPhrase: 'your-secret-phrase');

  runApp(const MyApp());
}

๐Ÿ—‚๏ธ Local Storage #

BaiomySharedPrefs #

Non-sensitive data โ€” settings, flags, UI state. Backed by SharedPreferences. Reads are synchronous after init().

final prefs = BaiomySharedPrefs.instance;

// โ”€โ”€ Write โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
await prefs.setString('theme', 'dark');
await prefs.setInt('launch_count', 1);
await prefs.setBool('onboarding_done', value: true);
await prefs.setDouble('font_size', 16.0);
await prefs.setStringList('tags', ['flutter', 'dart']);
await prefs.setObject('config', {'lang': 'en', 'theme': 'dark'});

// โ”€โ”€ Read โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
final theme  = prefs.getString('theme', defaultValue: 'light');
final count  = prefs.getInt('launch_count', defaultValue: 0);
final done   = prefs.getBool('onboarding_done');
final size   = prefs.getDouble('font_size');
final tags   = prefs.getStringList('tags');
final config = prefs.getObject('config');     // Map<String, dynamic>?

// โ”€โ”€ Update (key must already exist, throws otherwise) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
await prefs.updateString('theme', 'light');
await prefs.updateInt('launch_count', 2);
await prefs.updateBool('onboarding_done', newValue: false);
await prefs.updateDouble('font_size', 18.0);
await prefs.patchObject('config', {'theme': 'light'}); // partial update

// โ”€โ”€ Remove โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
await prefs.remove('theme');
await prefs.clear(); // โš ๏ธ wipes everything

// โ”€โ”€ Utility โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
prefs.containsKey('theme'); // bool
prefs.getKeys();            // Set<String>
prefs.get('theme');         // dynamic

BaiomySecureStorage #

Sensitive data โ€” tokens, passwords, PII. Encrypted at rest via platform Keychain (iOS) / Keystore (Android). All reads are async.

final secure = BaiomySecureStorage.instance;

// โ”€โ”€ Write โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
await secure.setString('access_token', 'eyJhbGci...');
await secure.setBool('biometrics_enabled', value: true);
await secure.setInt('user_id', 42);
await secure.setDouble('score', 9.5);
await secure.setObject('session', {'expires_at': 1700000000});

// โ”€โ”€ Read โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
final token   = await secure.getString('access_token');
final enabled = await secure.getBool('biometrics_enabled');
final uid     = await secure.getInt('user_id');
final session = await secure.getObject('session');

// โ”€โ”€ Update (key must already exist, throws otherwise) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
await secure.updateString('access_token', 'newToken');
await secure.updateBool('biometrics_enabled', newValue: false);
await secure.updateInt('user_id', 99);
await secure.updateDouble('score', 10.0);
await secure.patchObject('session', {'scope': 'read write'});

// โ”€โ”€ Remove โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
await secure.remove('access_token');
await secure.removeMany(['access_token', 'session']); // batch
await secure.clear(); // โš ๏ธ wipes everything

// โ”€โ”€ Utility โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
await secure.containsKey('access_token'); // Future<bool>
await secure.getKeys();                   // Future<Set<String>>
await secure.getAll();                    // Future<Map<String, String>>

๐Ÿ” Password Encryption #

Which one should I use? #

Need to recover the original password later?  โ†’  BaiomyPasswordEncryption  (AES-256 two-way)
Just need to verify it at login?              โ†’  PasswordHasher            (PBKDF2 one-way)

BaiomyPasswordEncryption โ€” Two-way AES-256-CBC #

Encrypts any string and lets you get the original value back. Every encrypt call produces a different ciphertext even for the same input because a fresh random IV is generated each time.

Configure once in main():

BaiomyPasswordEncryption.instance.configure(keyPhrase: 'your-secret-phrase');

Encrypt & store in Firestore:

final payload = BaiomyPasswordEncryption.instance.encrypt(passwordController.text);

// payload.combined   โ†’ "ivBase64:ciphertextBase64"  โ† store this
// payload.iv         โ†’ IV used, Base64-encoded
// payload.cipherText โ†’ encrypted value, Base64-encoded

await FirebaseFirestore.instance.collection('users').doc(uid).set({
  'password': payload.combined,
});

Decrypt โ€” recover the original:

final doc  = await FirebaseFirestore.instance.collection('users').doc(uid).get();
final pass = BaiomyPasswordEncryption.instance.decrypt(doc['password'] as String);

Convenience โ€” encrypt directly to a string:

final stored = BaiomyPasswordEncryption.instance.encryptToString(passwordController.text);

Validate a stored value:

BaiomyPasswordEncryption.instance.isValidPayload(stored); // bool

PasswordHasher โ€” One-way PBKDF2-HMAC-SHA256 #

Best for login systems where you never need the original password back. Uses 310,000 iterations (OWASP 2023) + a unique 32-byte random salt. Uses constant-time comparison to prevent timing attacks. The original password cannot be recovered โ€” ever.

Hash on registration:

final hashed = PasswordHasher.instance.hash(passwordController.text);

// hashed.combined   โ†’ "310000:saltBase64:hashBase64"  โ† store this
// hashed.hash       โ†’ derived key, Base64-encoded
// hashed.salt       โ†’ random salt, Base64-encoded
// hashed.iterations โ†’ 310000

await FirebaseFirestore.instance.collection('users').doc(uid).set({
  'passwordHash': hashed.combined,
});

Verify on login:

final doc = await FirebaseFirestore.instance.collection('users').doc(uid).get();

final ok = PasswordHasher.instance.verify(
  password: passwordController.text,
  combined: doc['passwordHash'] as String,
);

if (!ok) throw Exception('Wrong password');

Validate a stored hash string:

PasswordHasher.instance.isValidHash(storedValue); // bool

๐ŸŒ Egyptian ID Parser #

Parse and extract full information from a 14-digit Egyptian National ID.

final parser = BaiomyEgyptianIdParser('29901011234567');

print(parser.birthDate);    // e.g. "1999-01-01"
print(parser.governorate);  // e.g. "Cairo"
print(parser.gender);       // e.g. "Male"
print(parser.age);          // Age object

๐Ÿงฉ Extensions #

BuildContextExtension #

// Navigation
context.pop();
context.popWithValue('result');
await context.mayBePop(); // Future<bool>

// Screen dimensions
final width  = context.screenWidth;   // double
final height = context.screenHeight;  // double
final dpr    = context.devicePixelRatio; // double

FormAutoScroll #

Automatically scrolls to the first invalid field on form submission. Called as an extension on GlobalKey<FormState>:

final _formKey = GlobalKey<FormState>();

// Instead of _formKey.currentState!.validate()
final isValid = _formKey.validateAndScroll(); // bool
// Scrolls to the first field with an error if invalid

EmailValidator #

'user@gmail.com'.isValidEmail();          // true
'not-an-email'.isValidEmail();            // false
'user@uni.edu.eg'.isAcademicEmail();      // true
'user@company.com'.isCorporateEmail();    // true
'test@test.com'.hasSuspiciousEmailPattern(); // true
'user@gmail.com'.capitalize();            // 'User@gmail.com'

PasswordValidator #

'MyPass1!'.hasUppercase();                // true
'MyPass1!'.hasLowercase();                // true
'MyPass1!'.hasDigit();                    // true
'MyPass1!'.hasSpecialCharacter();         // true
'MyPass1!'.hasWhitespace();              // false
'MyPass1!'.hasMixedCase();               // true
'MyPass1!'.hasMultipleDigits();          // false
'MyPass1!'.hasMultipleSpecialChars();    // false
'password123'.isCommonPassword();         // true
'abc123'.hasSequentialCharacters();       // true
'aaabbb'.hasExcessiveRepeatedCharacters(); // true

NotesValidator #

'spam content'.hasInappropriateContent();       // true
'Hello!!!!!!'.hasExcessiveSpecialCharacters();  // true
'aaaaaaa note'.hasExcessiveRepeatedText();      // true
'Study near the library'.hasMeaningfulContent(); // true
'Valid Note.'.hasProperStructure();             // true
'Room 101 level 2'.hasSpecificDetails();        // true
'Near the faculty building'.hasLocationDetails(); // true
'Near university campus'.hasEducationalContext(); // true
'Hello world'.getWordCount();                   // 2
'Hello world'.getCharacterCountWithoutSpaces(); // 10

DomainValidator #

'flutter.dev'.isDomainValid();          // true
'mail.uni.edu.eg'.isDomainValid();      // true
'invalid'.isDomainValid();              // false
'ู…ุซุงู„.com'.isInternationalDomain();    // true

๐Ÿ› ๏ธ Utils #

BaiomyInputFormatters #

Apply as inputFormatters on any TextFormField:

// โ”€โ”€ Ready-made formatters โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
TextFormField(inputFormatters: BaiomyInputFormatters.nameField);
TextFormField(inputFormatters: BaiomyInputFormatters.phoneField);
TextFormField(inputFormatters: BaiomyInputFormatters.emailField);
TextFormField(inputFormatters: BaiomyInputFormatters.passwordField);
TextFormField(inputFormatters: BaiomyInputFormatters.notesField);
TextFormField(inputFormatters: BaiomyInputFormatters.cleanText);
TextFormField(inputFormatters: BaiomyInputFormatters.username);
TextFormField(inputFormatters: BaiomyInputFormatters.creditCard);
TextFormField(inputFormatters: BaiomyInputFormatters.currency);

// โ”€โ”€ Basic formatters โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
BaiomyInputFormatters.denyEmojis
BaiomyInputFormatters.numbersOnly
BaiomyInputFormatters.lettersOnly
BaiomyInputFormatters.alphanumericOnly
BaiomyInputFormatters.phoneNumberSafe
BaiomyInputFormatters.emailSafe
BaiomyInputFormatters.urlSafe
BaiomyInputFormatters.passwordSafe
BaiomyInputFormatters.denyProfanity

// โ”€โ”€ With length limits โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
BaiomyInputFormatters.lengthLimit(10)
BaiomyInputFormatters.nameWithLength(35)      // default 35
BaiomyInputFormatters.phoneWithLength(11)     // default 11
BaiomyInputFormatters.notesWithLength(500)    // default 500

// โ”€โ”€ Custom โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
BaiomyInputFormatters.customDeny([RegExp(r'[xyz]')], allowEmojis: false)
BaiomyInputFormatters.customAllow([RegExp(r'[0-9]')])
BaiomyInputFormatters.caseFormatter(uppercase: true)

// โ”€โ”€ Validation helpers (no TextFormField needed) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
BaiomyInputFormatters.containsEmojis('hello ๐Ÿ˜Š');      // true
BaiomyInputFormatters.isNumericOnly('12345');           // true
BaiomyInputFormatters.containsProfanity('some text');  // false
BaiomyInputFormatters.getCleanCharacterCount('hi ๐Ÿ˜Š'); // 3

inputDecoration() #

A global function that returns a styled InputDecoration:

// Underline style (default)
TextFormField(
  decoration: inputDecoration(
    'Enter your email',
    Theme.of(context),
    suffixIcon: const Icon(Icons.email),
    helperText: 'We will never share your email',
  ),
)

// Outlined style
TextFormField(
  decoration: inputDecoration(
    'Enter your password',
    Theme.of(context),
    isOutlined: true,
    suffixIcon: const Icon(Icons.lock),
  ),
)

๐ŸŽจ Widgets #

Widget files are in lib/widgets/. Refer to each file for full constructor details as the APIs depend on your local implementation.

BaiomyToast #

Quick snackbar-style notifications.

BaiomyAvatarGlow #

Avatar widget with an animated glow effect.

BaiomyConditionalBuilder #

Renders different widgets based on a condition.

CustomSizedBox #

Convenient spacing widget using extension.

BaiomyValueListenableBuilder2 #

Reactive widget that rebuilds when a ValueListenable changes.

BaiomyLoadingItem #

Loading skeleton / overlay widget (from widgets/loading/loading_item.dart).


๐Ÿ›ก๏ธ Error Handling #

Every module throws its own typed exception โ€” never a raw platform error.

// Storage errors
try {
  await BaiomySecureStorage.instance.updateString('missing_key', 'value');
} on StorageException catch (e) {
  print(e.message);    // 'Cannot update a key that does not exist.'
  print(e.key);        // 'missing_key'
  print(e.cause);      // original platform error
  print(e.stackTrace); // original stack trace
}

// Crypto errors
try {
  BaiomyPasswordEncryption.instance.decrypt('bad_format');
} on CryptoException catch (e) {
  print(e.message); // 'Decryption failed. The key may be wrong...'
  print(e.cause);   // original error
}

๐Ÿ“ Package Structure #

lib/
โ”œโ”€โ”€ egyptian_id_parser/
โ”‚   โ”œโ”€โ”€ impl/
โ”‚   โ”œโ”€โ”€ models/
โ”‚   โ”œโ”€โ”€ repo/
โ”‚   โ””โ”€โ”€ country_id_parser_base.dart    โ†’ BaiomyEgyptianIdParser
โ”œโ”€โ”€ extensions/
โ”‚   โ”œโ”€โ”€ validator/
โ”‚   โ”‚   โ”œโ”€โ”€ domain_validator.dart      โ†’ DomainValidator (extension)
โ”‚   โ”‚   โ”œโ”€โ”€ email_validator.dart       โ†’ EmailValidator (extension)
โ”‚   โ”‚   โ”œโ”€โ”€ notes_validator.dart       โ†’ NotesValidator (extension)
โ”‚   โ”‚   โ””โ”€โ”€ password_validator.dart    โ†’ PasswordValidator (extension)
โ”‚   โ”œโ”€โ”€ build_context_extensions.dart  โ†’ BuildContextExtension (extension)
โ”‚   โ””โ”€โ”€ form_auto_scroll.dart          โ†’ FormAutoScroll (extension on GlobalKey)
โ”œโ”€โ”€ local_storage/
โ”‚   โ”œโ”€โ”€ shared_preferences.dart        โ†’ BaiomySharedPrefs
โ”‚   โ”œโ”€โ”€ secure_storage.dart            โ†’ BaiomySecureStorage
โ”‚   โ””โ”€โ”€ storage_exception.dart         โ†’ StorageException
โ”œโ”€โ”€ password_encryption/
โ”‚   โ”œโ”€โ”€ password_encryption.dart       โ†’ BaiomyPasswordEncryption
โ”‚   โ”œโ”€โ”€ password_hasher.dart           โ†’ PasswordHasher
โ”‚   โ”œโ”€โ”€ encrypted_payload.dart         โ†’ EncryptedPayload
โ”‚   โ”œโ”€โ”€ hashed_password.dart           โ†’ HashedPassword
โ”‚   โ””โ”€โ”€ crypto_exception.dart          โ†’ CryptoException
โ”œโ”€โ”€ utils/
โ”‚   โ”œโ”€โ”€ app_input_formatters.dart      โ†’ BaiomyInputFormatters
โ”‚   โ”œโ”€โ”€ logger_class.dart
โ”‚   โ””โ”€โ”€ text_form_field_decoration.dart โ†’ inputDecoration()
โ”œโ”€โ”€ widgets/
โ”‚   โ”œโ”€โ”€ loading/
โ”‚   โ”‚   โ””โ”€โ”€ loading_item.dart
โ”‚   โ”œโ”€โ”€ app_toasts.dart
โ”‚   โ”œโ”€โ”€ avatar_glow.dart
โ”‚   โ”œโ”€โ”€ conditional_builder.dart
โ”‚   โ”œโ”€โ”€ custom_sized_box.dart
โ”‚   โ””โ”€โ”€ custom_value_listenable.dart
โ””โ”€โ”€ baiomy.dart

โš–๏ธ License #

Copyright (c) 2026 Mohamed Elbaiomy. All Rights Reserved.

This software is proprietary. Unauthorized copying, modification,
distribution, or use of this package, via any medium, is strictly
prohibited without prior written permission from the author.

See the LICENSE file for full details.


Built with โค๏ธ by Baiomy

1
likes
0
points
75
downloads

Publisher

unverified uploader

Weekly Downloads

built once, used everywhere.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

encrypt, flutter, flutter_secure_storage, pointycastle, shared_preferences, skeletonizer

More

Packages that depend on baiomy