import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../services/api_service.dart'; // ایمپورت صفحات دیگر برای ناوبری import '../../channel_list/channel_list_screen.dart'; import '../../screens/notifications_screen.dart'; import 'create_group_page.dart'; import '../../screens/about.dart'; // مدل داده‌ای برای آیکون‌های منو class LauncherItem { final String label; final IconData? icon; final String? imagePath; final Color color; final VoidCallback onTap; LauncherItem({ required this.label, this.icon, this.imagePath, required this.color, required this.onTap, }); } class WatchLauncher extends StatefulWidget { const WatchLauncher({super.key}); @override State createState() => _WatchLauncherState(); } class _WatchLauncherState extends State { // کنترلر اسکرول final ScrollController _scrollController = ScrollController(); // ارتفاع هر آیکون final double _itemHeight = 100.0; // کانال ارتباطی با کد نیتیو static final _nativeChannel = const MethodChannel( 'com.example.watch/launcher', ); late final ApiService _api; // لیست آیکون‌های برنامه late final List _items; // متغیرهای برای ساعت و تاریخ داینامیک String _currentTime = ''; String _currentDate = ''; late StreamSubscription _timeSubscription; @override void initState() { super.initState(); _items = _generateItems(); // استفاده از Stream برای بهینه‌سازی ساعت _timeSubscription = Stream.periodic( const Duration(seconds: 1), (count) => DateTime.now(), ).listen((dateTime) { if (!mounted) return; final timeString = '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}'; final dateString = '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')}'; setState(() { _currentTime = timeString; _currentDate = dateString; }); }); } @override void dispose() { _scrollController.dispose(); _timeSubscription.cancel(); super.dispose(); } // تولید لیست آیکون‌ها List _generateItems() { return [ LauncherItem( label: 'اعلان‌ها', icon: Icons.notifications_outlined, color: const Color(0xFFFF1744), onTap: () => _navigateTo(const NotificationsScreen()), ), LauncherItem( label: 'بی‌سیم', icon: Icons.radio, color: const Color(0xFF00C853), onTap: () => _navigateTo(const ChannelListScreen()), ), LauncherItem( label: 'گروه جدید', icon: Icons.add_circle_outline, color: const Color(0xFF00C853), onTap: _showCreateGroupDialog, ), LauncherItem( label: 'تلفن', icon: Icons.phone, color: const Color(0xFF2979FF), onTap: _openDialer, ), LauncherItem( label: 'اینترنت', icon: Icons.cell_tower, color: const Color(0xFF00BCD4), onTap: _openInternetSettings, ), LauncherItem( label: 'بلوتوث', icon: Icons.bluetooth, color: const Color(0xFF304FFE), onTap: _openBluetoothSettings, ), LauncherItem( label: 'درباره ما', imagePath: 'assets/images/logo.png', color: const Color(0xFF9C27B0), onTap: () => _navigateTo(const AboutScreen()), ), LauncherItem( label: 'خروج', icon: Icons.logout, color: Colors.redAccent, onTap: _logout, ), ]; } // --- متدهای کمکی --- void _navigateTo(Widget page) { Navigator.push(context, MaterialPageRoute(builder: (_) => page)); } void _showSnack(String msg) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( msg, style: const TextStyle(fontSize: 10, color: Colors.white), ), backgroundColor: const Color(0xFF2C2C2E), duration: const Duration(seconds: 2), behavior: SnackBarBehavior.floating, margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 40), ), ); } Future _openDialer() async { try { await _nativeChannel.invokeMethod('openDialer'); } catch (_) { _showSnack('خطا در باز کردن شماره‌گیر'); } } Future _openInternetSettings() async { try { await _nativeChannel.invokeMethod('openInternetSettings'); } catch (_) { _showSnack('تنظیمات اینترنت در دسترس نیست'); } } Future _openBluetoothSettings() async { try { await _nativeChannel.invokeMethod('openBluetoothSettings'); } catch (_) { _showSnack('خطا در باز کردن بلوتوث'); } } Future _logout() async { try { if (!mounted) return; Navigator.pushReplacementNamed(context, '/login'); } catch (e) { _showSnack('خطا در خروج'); } } Future _showCreateGroupDialog() async { final groupName = await Navigator.push( context, MaterialPageRoute( builder: (ctx) => const CreateGroupPage(), fullscreenDialog: true, ), ); if (groupName != null && groupName.trim().isNotEmpty && mounted) { try { final newChannel = await _api.createGroup(groupName.trim()); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( newChannel != null ? 'گروه ساخته شد' : 'خطا در ساخت گروه', style: const TextStyle(fontSize: 11, color: Colors.white), textAlign: TextAlign.center, ), backgroundColor: const Color(0xFF2C2C2E), behavior: SnackBarBehavior.floating, duration: const Duration(seconds: 2), margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 40), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ), ); } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('خطا در ارتباط با سرور'), backgroundColor: Colors.red, ), ); } } } @override Widget build(BuildContext context) { final double screenHeight = MediaQuery.of(context).size.height; final double verticalPadding = (screenHeight / 2) - (_itemHeight / 2); return Scaffold( backgroundColor: Colors.black, body: Stack( children: [ // لایه اسکرول آیکون‌ها Positioned.fill( child: NotificationListener( onNotification: (scrollNotification) { // فقط برای Snapping نیاز به setState نیست، AnimatedBuilder کار را انجام می‌دهد if (scrollNotification is ScrollEndNotification) { _snapToItem(); } return false; }, child: ListView.builder( controller: _scrollController, physics: const BouncingScrollPhysics(), padding: EdgeInsets.symmetric(vertical: verticalPadding), itemCount: _items.length, itemBuilder: (context, index) { return _buildAnimatedItem(index); }, ), ), ), // ساعت در سمت چپ Positioned( left: 8, top: 0, bottom: 0, child: Center( child: RotatedBox( quarterTurns: 3, child: Text( _currentTime, style: const TextStyle( color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold, letterSpacing: 1, ), ), ), ), ), // تاریخ در سمت راست Positioned( right: 8, top: 0, bottom: 0, child: Center( child: RotatedBox( quarterTurns: 1, child: Text( _currentDate, style: const TextStyle(color: Colors.white70, fontSize: 13), ), ), ), ), ], ), ); } // متد ساخت آیکون با انیمیشن بهینه Widget _buildAnimatedItem(int index) { return AnimatedBuilder( animation: _scrollController, builder: (context, child) { double scrollOffset = _scrollController.hasClients ? _scrollController.offset : 0.0; double itemPosition = index * _itemHeight; double screenHeight = MediaQuery.of(context).size.height; double centerOffset = scrollOffset + (screenHeight / 2); double distanceFromCenter = (itemPosition - centerOffset).abs(); // محاسبات انیمیشن double scale = (1.6 - (distanceFromCenter / 180)).clamp(0.4, 1.6); double opacity = (1.2 - (distanceFromCenter / 250)).clamp(0.1, 1.0); // چرخش آیکون‌های دورتر برای افکت سه بعدی double rotation = (distanceFromCenter / 1000).clamp(-0.2, 0.2); // شدت سایه بر اساس فاصله از مرکز double blurRadius = (20 - (distanceFromCenter / 20)).clamp(0, 20); double shadowOpacity = (0.6 - (distanceFromCenter / 400)).clamp( 0.0, 0.6, ); return SizedBox( height: _itemHeight, child: Center( child: Transform.scale( scale: scale, child: Transform.rotate( angle: rotation, child: Opacity( opacity: opacity, child: Container( // حلقه درخشان دور آیکون وقتی وسط است decoration: BoxDecoration( shape: BoxShape.circle, boxShadow: [ BoxShadow( color: _items[index].color.withOpacity(shadowOpacity), blurRadius: blurRadius, spreadRadius: 2, ), ], ), child: _buildIconButton(_items[index]), ), ), ), ), ), ); }, ); } // متد قفل و زنجیر (Snapping) void _snapToItem() { if (!_scrollController.hasClients) return; double screenHeight = MediaQuery.of(context).size.height; double currentScroll = _scrollController.offset; double centerOffset = currentScroll + (screenHeight / 2); int targetIndex = (centerOffset / _itemHeight).round(); if (targetIndex < 0) targetIndex = 0; if (targetIndex >= _items.length) targetIndex = _items.length - 1; double targetScroll = (targetIndex * _itemHeight) - ((screenHeight / 2) - (_itemHeight / 2)); _scrollController.animateTo( targetScroll, duration: const Duration(milliseconds: 300), curve: Curves.easeOutCubic, ); } // ویجت دایره‌ای آیکون Widget _buildIconButton(LauncherItem item) { return InkWell( onTap: item.onTap, borderRadius: BorderRadius.circular(50), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 60, height: 60, decoration: BoxDecoration( shape: BoxShape.circle, color: item.color, border: Border.all(color: item.color.withOpacity(0.5), width: 2), ), child: item.imagePath != null && item.imagePath!.isNotEmpty ? Padding( padding: const EdgeInsets.all(12.0), child: Image.asset( item.imagePath!, errorBuilder: (context, error, stackTrace) { return Icon( item.icon ?? Icons.error_outline, color: Colors.white, size: 30, ); }, ), ) : Icon( item.icon ?? Icons.error_outline, color: Colors.white, size: 30, ), ), const SizedBox(height: 4), Text( item.label, style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.w500, ), ), ], ), ); } }