YogiBook_App/lib/screens/home_page.dart
2025-12-27 20:46:49 +01:00

532 lines
16 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../models/school.dart';
import 'lessons_page.dart';
import 'select_school_page.dart';
import 'meditation_page.dart';
import '../services/vanguard_api.dart';
import 'login_page.dart';
class HomePage extends StatefulWidget {
final String token;
final School school;
final String? userFirstName;
const HomePage({
super.key,
required this.token,
required this.school,
this.userFirstName,
});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
static const Color kBg = Color(0xFFF6F6FB);
static const Color kGreen = Color(0xFF10B981);
int bottomIndex = 0; // Home
late final String zenQuote;
@override
void initState() {
super.initState();
zenQuote = _pickZenQuote();
}
String get _name => (widget.userFirstName ?? '').trim();
String get _avatarLetter => _name.isNotEmpty ? _name[0].toUpperCase() : 'U';
String get _schoolAddress => (widget.school.addressFull ?? '').trim();
String _pickZenQuote() {
const quotes = <String>[
'Respira. Sei esattamente dove devi essere.',
'Un respiro alla volta, un passo alla volta.',
'La costanza è più forte della motivazione.',
'Lascia andare ciò che non serve.',
'La calma è una superpotenza.',
'Il corpo ascolta ciò che la mente ripete.',
'Presenza. Non perfezione.',
'Sii gentile con te stesso: è pratica.',
'Ogni pratica è un nuovo inizio.',
'Dove va il respiro, va lattenzione.',
'Inspira fiducia, espira paura.',
'La pace inizia dal respiro.',
'Scegli la lentezza, trovi chiarezza.',
'Anche il silenzio insegna.',
'Oggi basta esserci.',
'Il meglio è nel semplice.',
'Radicati, poi fiorisci.',
'Lascia che il respiro ti guidi.',
'Un minuto di presenza cambia la giornata.',
'La forza è morbida.',
'Il cambiamento nasce dallascolto.',
'Non correre: senti.',
'Ogni espirazione è un rilascio.',
'Ogni inspirazione è un dono.',
'La pratica è un ritorno a casa.',
'Dove cè respiro, cè spazio.',
'Trasforma la tensione in attenzione.',
'Sii saldo e leggero.',
'Sii stabile come una montagna.',
'Sii fluido come lacqua.',
'La quiete è una scelta.',
'Concediti tempo.',
'Concediti gentilezza.',
'La mente si calma nel corpo.',
'Il corpo ricorda la cura.',
'Fai pace con il tuo ritmo.',
'Non devi dimostrare nulla.',
'Lascia che sia abbastanza.',
'La gratitudine apre il cuore.',
'La disciplina crea libertà.',
'Piccoli gesti, grandi cambiamenti.',
'Ogni giorno ricomincia.',
'Sorridi al respiro.',
'La tua energia segue la tua attenzione.',
'Rallenta e osserva.',
'Cè forza nella dolcezza.',
'Cè bellezza nella pausa.',
'Quando dubiti, respira.',
'Lascia andare il giudizio.',
'Cerca lequilibrio, non il controllo.',
'Senti i piedi: sei qui.',
'Senti il cuore: sei vivo.',
'Accetta, poi agisci.',
'La presenza è il vero lusso.',
'Larmonia è un allenamento.',
'Nessuna postura, solo esperienza.',
'Ogni postura è un dialogo.',
'Ascolta prima di spingere.',
'La stabilità nasce dalla morbidezza.',
'Il respiro è il tuo ancoraggio.',
'La calma è contagiosa.',
'Lascia che il corpo si apra con pazienza.',
'Scegli pensieri che ti nutrono.',
'Scegli parole che ti rispettano.',
'Scegli un ritmo sostenibile.',
'Lentamente è un modo di arrivare.',
'Fidati del processo.',
'Ogni tremore è vita.',
'Ogni difficoltà è un insegnante.',
'Non sei in ritardo.',
'Non sei indietro: sei in cammino.',
'Sii curioso, non severo.',
'La pratica non si perde mai.',
'Oggi fai un passo gentile.',
'Respira nel punto che resiste.',
'Dove cè rigidità, porta luce.',
'Il corpo si ammorbidisce quando ti fidi.',
'La chiarezza arriva quando rallenti.',
'La mente si quieta quando senti.',
'Sii presente al 1%. È già tanto.',
'Lequilibrio è micro-aggiustamento.',
'La semplicità è potenza.',
'Lascia spazio al respiro.',
'Lascia spazio allerrore.',
'La pratica è onestà.',
'La pratica è cura.',
'Senti il tuo centro.',
'Torna al corpo, torna al presente.',
'Ogni respiro è una nuova possibilità.',
'Ogni inspirazione è un inizio.',
'Ogni espirazione è una resa.',
'Non forzare: accompagna.',
'Quando ti perdi, torna al respiro.',
'Il respiro illumina la strada.',
'Il corpo è la tua casa.',
'La pace è già qui, ascoltala.',
'Dai priorità a ciò che ti fa bene.',
'Sii fedele alla tua pratica, non allidea.',
'La tua serenità è una scelta quotidiana.',
'Oggi scegli leggerezza.',
];
return quotes[Random().nextInt(quotes.length)];
}
Future<void> _openMaps() async {
final query = _schoolAddress.isNotEmpty
? _schoolAddress
: widget.school.name;
final uri = Uri.parse(
'https://www.google.com/maps/search/?api=1&query=${Uri.encodeComponent(query)}',
);
final ok = await launchUrl(uri, mode: LaunchMode.externalApplication);
if (!ok && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Impossibile aprire Mappe su questo dispositivo.'),
),
);
}
}
void _goLessons() {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => LessonsPage(
token: widget.token,
school: widget.school,
userFirstName: widget.userFirstName,
),
),
);
}
Future<void> _logout() async {
final ok = await showDialog<bool>(
context: context,
builder: (_) => AlertDialog(
title: const Text('Conferma logout'),
content: const Text('Vuoi uscire dal tuo account?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Annulla'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('Logout'),
),
],
),
);
if (ok != true) return;
// chiude drawer
Navigator.of(context).pop();
try {
await VanguardApi.logout(token: widget.token);
} catch (_) {}
if (!mounted) return;
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (_) => const LoginPage()),
(route) => false,
);
}
void _handleBottomNav(int i) {
setState(() => bottomIndex = i);
if (i == 0) return;
if (i == 1) {
_goLessons();
return;
}
if (i == 2) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('TODO: vai ad Account')));
return;
}
if (i == 3) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => MeditationPage(
token: widget.token,
school: widget.school,
userFirstName: widget.userFirstName,
),
),
);
return;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kBg,
drawer: Drawer(
child: SafeArea(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
child: Align(
alignment: Alignment.bottomLeft,
child: Text(
'Menu',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w900),
),
),
),
ListTile(
leading: const Icon(Icons.swap_horiz),
title: const Text('Cambia scuola'),
onTap: () {
Navigator.of(context).pop();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => SelectSchoolPage(token: widget.token),
),
);
},
),
const Divider(height: 1),
ListTile(
leading: const Icon(Icons.logout),
title: const Text('Logout'),
onTap: _logout,
),
],
),
),
),
appBar: AppBar(
backgroundColor: kBg,
elevation: 0,
centerTitle: true,
title: const Text(
'Home',
style: TextStyle(fontWeight: FontWeight.w800, fontSize: 18),
),
actions: [
Padding(
padding: const EdgeInsets.only(right: 12),
child: CircleAvatar(
radius: 16,
backgroundColor: kGreen,
child: Text(
_avatarLetter,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 12,
),
),
),
),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed, // con 4 icone serve
backgroundColor: Colors.white,
selectedItemColor: const Color(0xFF10B981),
unselectedItemColor: Colors.black54,
currentIndex: bottomIndex,
onTap: _handleBottomNav,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home_rounded),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.event_note_rounded),
label: 'Lezioni',
),
BottomNavigationBarItem(
icon: Icon(Icons.person_rounded),
label: 'Account',
),
BottomNavigationBarItem(
icon: Icon(Icons.self_improvement_rounded),
label: 'Meditazione',
),
],
),
body: Padding(
padding: const EdgeInsets.fromLTRB(16, 10, 16, 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Ciao + nome
Text(
_name.isNotEmpty ? 'Ciao, $_name' : 'Ciao',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900),
),
const SizedBox(height: 10),
// Nome scuola + indirizzo sotto
Text(
widget.school.name,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w900),
),
if (_schoolAddress.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
_schoolAddress,
style: const TextStyle(
fontSize: 12,
color: Colors.black54,
fontWeight: FontWeight.w600,
),
),
],
const SizedBox(height: 10),
// Pulsante mappe
OutlinedButton.icon(
onPressed: _openMaps,
icon: const Icon(Icons.directions, size: 18, color: kGreen),
label: const Text(
'Apri Mappe',
style: TextStyle(fontWeight: FontWeight.w800, color: kGreen),
),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: kGreen, width: 2),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
),
const SizedBox(height: 14),
// Tiles
_HomeTile(
icon: Icons.event_note_rounded,
title: 'Vedi lezioni',
subtitle: 'Prenotazioni e gestione lezioni',
onTap: _goLessons,
),
_HomeTile(
icon: Icons.shopping_bag_rounded,
title: 'Vedi ordini',
subtitle: 'Storico e stato ordini (TODO)',
onTap: () => ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('TODO: Ordini'))),
),
_HomeTile(
icon: Icons.person_rounded,
title: 'Account',
subtitle: 'Profilo e impostazioni (TODO)',
onTap: () => ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('TODO: Account'))),
),
const Spacer(),
// Logo + frase zen
Center(
child: Column(
children: [
Text(
'$zenQuote',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 16,
height: 1.3,
color: Colors.black87,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 14),
Image.asset('assets/images/yogibook_logo.png', height: 78),
const SizedBox(height: 6),
],
),
),
],
),
),
);
}
}
class _HomeTile extends StatelessWidget {
static const Color kGreen = Color(0xFF10B981);
final IconData icon;
final String title;
final String subtitle;
final VoidCallback onTap;
const _HomeTile({
required this.icon,
required this.title,
required this.subtitle,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18),
boxShadow: const [
BoxShadow(
blurRadius: 18,
color: Color(0x12000000),
offset: Offset(0, 10),
),
],
),
child: InkWell(
borderRadius: BorderRadius.circular(18),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: const Color(0xFFE7F8F1),
borderRadius: BorderRadius.circular(14),
),
child: Icon(icon, color: kGreen),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.w900,
fontSize: 15,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: const TextStyle(
color: Colors.black54,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
],
),
),
const SizedBox(width: 10),
Container(
width: 34,
height: 34,
decoration: const BoxDecoration(
color: kGreen,
shape: BoxShape.circle,
),
child: const Icon(Icons.chevron_right, color: Colors.white),
),
],
),
),
),
);
}
}