commonkit 0.1.6
commonkit: ^0.1.6 copied to clipboard
CommonKit: Lightweight, WASM-compatible package with widgets, helpers, and extensions for streamlined app development, UI components, and utilities.
import 'package:flutter/material.dart';
import 'package:commonkit/commonkit.dart';
import 'dart:io' if (dart.library.io) 'dart:io';
import 'package:image_picker/image_picker.dart'
if (dart.library.io) 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart'
if (dart.library.io) 'package:permission_handler/permission_handler.dart';
/// Initializes the app with global configuration and runs the [CommonKitExampleApp].
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize storage for non-web platforms (WASM-safe)
if (!kIsWeb) {
await StorageHelper.init();
}
// Set up global configuration with theme, API base URL, and session data
await GlobalConfig().init(
baseUrl: 'https://jsonplaceholder.typicode.com',
variables: {'apiKey': '12345'},
isDebugMode: true,
theme: CommonKitTheme(
primaryColor: Colors.teal,
secondaryColor: Colors.grey[800]!,
accentColor: Colors.tealAccent,
backgroundColor: Colors.grey[900]!,
textColor: Colors.white,
highContrast: true,
isDarkMode: true,
borderRadius: 12.0,
animationDuration: Duration(milliseconds: 400),
),
locale: Locale('en', 'US'),
apiTimeout: Duration(seconds: 20),
environment: 'development',
isAnalyticsEnabled: false,
);
runApp(const CommonKitExampleApp());
}
/// The main application demonstrating `commonkit` features.
///
/// This app provides a tabbed interface to test widgets, helpers, extensions, and
/// configurations, with full WASM compatibility for web platforms. Non-web features
/// (e.g., file uploads, directory management) are disabled on web with user-friendly
/// feedback via toasts.
///
/// Run this app to explore:
/// - Themed UI with `CommonKitTheme`
/// - Network requests and image caching
/// - Session management and form validation
/// - Clipboard, animations, and more
class CommonKitExampleApp extends StatelessWidget {
const CommonKitExampleApp({super.key});
@override
Widget build(BuildContext context) {
final theme = GlobalConfig().theme ?? CommonKitTheme();
return MaterialApp(
theme: ThemeData(
primaryColor: theme.primaryColor,
scaffoldBackgroundColor: theme.backgroundColor,
textTheme: TextTheme(
bodyMedium:
theme.bodyTextStyle ??
TextStyle(color: theme.textColor, fontSize: 16),
titleLarge:
theme.headingTextStyle ??
TextStyle(
color: theme.textColor,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
buttonTheme: ButtonThemeData(
textTheme: ButtonTextTheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(theme.borderRadius),
),
),
brightness: theme.isDarkMode ? Brightness.dark : Brightness.light,
),
home: const ExamplePage(),
);
}
}
/// A tabbed page showcasing `commonkit` features in an organized, user-friendly way.
///
/// Tabs include:
/// - **Network & Media**: Test network requests, image caching, and file uploads.
/// - **Session & Forms**: Explore session management and form validation.
/// - **System & Utils**: Try directory management, permissions, clipboard, and animations.
///
/// Each feature includes status updates and toasts for clear feedback, with non-web
/// features disabled on WASM platforms.
class ExamplePage extends StatefulWidget {
const ExamplePage({super.key});
@override
State<ExamplePage> createState() => _ExamplePageState();
}
class _ExamplePageState extends State<ExamplePage>
with SingleTickerProviderStateMixin {
// Helpers and managers
final _networkHelper = NetworkHelper();
final _directoryManager = kIsWeb ? null : DirectoryManager();
final _sessionManager = SessionManager();
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
String _status = 'Ready';
bool _isLoading = false;
// Animation controller for fade effects
late AnimationController _animationController;
late Animation<double> _fadeAnimation;
@override
void initState() {
super.initState();
// Initialize animation for UI effects
_animationController = AnimationController(
vsync: this,
duration:
GlobalConfig().theme?.animationDuration ??
Duration(milliseconds: 400),
);
_fadeAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
);
_animationController.forward();
// Preload an image to demonstrate ImageCacheHelper
WidgetsBinding.instance.addPostFrameCallback((_) {
_preloadImage();
});
}
@override
void dispose() {
_emailController.dispose();
_animationController.dispose();
super.dispose();
}
/// Preloads a sample image for performance
Future<void> _preloadImage() async {
if (!mounted) return;
setState(() => _status = 'Preloading image...');
final failed = await ImageCacheHelper.preloadImages(
['https://via.placeholder.com/150'],
context,
onProgress:
(loaded, total) => setState(() => _status = 'Images: $loaded/$total'),
);
if (!mounted) return;
setState(
() =>
_status =
failed.isEmpty ? 'Image preloaded' : 'Preload failed: $failed',
);
if (failed.isEmpty) {
showToast(context, message: 'Image Cached');
}
}
/// Tests a network GET request
Future<void> _testNetwork() async {
if (!mounted) return;
setState(() {
_isLoading = true;
_status = 'Fetching data...';
});
try {
final data = await _networkHelper.get('/posts/1');
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Title: ${(data['title'] as String).capitalize()}';
});
Logger.info('Network test succeeded');
showToast(context, message: 'Network Success');
} catch (e) {
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Network Error: $e';
});
Logger.error('Network test failed', e);
showCustomDialog(context, title: 'Error', content: e.toString());
}
}
/// Tests file upload with ImagePicker (non-web only)
Future<void> _testFileUpload() async {
if (kIsWeb) {
setState(() => _status = 'File upload not supported on web');
showToast(context, message: 'Feature unavailable on web');
return;
}
setState(() {
_isLoading = true;
_status = 'Picking image...';
});
try {
final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile == null) {
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'No image selected';
});
return;
}
final file = File(pickedFile.path);
setState(() => _status = 'Uploading...');
final data = await _networkHelper.post(
'/upload',
body: {'title': 'Example Upload'},
files: [file],
);
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Upload: $data';
});
Logger.info('File upload succeeded');
showToast(context, message: 'Upload Success');
} catch (e) {
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Upload Error: $e';
});
Logger.error('File upload failed', e);
showToast(context, message: 'Upload Error');
}
}
/// Tests directory management (non-web only)
Future<void> _testDirectory() async {
if (kIsWeb || _directoryManager == null) {
setState(() => _status = 'Directory management not supported on web');
showToast(context, message: 'Feature unavailable on web');
return;
}
setState(() {
_isLoading = true;
_status = 'Creating directory...';
});
try {
final dir = await _directoryManager.createDirectory('example_dir');
await _directoryManager.createFile(
'example_dir/test.txt',
content: 'Example',
);
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Directory: ${dir.path}';
});
Logger.info('Directory test succeeded');
showToast(context, message: 'Directory Created');
} catch (e) {
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Directory Error: $e';
});
Logger.error('Directory test failed', e);
showToast(context, message: 'Directory Error');
}
}
/// Tests clipboard operations
Future<void> _testClipboard() async {
setState(() => _status = 'Testing clipboard...');
try {
await ClipboardManager.copy('Example Text');
final pasted = await ClipboardManager.paste();
if (!mounted) return;
setState(() => _status = 'Clipboard: $pasted');
Logger.info('Clipboard test succeeded');
showToast(context, message: 'Copied and Pasted');
} catch (e) {
if (!mounted) return;
setState(() => _status = 'Clipboard Error: $e');
Logger.error('Clipboard test failed', e);
showToast(context, message: 'Clipboard Error');
}
}
/// Tests permission handling (non-web only)
Future<void> _testPermission() async {
if (kIsWeb) {
setState(() => _status = 'Permissions not supported on web');
showToast(context, message: 'Feature unavailable on web');
return;
}
setState(() {
_isLoading = true;
_status = 'Requesting permission...';
});
try {
final granted = await PermissionManager.request(Permission.storage);
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Permission: ${granted ? "Granted" : "Denied"}';
});
Logger.info('Permission test: ${granted ? "Granted" : "Denied"}');
showCustomDialog(
context,
title: 'Permission',
content: granted ? 'Storage granted' : 'Storage denied',
negativeText: granted ? null : 'Settings',
onNegative: granted ? null : () => PermissionManager.openSettings(),
);
} catch (e) {
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Permission Error: $e';
});
Logger.error('Permission test failed', e);
showToast(context, message: 'Permission Error');
}
}
/// Tests session management with login/logout
Future<void> _testSession() async {
setState(() {
_isLoading = true;
_status = 'Logging in...';
});
try {
await _sessionManager.login(
username: 'test_user',
password: 'password123',
);
await GlobalConfig().updateSession(
username: 'test_user',
token: 'abc123',
expiry: DateTime.now().add(Duration(days: 1)),
);
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Logged in as ${GlobalConfig().username}';
});
Logger.info('Session test succeeded');
showToast(context, message: 'Login Success');
} catch (e) {
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Session Error: $e';
});
Logger.error('Session test failed', e);
showToast(context, message: 'Login Error');
}
}
/// Tests form validation with email input
Future<void> _testValidation() async {
if (_formKey.currentState!.validate()) {
setState(() => _status = 'Email valid: ${_emailController.text}');
showToast(context, message: 'Validation Success');
} else {
setState(() => _status = 'Invalid email');
showToast(context, message: 'Validation Failed');
}
}
/// Tests image picking with ImagePicker (non-web only)
Future<void> _testImagePicker() async {
if (kIsWeb) {
setState(() => _status = 'Image picker not supported on web');
showToast(context, message: 'Feature unavailable on web');
return;
}
setState(() {
_isLoading = true;
_status = 'Picking image...';
});
try {
final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile == null) {
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'No image selected';
});
return;
}
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Image: ${pickedFile.path}';
});
Logger.info('Image picker succeeded');
showToast(context, message: 'Image Picked');
} catch (e) {
if (!mounted) return;
setState(() {
_isLoading = false;
_status = 'Image Picker Error: $e';
});
Logger.error('Image picker failed', e);
showToast(context, message: 'Image Picker Error');
}
}
/// Tests animation helper with a fade effect
Future<void> _testAnimation() async {
setState(() => _status = 'Starting animation...');
_animationController.reset();
await _animationController.forward();
if (!mounted) return;
setState(() => _status = 'Animation completed');
showToast(context, message: 'Animation Success');
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: Text(
'CommonKit Demo',
style: Theme.of(context).textTheme.titleLarge,
),
bottom: const TabBar(
tabs: [
Tab(text: 'Network & Media'),
Tab(text: 'Session & Forms'),
Tab(text: 'System & Utils'),
],
),
),
body: Stack(
children: [
TabBarView(
children: [
// Network & Media Tab
SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: FadeTransition(
opacity: _fadeAnimation,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Test network, images, and uploads',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 16),
CustomButton(
text: 'Test Network Request',
onPressed: _testNetwork,
icon: Icons.network_check,
),
const SizedBox(height: 16),
CustomButton(
text: 'Test File Upload',
onPressed: _testFileUpload,
icon: Icons.upload_file,
isDisabled: kIsWeb,
),
const SizedBox(height: 16),
CustomButton(
text: 'Test Image Picker',
onPressed: _testImagePicker,
icon: Icons.image,
isDisabled: kIsWeb,
),
const SizedBox(height: 16),
Image.network(
'https://via.placeholder.com/150',
width: 150,
height: 150,
loadingBuilder:
(context, child, progress) =>
progress == null
? child
: const CircularProgressIndicator(),
),
const SizedBox(height: 16),
Text(
'Status: $_status',
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
),
// Session & Forms Tab
SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: FadeTransition(
opacity: _fadeAnimation,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Test session and validation',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 16),
CustomButton(
text: 'Test Session Login',
onPressed: _testSession,
icon: Icons.person,
),
const SizedBox(height: 16),
Form(
key: _formKey,
child: TextFormField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
validator:
(value) =>
Validators.email(value ?? '') != null
? null
: 'Invalid email address',
),
),
const SizedBox(height: 16),
CustomButton(
text: 'Validate Email',
onPressed: _testValidation,
icon: Icons.check_circle,
),
const SizedBox(height: 16),
Text(
'Status: $_status',
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
),
// System & Utils Tab
SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: FadeTransition(
opacity: _fadeAnimation,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Test system features and utilities',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 16),
CustomButton(
text: 'Test Directory Management',
onPressed: _testDirectory,
icon: Icons.folder,
isDisabled: kIsWeb,
),
const SizedBox(height: 16),
CustomButton(
text: 'Test Permission',
onPressed: _testPermission,
icon: Icons.security,
isDisabled: kIsWeb,
),
const SizedBox(height: 16),
CustomButton(
text: 'Test Clipboard',
onPressed: _testClipboard,
icon: Icons.copy,
),
const SizedBox(height: 16),
CustomButton(
text: 'Test Animation',
onPressed: _testAnimation,
icon: Icons.animation,
),
const SizedBox(height: 16),
Text(
'Status: $_status',
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
),
],
),
if (_isLoading)
LoadingOverlay(
isLoading: _isLoading,
overlayColor: GlobalConfig().theme?.loadingOverlayColor,
progressIndicatorColor:
GlobalConfig().theme?.loadingSpinnerColor,
),
],
),
),
);
}
}