fl_core_third 0.1.1 copy "fl_core_third: ^0.1.1" to clipboard
fl_core_third: ^0.1.1 copied to clipboard

Flutter toolkit for image loading, WebView, storage, date/number formatting, and encryption.

example/lib/main.dart

import 'package:flutter/material.dart';

import 'package:fl_core_third/fl_core_third.dart';

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

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

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

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

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 4,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('fl_core_third Demo'),
          bottom: const TabBar(
            isScrollable: true,
            tabs: [
              Tab(text: 'Image'),
              Tab(text: 'Format'),
              Tab(text: 'Crypto'),
              Tab(text: 'Storage'),
            ],
          ),
        ),
        body: const TabBarView(
          children: [
            _ImageTab(),
            _FormatTab(),
            _CryptoTab(),
            _StorageTab(),
          ],
        ),
      ),
    );
  }
}

// ──────────────────────────────────────────────
//  Image Tab
// ──────────────────────────────────────────────

class _ImageTab extends StatelessWidget {
  const _ImageTab();

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        Text('Network Image', style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        SizedBox(
          height: 120,
          child: FlImageView.network(
            imageUrl: 'https://picsum.photos/seed/flutter/400/200',
            fit: BoxFit.cover,
            placeholder: (ctx, url) =>
                const Center(child: CircularProgressIndicator()),
            error: (ctx, url, err) =>
                const Center(child: Icon(Icons.broken_image, size: 48)),
          ),
        ),
        const Divider(height: 32),
        Text('SVG Asset', style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        const SizedBox(
          height: 100,
          child: Center(
            child: Text('Place SVG files in example/assets/ to test.'),
          ),
        ),
        const Divider(height: 32),
        Text('Date Formatting (DateTimeExtension)',
            style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _InfoRow(
          'Formatted:',
          DateTime.now().formatDate(format: 'yyyy-MM-dd HH:mm') ?? '-',
        ),
        _InfoRow(
          'Quarter:',
          'Q${DateTime.now().quarter}',
        ),
        _InfoRow(
          'Days in month:',
          '${DateTime.now().daysInMonth}',
        ),
        _InfoRow(
          'Is leap year:',
          '${DateTime.now().isLeapYear}',
        ),
      ],
    );
  }
}

// ──────────────────────────────────────────────
//  Format Tab
// ──────────────────────────────────────────────

class _FormatTab extends StatelessWidget {
  const _FormatTab();

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        // Number Formatting
        Text('Number Formatting', style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _InfoRow('Separator:', FlNumFormatUtils.formatWithSeparator('1234567.89')),
        _InfoRow('Percent:', FlNumFormatUtils.formatPercent('0.1234')),
        _InfoRow('Compact:', FlNumFormatUtils.formatCompactNumber('1234567')),
        _InfoRow('Scientific:', FlNumFormatUtils.formatScientific('1234567')),
        _InfoRow('Currency:', FlNumFormatUtils.formatCurrency('1234.56', symbol: r'$')),
        _InfoRow('Valid numbers:', FlNumFormatUtils.formatValidNumbers('0.00001234567')),

        const Divider(height: 24),
        Text('Rounding Modes (1.2345 → 2 decimals)',
            style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _InfoRow('Round:', FlNumFormatUtils.format('1.2345')),
        _InfoRow('Floor:', FlNumFormatUtils.format('1.2345', roundingMode: FlRoundingMode.floor)),
        _InfoRow('Ceil:', FlNumFormatUtils.format('1.2345', roundingMode: FlRoundingMode.ceil)),
        _InfoRow('Truncate:', FlNumFormatUtils.format('1.2345', roundingMode: FlRoundingMode.truncate)),

        const Divider(height: 24),
        Text('High-Precision Decimal',
            style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _InfoRow('0.1 + 0.2:', FlDecimalUtils.add('0.1', '0.2')),
        _InfoRow('10 / 3 (prec=4):', FlDecimalUtils.divide('10', '3', precision: 4)),
        _InfoRow('3.14159 round(2):', FlDecimalUtils.round('3.14159', scale: 2)),

        const Divider(height: 24),
        Text('Date/Time', style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _InfoRow('Parse + Format:', FlDateTimeUtils.formatDateStr('2024-01-15', format: 'yyyy/MM/dd') ?? '-'),
        _InfoRow('Quarter:', '${FlDateTimeUtils.quarter('2024-10-01')}'),
        _InfoRow('Days in Feb 2024:', '${FlDateTimeUtils.daysInMonth('2024-02-15')} (leap)'),
        _InfoRow('UTC formatted:',
            FlDateTimeUtils.formatDate(DateTime(2024, 1, 15, 14, 30, 0), format: 'HH:mm') ?? '-'),
      ],
    );
  }
}

// ──────────────────────────────────────────────
//  Crypto Tab
// ──────────────────────────────────────────────

class _CryptoTab extends StatelessWidget {
  const _CryptoTab();

  @override
  Widget build(BuildContext context) {
    const input = 'hello';
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        Text('Hashing (input: "$input")',
            style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _InfoRow('MD5:', FlCryptoUtils.md5(input)),
        _InfoRow('SHA-1:', FlCryptoUtils.sha1(input)),
        _InfoRow('SHA-256:', FlCryptoUtils.sha256(input)),
        _InfoRow('SHA-512:', FlCryptoUtils.sha512(input)),

        const Divider(height: 24),
        Text('HMAC & Base64',
            style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _InfoRow('HMAC-SHA256:', FlCryptoUtils.hmac('secret', input)),
        _InfoRow('Base64 encode:', FlCryptoUtils.base64Encode(input)),
        _InfoRow('Base64 decode:',
            FlCryptoUtils.base64Decode('aGVsbG8=')),

        const Divider(height: 24),
        Text('String Extensions',
            style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _InfoRow(input.md5, '(MD5 via extension)'),
        _InfoRow(input.sha256, '(SHA-256 via extension)'),
        _InfoRow(input.base64Encode, '(Base64 via extension)'),

        const Divider(height: 24),
        Text('Secure Compare (constant-time)',
            style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _InfoRow('hash == same:', '${FlCryptoUtils.secureCompare('abc123', 'abc123')}'),
        _InfoRow('hash != diff:', '${FlCryptoUtils.secureCompare('abc123', 'xyz789')}'),
      ],
    );
  }
}

// ──────────────────────────────────────────────
//  Storage Tab
// ──────────────────────────────────────────────

class _StorageTab extends StatefulWidget {
  const _StorageTab();

  @override
  State<_StorageTab> createState() => _StorageTabState();
}

class _StorageTabState extends State<_StorageTab> {
  String _spValue = 'not initialized';
  String _fsValue = 'not initialized';
  final _textController = TextEditingController();

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



  Future<void> _saveSP(String value) async {
    await FlSPUtils.instance.setString('demo_key', value);
    final read = await FlSPUtils.instance.getString('demo_key');
    setState(() => _spValue = read);
  }

  Future<void> _saveFS(String value) async {
    await FlFSUtils.instance.setString('demo_key', value);
    final read = await FlFSUtils.instance.getString('demo_key');
    setState(() => _fsValue = read);
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        Text('Storage Demo', style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),

        TextField(
          controller: _textController,
          decoration: const InputDecoration(
            labelText: 'Enter a value to store',
            border: OutlineInputBorder(),
          ),
        ),
        const SizedBox(height: 12),
        Row(
          children: [
            Expanded(
              child: FilledButton(
                onPressed: () => _saveSP(_textController.text),
                child: const Text('Save to SP'),
              ),
            ),
            const SizedBox(width: 12),
            Expanded(
              child: FilledButton(
                onPressed: () => _saveFS(_textController.text),
                child: const Text('Save to FS'),
              ),
            ),
          ],
        ),
        const Divider(height: 24),
        _InfoRow('SharedPreferences:', _spValue),
        _InfoRow('SecureStorage:', _fsValue),

        const Divider(height: 24),
        Text('Device Info', style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        FutureBuilder<FlDeviceInfo>(
          future: FlDeviceInfoManager.getDeviceInfo(),
          builder: (context, snapshot) {
            if (!snapshot.hasData) {
              return const CircularProgressIndicator();
            }
            final info = snapshot.data!;
            return Column(
              children: [
                _InfoRow('OS:', '${info.osType} ${info.osVersion}'),
                _InfoRow('Model:', info.deviceModel),
                _InfoRow('Type:', info.deviceType.name),
                _InfoRow('Physical:', '${info.isPhysicalDevice}'),
              ],
            );
          },
        ),
      ],
    );
  }
}

// ──────────────────────────────────────────────
//  Shared Widget
// ──────────────────────────────────────────────

class _InfoRow extends StatelessWidget {
  final String label;
  final String value;

  const _InfoRow(this.label, this.value);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 160,
            child: Text(label, style: const TextStyle(fontWeight: FontWeight.w500)),
          ),
          Expanded(child: Text(value, style: const TextStyle(fontFamily: 'monospace', fontSize: 13))),
        ],
      ),
    );
  }
}