fl_core_third 0.1.1
fl_core_third: ^0.1.1 copied to clipboard
Flutter toolkit for image loading, WebView, storage, date/number formatting, and encryption.
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))),
],
),
);
}
}