part of 'main.dart'; class PersonsPage extends StatelessWidget { const PersonsPage({ super.key, required this.people, required this.events, required this.onAdd, required this.onEdit, required this.onDelete, }); final List people; final List events; final VoidCallback onAdd; final ValueChanged onEdit; final ValueChanged onDelete; @override Widget build(BuildContext context) { return PageFrame( eyebrow: '${people.length} csal\u00e1dtag', title: 'Csal\u00e1dtagok', action: FilledButton.icon( onPressed: onAdd, icon: const Icon(Icons.add), label: const Text('\u00daj csal\u00e1dtag'), ), child: people.isEmpty ? const EmptyState( icon: Icons.group_add_outlined, title: 'M\u00e9g nincs csal\u00e1dtag', subtitle: 'Vegy\u00e9l fel csal\u00e1dtagokat, gyerekeket vagy b\u00e1rkit.', ) : ListView.separated( padding: const EdgeInsets.only(bottom: 24), itemCount: people.length, separatorBuilder: (_, _) => const SizedBox(height: 10), itemBuilder: (_, i) { final person = people[i]; final personEvents = events .where((event) => event.personId == person.id) .toList() ..sort((a, b) => a.date.compareTo(b.date)); final nextEvent = personEvents.isEmpty ? null : personEvents.first; return Card( child: Padding( padding: const EdgeInsets.all(18), child: Row( children: [ FamilyAvatar(person: person), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( person.name, style: const TextStyle( color: ink, fontWeight: FontWeight.w800, fontSize: 17, ), ), const SizedBox(height: 4), Text( nextEvent == null ? 'Nincs hozz\u00e1rendelt esem\u00e9ny' : 'K\u00f6vetkez\u0151: ${nextEvent.title}, ${formatDate(nextEvent.date)} ${nextEvent.start}', style: const TextStyle(color: Colors.black54), ), ], ), ), Text( '${personEvents.length} esem\u00e9ny', style: const TextStyle( color: sage, fontWeight: FontWeight.w800, ), ), const SizedBox(width: 8), PopupMenuButton( onSelected: (value) { if (value == 'edit') onEdit(person); if (value == 'delete') onDelete(person); }, itemBuilder: (_) => const [ PopupMenuItem( value: 'edit', child: Text('\u00c1tnevez\u00e9s / avatar'), ), PopupMenuItem( value: 'delete', child: Text('T\u00f6rl\u00e9s'), ), ], ), ], ), ), ); }, ), ); } } class StylePage extends StatelessWidget { const StylePage({ super.key, required this.themeId, required this.onThemeChanged, }); final String themeId; final ValueChanged onThemeChanged; @override Widget build(BuildContext context) { return PageFrame( eyebrow: 'T\u00e9ma \u00e9s st\u00edlusok', title: 'Megjelen\u00e9s', child: ListView( padding: const EdgeInsets.only(bottom: 24), children: [ const Text( 'V\u00e1lassz egy alkalmaz\u00e1st\u00e9m\u00e1t. A v\u00e1laszt\u00e1s JSON-be ment\u0151dik, \u00e9s \u00fajraind\u00edt\u00e1s ut\u00e1n is megmarad.', style: TextStyle(color: Colors.black54, fontSize: 16), ), const SizedBox(height: 18), ...appThemes.map((theme) { final selected = theme.id == themeId; return Card( child: ListTile( contentPadding: const EdgeInsets.all(16), leading: CircleAvatar(backgroundColor: theme.seed), title: Text( theme.name, style: const TextStyle( color: ink, fontWeight: FontWeight.w800, ), ), subtitle: Text(theme.description), trailing: selected ? const Icon(Icons.check_circle, color: sage) : const Icon(Icons.circle_outlined, color: Colors.black26), onTap: () => onThemeChanged(theme.id), ), ); }), const SizedBox(height: 18), const Text( 'Csal\u00e1dtag sz\u00ednek \u00e9s avatar k\u00e9szlet', style: TextStyle( color: ink, fontWeight: FontWeight.w800, fontSize: 18, ), ), const SizedBox(height: 10), Wrap( spacing: 8, runSpacing: 8, children: avatarPresets .map( (preset) => Chip( avatar: Text(preset.symbol), label: Text(preset.label), ), ) .toList(), ), ], ), ); } } class ShoppingPage extends StatelessWidget { const ShoppingPage({ super.key, required this.items, required this.onChanged, required this.onAdd, }); final List items; final VoidCallback onChanged; final VoidCallback onAdd; @override Widget build(BuildContext context) { final remaining = items.where((e) => !e.done).length; return PageFrame( eyebrow: '$remaining t\u00e9tel van h\u00e1tra', title: 'Bev\u00e1s\u00e1rl\u00f3lista', action: IconButton.filled(onPressed: onAdd, icon: const Icon(Icons.add)), child: Column( children: [ Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: mint, borderRadius: BorderRadius.circular(18), ), child: Row( children: [ const Icon(Icons.auto_awesome_outlined, color: sage), const SizedBox(width: 12), Expanded( child: Text( items.isEmpty ? 'A list\u00e1d \u00fcres.' : '${items.length - remaining} t\u00e9tel m\u00e1r a kos\u00e1rban van.', style: const TextStyle(color: ink), ), ), Text( '${items.length - remaining}/${items.length}', style: const TextStyle( color: sage, fontWeight: FontWeight.w800, ), ), ], ), ), const SizedBox(height: 16), Expanded( child: ListView.separated( padding: const EdgeInsets.only(bottom: 24), itemCount: items.length, separatorBuilder: (_, _) => const SizedBox(height: 8), itemBuilder: (_, i) { final item = items[i]; return Card( child: CheckboxListTile( value: item.done, activeColor: sage, onChanged: (value) { item.done = value ?? false; onChanged(); }, title: Text( item.name, style: TextStyle( fontWeight: FontWeight.w700, decoration: item.done ? TextDecoration.lineThrough : null, color: item.done ? Colors.black38 : ink, ), ), subtitle: Text('${item.amount} \u00b7 ${item.category}'), secondary: const Icon( Icons.drag_indicator, color: Colors.black26, ), ), ); }, ), ), ], ), ); } } class StockPage extends StatefulWidget { const StockPage({ super.key, required this.items, required this.onChanged, required this.onAdd, required this.onEdit, required this.jarvisSettings, }); final List items; final VoidCallback onChanged; final VoidCallback onAdd; final ValueChanged onEdit; final JarvisSettings jarvisSettings; @override State createState() => _StockPageState(); } class _StockPageState extends State { String location = 'H\u0171t\u0151'; @override Widget build(BuildContext context) { final filtered = widget.items.where((e) => e.location == location).toList(); return PageFrame( eyebrow: '${widget.items.length} term\u00e9k nyilv\u00e1ntartva', title: 'Otthoni k\u00e9szlet', action: IconButton.filled( onPressed: widget.onAdd, icon: const Icon(Icons.add), ), child: Column( children: [ SegmentedButton( segments: const [ ButtonSegment( value: 'H\u0171t\u0151', label: Text('H\u0171t\u0151szekr\u00e9ny'), icon: Icon(Icons.kitchen), ), ButtonSegment( value: 'Kamra', label: Text('Sz\u00e1raz\u00e1ru'), icon: Icon(Icons.inventory_2_outlined), ), ], selected: {location}, onSelectionChanged: (s) => setState(() => location = s.first), ), const SizedBox(height: 18), Expanded( child: ListView.separated( padding: const EdgeInsets.only(bottom: 24), itemCount: filtered.length, separatorBuilder: (_, _) => const SizedBox(height: 9), itemBuilder: (_, i) { final item = filtered[i]; final warning = item.expiry == 'j\u00fal. 22.'; return Card( child: Padding( padding: const EdgeInsets.all(15), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: warning ? const Color(0xFFFFEBDD) : mint, borderRadius: BorderRadius.circular(13), ), child: Icon( location == 'H\u0171t\u0151' ? Icons.egg_alt_outlined : Icons.grain, color: warning ? orange : sage, ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.name, style: const TextStyle( color: ink, fontSize: 16, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 3), Text( 'Lej\u00e1rat: ${item.expiry}', style: TextStyle( color: warning ? orange : Colors.black45, fontWeight: warning ? FontWeight.w700 : FontWeight.normal, ), ), ], ), ), IconButton( onPressed: item.amount > 0 ? () { item.amount--; widget.onChanged(); } : null, icon: const Icon(Icons.remove_circle_outline), ), Text( '${item.amount} ${item.unit}', style: const TextStyle( color: ink, fontWeight: FontWeight.w800, ), ), IconButton( onPressed: () { item.amount++; widget.onChanged(); }, icon: const Icon(Icons.add_circle_outline), ), IconButton( tooltip: 'K\u00e9szlet szerkeszt\u00e9se', onPressed: () => widget.onEdit(item), icon: const Icon(Icons.edit_outlined), ), ], ), ), ); }, ), ), ], ), ); } } class AccountsPage extends StatefulWidget { const AccountsPage({ super.key, required this.connections, required this.cachedEvents, required this.people, required this.onChanged, required this.onSyncedEventsChanged, }); final List connections; final List cachedEvents; final List people; final ValueChanged> onChanged; final ValueChanged> onSyncedEventsChanged; @override State createState() => _AccountsPageState(); } class _AccountsPageState extends State { final calendarPlugin = device_calendar.DeviceCalendarPlugin(); List deviceCalendars = []; bool loadingCalendars = false; bool syncing = false; String? statusMessage; @override void initState() { super.initState(); _loadDeviceCalendars(); } GoogleCalendarConnection? get connection => widget.connections.isEmpty ? null : widget.connections.first; Future _loadDeviceCalendars() async { setState(() { loadingCalendars = true; statusMessage = null; }); try { final permissions = await calendarPlugin.requestPermissions(); if (permissions.isSuccess != true || permissions.data != true) { setState(() { loadingCalendars = false; statusMessage = 'Napt\u00e1r olvas\u00e1si enged\u00e9ly n\u00e9lk\u00fcl nem lehet szinkroniz\u00e1lni.'; }); return; } final result = await calendarPlugin.retrieveCalendars(); final calendars = (result.data ?? const []) .where((calendar) => calendar.id != null) .toList() ..sort((a, b) => (a.name ?? '').compareTo(b.name ?? '')); setState(() { deviceCalendars = calendars; loadingCalendars = false; if (calendars.isEmpty) { statusMessage = 'Nem tal\u00e1ltam lok\u00e1lis napt\u00e1rat az eszk\u00f6z\u00f6n. Androidon ellen\u0151rizd: Be\u00e1ll\u00edt\u00e1sok > Fi\u00f3kok > Google > Fi\u00f3kszinkroniz\u00e1l\u00e1s > Napt\u00e1r legyen bekapcsolva.'; } else { final accountTypes = calendars .map((calendar) => calendar.accountType) .whereType() .where((type) => type.trim().isNotEmpty) .toSet() .toList() ..sort(); statusMessage = '${calendars.length} napt\u00e1rat l\u00e1tok az eszk\u00f6z\u00f6n.' '${accountTypes.isEmpty ? '' : ' Account t\u00edpusok: ${accountTypes.join(', ')}.'}'; } }); } catch (error) { setState(() { loadingCalendars = false; statusMessage = 'Nem siker\u00fclt lek\u00e9rni az eszk\u00f6z napt\u00e1rait: $error'; }); } } Future _syncSelectedCalendars() async { final current = connection; if (current == null || current.enabledCalendarIds.isEmpty) { setState( () => statusMessage = 'V\u00e1lassz ki legal\u00e1bb egy napt\u00e1rat.', ); return; } setState(() { syncing = true; statusMessage = 'Esem\u00e9nyek beolvas\u00e1sa a lok\u00e1lis napt\u00e1rt\u00e1rb\u00f3l\u2026'; }); try { final selectedCalendars = deviceCalendars .where((calendar) => current.enabledCalendarIds.contains(calendar.id)) .toList(); final synced = []; final syncDetails = []; final now = DateTime.now(); final start = DateTime( now.year, now.month, now.day, ).subtract(const Duration(days: 30)); final end = start.add(const Duration(days: 180)); for (final calendar in selectedCalendars) { final result = await calendarPlugin.retrieveEvents( calendar.id, device_calendar.RetrieveEventsParams(startDate: start, endDate: end), ); final events = result.data ?? const []; syncDetails.add('${calendar.name ?? 'Napt\u00e1r'}: ${events.length}'); for (final event in events) { synced.add( CalendarEvent.fromDeviceEvent( event, calendar, personId: current.calendarPersonIds[calendar.id], ), ); } } synced.sort((a, b) { final dateCompare = a.date.compareTo(b.date); return dateCompare != 0 ? dateCompare : a.start.compareTo(b.start); }); widget.onSyncedEventsChanged(synced); widget.onChanged([current.copyWith(lastSync: DateTime.now())]); setState(() { syncing = false; statusMessage = '${synced.length} esem\u00e9ny szinkroniz\u00e1lva \u00e9s JSON cache-be mentve.' '${syncDetails.isEmpty ? '' : ' R\u00e9szletek: ${syncDetails.join('; ')}.'}'; }); } catch (error) { setState(() { syncing = false; statusMessage = 'A szinkroniz\u00e1l\u00e1s nem siker\u00fclt: $error'; }); } } @override Widget build(BuildContext context) { final current = connection; final hasConnection = current != null; final cachedUncategorized = widget.cachedEvents .where((event) => event.isUncategorized) .length; return PageFrame( eyebrow: 'Lok\u00e1lis napt\u00e1r szinkron', title: 'Eszk\u00f6z napt\u00e1rak', action: FilledButton.icon( onPressed: () => _connectGoogle(context), icon: const Icon(Icons.add_link), label: Text( hasConnection ? 'Fi\u00f3k c\u00edmk\u00e9je' : 'Fi\u00f3k c\u00edmke', ), ), child: ListView( padding: const EdgeInsets.only(bottom: 24), children: [ GoogleSyncHero( accountEmail: current?.accountEmail, enabledCount: current?.enabledCalendarIds.length ?? 0, cachedCount: widget.cachedEvents.length, uncategorizedCount: cachedUncategorized, ), const SizedBox(height: 22), if (!hasConnection) const EmptyGoogleConnectionCard() else ...[ const Text( 'Eszk\u00f6z\u00f6n el\u00e9rhet\u0151 napt\u00e1rak', style: TextStyle( color: ink, fontWeight: FontWeight.w800, fontSize: 18, ), ), const SizedBox(height: 10), if (loadingCalendars) const LinearProgressIndicator() else ...deviceCalendars.map( (calendar) => DeviceCalendarToggleCard( calendar: calendar, enabled: current.enabledCalendarIds.contains(calendar.id), people: widget.people, selectedPersonId: current.calendarPersonIds[calendar.id], onChanged: (enabled) { final nextIds = [...current.enabledCalendarIds]; final nextPersonIds = {...current.calendarPersonIds}; if (enabled) { if (!nextIds.contains(calendar.id)) { nextIds.add(calendar.id!); } } else { nextIds.remove(calendar.id); nextPersonIds.remove(calendar.id); } widget.onChanged([ current.copyWith( enabledCalendarIds: nextIds, calendarPersonIds: nextPersonIds, lastSync: current.lastSync, ), ]); }, onPersonChanged: (personId) { if (calendar.id == null) return; final nextPersonIds = {...current.calendarPersonIds}; if (personId == null) { nextPersonIds.remove(calendar.id); } else { nextPersonIds[calendar.id!] = personId; } widget.onChanged([ current.copyWith( calendarPersonIds: nextPersonIds, lastSync: current.lastSync, ), ]); }, ), ), const SizedBox(height: 16), FilledButton.icon( onPressed: syncing ? null : _syncSelectedCalendars, icon: syncing ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.sync), label: Text( syncing ? 'Szinkroniz\u00e1l\u00e1s\u2026' : 'Szinkroniz\u00e1l\u00e1s most', ), ), const SizedBox(height: 8), OutlinedButton.icon( onPressed: _loadDeviceCalendars, icon: const Icon(Icons.refresh), label: const Text('Napt\u00e1rlista friss\u00edt\u00e9se'), ), const SizedBox(height: 8), OutlinedButton.icon( onPressed: () { widget.onChanged([]); widget.onSyncedEventsChanged([]); }, icon: const Icon(Icons.link_off), label: const Text('Napt\u00e1rkapcsolat lev\u00e1laszt\u00e1sa'), ), ], if (statusMessage != null) ...[ const SizedBox(height: 14), Text(statusMessage!, style: const TextStyle(color: Colors.black54)), ], const SizedBox(height: 24), Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: mint, borderRadius: BorderRadius.circular(18), ), child: const Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(Icons.info_outline, color: sage), SizedBox(width: 13), Expanded( child: Text( 'Ez a szinkron egyir\u00e1ny\u00fa: az eszk\u00f6z lok\u00e1lis napt\u00e1rt\u00e1r\u00e1b\u00f3l olvas, a beolvasott esem\u00e9nyeket JSON cache-be menti, \u00e9s nem \u00edr vissza k\u00fcls\u0151 napt\u00e1rba. ' 'Google, Apple/iCloud, Outlook vagy m\u00e1s napt\u00e1r akkor jelenik meg itt, ha az oper\u00e1ci\u00f3s rendszer napt\u00e1rt\u00e1r\u00e1ban el\u00e9rhet\u0151.', style: TextStyle(color: ink, height: 1.45), ), ), ], ), ), ], ), ); } Future _connectGoogle(BuildContext context) async { final email = await showDialog( context: context, builder: (context) => GoogleAccountDialog(initialEmail: connection?.accountEmail ?? ''), ); if (email == null || email.isEmpty) return; widget.onChanged([ GoogleCalendarConnection( id: connection?.id ?? uniqueId(), accountEmail: email, enabledCalendarIds: connection?.enabledCalendarIds ?? const [], lastSync: DateTime.now(), ), ]); } } class GoogleSyncHero extends StatelessWidget { const GoogleSyncHero({ super.key, required this.accountEmail, required this.enabledCount, required this.cachedCount, required this.uncategorizedCount, }); final String? accountEmail; final int enabledCount; final int cachedCount; final int uncategorizedCount; @override Widget build(BuildContext context) { final connected = accountEmail != null && accountEmail!.isNotEmpty; return Container( padding: const EdgeInsets.all(22), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF18332D), Color(0xFF2F6B5B)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(28), boxShadow: [ BoxShadow( color: sage.withValues(alpha: .22), blurRadius: 28, offset: const Offset(0, 16), ), ], ), child: Row( children: [ Container( width: 58, height: 58, decoration: BoxDecoration( color: Colors.white.withValues(alpha: .16), borderRadius: BorderRadius.circular(20), ), child: const Icon( Icons.g_mobiledata_rounded, color: Colors.white, size: 44, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( connected ? accountEmail! : 'M\u00e9g nincs napt\u00e1rkapcsolat', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w900, fontSize: 20, ), ), const SizedBox(height: 5), Text( connected ? '$enabledCount napt\u00e1r bekapcsolva \u00b7 $cachedCount esem\u00e9ny cache-ben \u00b7 $uncategorizedCount kategoriz\u00e1latlan' : 'Adj meg egy c\u00edmk\u00e9t, majd v\u00e1laszd ki, mely eszk\u00f6znapt\u00e1rakb\u00f3l hozzon esem\u00e9nyeket.', style: const TextStyle(color: Colors.white70, height: 1.35), ), ], ), ), ], ), ); } } class GoogleAccountDialog extends StatefulWidget { const GoogleAccountDialog({super.key, required this.initialEmail}); final String initialEmail; @override State createState() => _GoogleAccountDialogState(); } class _GoogleAccountDialogState extends State { late final TextEditingController controller; @override void initState() { super.initState(); controller = TextEditingController( text: widget.initialEmail.isEmpty ? 'csalad@gmail.com' : widget.initialEmail, ); } @override void dispose() { controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AlertDialog( title: const Text('Napt\u00e1rkapcsolat c\u00edmk\u00e9je'), content: TextField( controller: controller, autofocus: true, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration( labelText: 'Fi\u00f3k / kapcsolat neve', helperText: 'Pl. csalad@gmail.com, iCloud, munkahelyi napt\u00e1r', ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('M\u00e9gse'), ), FilledButton( onPressed: () => Navigator.pop(context, controller.text.trim()), child: const Text('Ment\u00e9s'), ), ], ); } } class EmptyGoogleConnectionCard extends StatelessWidget { const EmptyGoogleConnectionCard({super.key}); @override Widget build(BuildContext context) { return const Card( child: Padding( padding: EdgeInsets.all(20), child: Row( children: [ Icon(Icons.calendar_month_outlined, color: sage), SizedBox(width: 14), Expanded( child: Text( 'Adj hozz\u00e1 egy Google fi\u00f3kot, majd v\u00e1laszd ki, mely napt\u00e1rak jelenjenek meg a helyi csal\u00e1di n\u00e9zetben.', style: TextStyle(color: ink, height: 1.4), ), ), ], ), ), ); } } class GoogleCalendarToggleCard extends StatelessWidget { const GoogleCalendarToggleCard({ super.key, required this.calendar, required this.enabled, required this.onChanged, }); final GoogleCalendarOption calendar; final bool enabled; final ValueChanged onChanged; @override Widget build(BuildContext context) { return Card( child: SwitchListTile( value: enabled, activeThumbColor: calendar.color, onChanged: onChanged, secondary: CircleAvatar( backgroundColor: calendar.color.withValues(alpha: .14), child: Icon(Icons.calendar_today, color: calendar.color, size: 19), ), title: Text( calendar.name, style: const TextStyle(color: ink, fontWeight: FontWeight.w800), ), subtitle: Text(calendar.description), ), ); } } class DeviceCalendarToggleCard extends StatelessWidget { const DeviceCalendarToggleCard({ super.key, required this.calendar, required this.enabled, required this.people, required this.selectedPersonId, required this.onChanged, required this.onPersonChanged, }); final device_calendar.Calendar calendar; final bool enabled; final List people; final String? selectedPersonId; final ValueChanged onChanged; final ValueChanged onPersonChanged; @override Widget build(BuildContext context) { final calendarColor = Color( calendar.color ?? const Color(0xFF4285F4).toARGB32(), ); return Card( child: Padding( padding: const EdgeInsets.only(bottom: 10), child: Column( children: [ SwitchListTile( value: enabled, activeThumbColor: calendarColor, onChanged: onChanged, secondary: CircleAvatar( backgroundColor: calendarColor.withValues(alpha: .14), child: Icon( Icons.calendar_today, color: calendarColor, size: 19, ), ), title: Text( calendar.name ?? 'N\u00e9vtelen napt\u00e1r', style: const TextStyle(color: ink, fontWeight: FontWeight.w800), ), subtitle: Text( [ calendar.accountName, calendar.accountType, if (calendar.isReadOnly == true) 'csak olvashat\u00f3', ] .whereType() .where((part) => part.isNotEmpty) .join(' \u00b7 '), ), ), if (enabled) Padding( padding: const EdgeInsets.fromLTRB(72, 0, 16, 0), child: DropdownButtonFormField( initialValue: selectedPersonId, decoration: const InputDecoration( labelText: 'Kihez tartozzanak az esem\u00e9nyek?', ), items: [ const DropdownMenuItem( value: null, child: Text('Kategoriz\u00e1latlan'), ), const DropdownMenuItem( value: everyonePersonId, child: Text('Mindenki'), ), ...people.map( (person) => DropdownMenuItem( value: person.id, child: Text('${person.avatar} ${person.name}'), ), ), ], onChanged: onPersonChanged, ), ), ], ), ), ); } } class InfoBox extends StatelessWidget { const InfoBox({super.key}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: mint, borderRadius: BorderRadius.circular(18), ), child: const Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(Icons.lock_outline, color: sage), SizedBox(width: 13), Expanded( child: Text( 'A hiteles\u00edt\u00e9si adatok nem ker\u00fclnek az alkalmaz\u00e1sba. ' 'A Google OAuth, az iCloud pedig biztons\u00e1gos CalDAV-kapcsolaton kereszt\u00fcl csatlakozik.', style: TextStyle(color: ink, height: 1.45), ), ), ], ), ); } } class AccountCard extends StatefulWidget { const AccountCard({ super.key, required this.icon, required this.color, required this.title, required this.subtitle, }); final IconData icon; final Color color; final String title; final String subtitle; @override State createState() => _AccountCardState(); } class _AccountCardState extends State { bool connected = false; @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(18), child: Row( children: [ CircleAvatar( backgroundColor: widget.color.withValues(alpha: .1), child: Icon(widget.icon, color: widget.color, size: 28), ), const SizedBox(width: 15), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.title, style: const TextStyle( color: ink, fontSize: 17, fontWeight: FontWeight.w800, ), ), const SizedBox(height: 3), Text( connected ? 'Csatlakoztatva \u00b7 Most friss\u00edtve' : widget.subtitle, style: TextStyle(color: connected ? sage : Colors.black54), ), ], ), ), OutlinedButton( onPressed: () { setState(() => connected = !connected); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( connected ? '${widget.title} dem\u00f3kapcsolat l\u00e9trehozva' : '${widget.title} lev\u00e1lasztva', ), ), ); }, child: Text( connected ? 'Lev\u00e1laszt\u00e1s' : 'Kapcsol\u00f3d\u00e1s', ), ), ], ), ), ); } }