import 'package:flutter/material.dart'; import 'package:auto_route/auto_route.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart'; import '../../../common/theme/theme.dart'; @RoutePage() class AboutAppPage extends StatefulWidget { const AboutAppPage({super.key}); @override State createState() => _AboutAppPageState(); } class _AboutAppPageState extends State with TickerProviderStateMixin { PackageInfo? packageInfo; String deviceInfo = ''; late AnimationController _fadeController; late AnimationController _slideController; late Animation _fadeAnimation; late Animation _slideAnimation; @override void initState() { super.initState(); _initAnimations(); _loadAppInfo(); } void _initAnimations() { _fadeController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _slideController = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); _fadeAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: _fadeController, curve: Curves.easeInOut), ); _slideAnimation = Tween(begin: const Offset(0, 0.3), end: Offset.zero).animate( CurvedAnimation(parent: _slideController, curve: Curves.elasticOut), ); _fadeController.forward(); Future.delayed(const Duration(milliseconds: 300), () { _slideController.forward(); }); } Future _loadAppInfo() async { try { final info = await PackageInfo.fromPlatform(); final deviceInfoPlugin = DeviceInfoPlugin(); String device = ''; if (Theme.of(context).platform == TargetPlatform.android) { final androidInfo = await deviceInfoPlugin.androidInfo; device = '${androidInfo.brand} ${androidInfo.model}'; } else if (Theme.of(context).platform == TargetPlatform.iOS) { final iosInfo = await deviceInfoPlugin.iosInfo; device = '${iosInfo.name} ${iosInfo.model}'; } setState(() { packageInfo = info; deviceInfo = device; }); } catch (e) { print('Error loading app info: $e'); } } @override void dispose() { _fadeController.dispose(); _slideController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( physics: const BouncingScrollPhysics(), slivers: [_buildSliverAppBar(), _buildContent()], ), ); } Widget _buildSliverAppBar() { return SliverAppBar( expandedHeight: 280.0, pinned: true, elevation: 0, backgroundColor: AppColor.primary, flexibleSpace: FlexibleSpaceBar( title: AnimatedBuilder( animation: _fadeAnimation, builder: (context, child) { return Opacity( opacity: _fadeAnimation.value, child: Text( 'Tentang Aplikasi', style: AppStyle.lg.copyWith( color: AppColor.white, fontWeight: FontWeight.bold, ), ), ); }, ), background: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: AppColor.primaryGradient, ), ), child: Stack( children: [ // Background pattern Positioned.fill(child: CustomPaint(painter: _PatternPainter())), // App icon and version Center( child: FadeTransition( opacity: _fadeAnimation, child: SlideTransition( position: _slideAnimation, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 100, height: 100, decoration: BoxDecoration( color: AppColor.white, borderRadius: BorderRadius.circular(25), boxShadow: [ BoxShadow( color: AppColor.black.withOpacity(0.2), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: Icon( Icons.mobile_friendly, size: 50, color: AppColor.primary, ), ), const SizedBox(height: 16), Text( packageInfo?.appName ?? 'My App', style: AppStyle.h4.copyWith( color: AppColor.white, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: AppColor.white.withOpacity(0.2), borderRadius: BorderRadius.circular(20), ), child: Text( 'v${packageInfo?.version ?? '1.0.0'}', style: AppStyle.sm.copyWith( color: AppColor.white, fontWeight: FontWeight.w600, ), ), ), ], ), ), ), ), ], ), ), ), leading: IconButton( icon: const Icon(Icons.arrow_back_ios, color: AppColor.white), onPressed: () => context.router.back(), ), ); } Widget _buildContent() { return SliverToBoxAdapter( child: FadeTransition( opacity: _fadeAnimation, child: Container( color: AppColor.background, child: Column( children: [_buildAppInfoSection(), const SizedBox(height: 40)], ), ), ), ); } Widget _buildAppInfoSection() { return Container( margin: const EdgeInsets.all(20), padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: AppColor.white, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: AppColor.primary.withOpacity(0.1), blurRadius: 20, offset: const Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppColor.primary.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( Icons.info_outline, color: AppColor.primary, size: 24, ), ), const SizedBox(width: 16), Text( 'Informasi Aplikasi', style: AppStyle.h6.copyWith( fontWeight: FontWeight.bold, color: AppColor.primary, ), ), ], ), const SizedBox(height: 20), _buildInfoRow('Nama Aplikasi', packageInfo?.appName ?? 'Loading...'), _buildInfoRow('Versi', packageInfo?.version ?? 'Loading...'), _buildInfoRow( 'Build Number', packageInfo?.buildNumber ?? 'Loading...', ), _buildInfoRow( 'Package Name', packageInfo?.packageName ?? 'Loading...', ), _buildInfoRow( 'Device', deviceInfo.isEmpty ? 'Loading...' : deviceInfo, ), ], ), ); } Widget _buildInfoRow(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 120, child: Text( label, style: AppStyle.md.copyWith( fontWeight: FontWeight.w600, color: AppColor.textSecondary, ), ), ), const SizedBox(width: 16), Expanded( child: Text( value, style: AppStyle.md.copyWith(color: AppColor.textPrimary), ), ), ], ), ); } } class _PatternPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = AppColor.white.withOpacity(0.1) ..strokeWidth = 1 ..style = PaintingStyle.stroke; // Create geometric pattern for (int i = 0; i < 20; i++) { for (int j = 0; j < 20; j++) { final x = (size.width / 20) * i; final y = (size.height / 20) * j; if ((i + j) % 3 == 0) { canvas.drawCircle(Offset(x, y), 3, paint); } } } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; }