edit backgroun and inmclude

This commit is contained in:
2026-01-18 21:33:01 +01:00
parent 1a41a99e20
commit 49a9d2a2a6
17 changed files with 1575 additions and 794 deletions
+39
View File
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
class AppBottomNav extends StatelessWidget {
final int currentIndex;
final ValueChanged<int> onTap;
const AppBottomNav({
super.key,
required this.currentIndex,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
selectedItemColor: const Color(0xFF10B981),
unselectedItemColor: Colors.black54,
currentIndex: currentIndex,
onTap: onTap,
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',
),
],
);
}
}
+271
View File
@@ -0,0 +1,271 @@
import 'package:flutter/material.dart';
import '../models/school.dart';
import '../services/vanguard_api.dart';
import '../config/api_config.dart';
import '../screens/select_school_page.dart';
import '../screens/login_page.dart';
import '../screens/medical_certificates_page.dart';
class AppDrawer extends StatelessWidget {
final String token;
final School school;
final String? userFirstName;
const AppDrawer({
super.key,
required this.token,
required this.school,
this.userFirstName,
});
String get _name => (userFirstName ?? '').trim();
String get _avatarLetter => _name.isNotEmpty ? _name[0].toUpperCase() : 'U';
String? get _schoolLogoUrl {
final raw = (school.logo ?? '').toString().trim();
if (raw.isEmpty) return null;
// base: https://app.yogibook.com/public/userarea/
return '${ApiConfig.scheme}://${ApiConfig.host}/public/userarea/$raw';
}
Future<void> _logout(BuildContext context) 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;
Navigator.of(context).pop(); // chiude drawer
try {
await VanguardApi.logout(token: token);
} catch (_) {}
if (!context.mounted) return;
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (_) => const LoginPage()),
(route) => false,
);
}
@override
Widget build(BuildContext context) {
final logoUrl = _schoolLogoUrl;
return Drawer(
child: SafeArea(
child: ListView(
padding: EdgeInsets.zero,
children: [
_DrawerHeaderWide(
schoolName: school.name,
logoUrl: logoUrl,
fallbackLetter: _avatarLetter,
),
ListTile(
leading: const Icon(Icons.swap_horiz),
title: const Text('Cambia scuola'),
onTap: () {
Navigator.of(context).pop();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => SelectSchoolPage(token: token),
),
);
},
),
// ✅ solo nel drawer
ListTile(
leading: const Icon(Icons.medical_information),
title: const Text('Certificati medici'),
onTap: () {
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => MedicalCertificatesPage(
token: token,
school: school,
userFirstName: userFirstName,
),
),
);
},
),
const Divider(height: 1),
ListTile(
leading: const Icon(Icons.logout),
title: const Text('Logout'),
onTap: () => _logout(context),
),
],
),
),
);
}
}
class _DrawerHeaderWide extends StatelessWidget {
final String schoolName;
final String? logoUrl;
final String fallbackLetter;
const _DrawerHeaderWide({
required this.schoolName,
required this.logoUrl,
required this.fallbackLetter,
});
@override
Widget build(BuildContext context) {
const green = Color(0xFF10B981);
return Container(
// Header largo quanto il drawer
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFF4F6FA), Color(0xFFEFF7F3)],
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Menu',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w900),
),
const SizedBox(height: 12),
// ✅ BOX LOGO LARGO + PIÙ ALTO + IN RATIO (non taglia)
Container(
width: double.infinity,
height: 140, // <-- aumenta qui (es. 140/160/180)
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(
blurRadius: 18,
color: Color(0x12000000),
offset: Offset(0, 10),
),
],
border: Border.all(color: const Color(0x14000000)),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: logoUrl == null
? _LogoFallback(letter: fallbackLetter)
: Padding(
padding: const EdgeInsets.all(10), // respiro per logo
child: Image.network(
logoUrl!,
width: double.infinity, // ✅ solo larghezza “imposta”
fit: BoxFit.contain, // ✅ mantiene ratio, non taglia
errorBuilder: (_, __, ___) =>
_LogoFallback(letter: fallbackLetter),
),
),
),
),
const SizedBox(height: 10),
// Nome scuola sotto
Text(
schoolName,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.w900,
fontSize: 22,
),
),
const SizedBox(height: 6),
// badge piccolo opzionale, fa “design”
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: green.withOpacity(0.12),
borderRadius: BorderRadius.circular(999),
),
child: const Text(
'Scuola selezionata',
style: TextStyle(
color: green,
fontWeight: FontWeight.w900,
fontSize: 11,
),
),
),
],
),
);
}
}
class _LogoFallback extends StatelessWidget {
final String letter;
const _LogoFallback({required this.letter});
@override
Widget build(BuildContext context) {
const green = Color(0xFF10B981);
return Container(
width: double.infinity,
height: double.infinity,
alignment: Alignment.center,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFE7F8F1), Color(0xFFF4F6FA)],
),
),
child: Container(
width: 44,
height: 44,
decoration: const BoxDecoration(color: green, shape: BoxShape.circle),
alignment: Alignment.center,
child: Text(
letter,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 16,
),
),
),
);
}
}
+88
View File
@@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
class YogibookBackground extends StatelessWidget {
final Widget child;
const YogibookBackground({super.key, required this.child});
@override
Widget build(BuildContext context) {
return Stack(
children: [
// --- SFONDO (gradient)
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFF4F6FA), Color(0xFFEFF7F3), Color(0xFFF4F6FA)],
),
),
),
// --- BOLLE decorative
const _BgBlob(color: Color(0x3310B981), size: 280, top: -70, left: -90),
const _BgBlob(
color: Color(0x22F59E0B),
size: 230,
bottom: 80,
right: -70,
),
const _BgBlob(
color: Color(0x227C3AED),
size: 190,
bottom: -55,
left: 35,
),
// contenuto pagina
child,
],
);
}
}
class _BgBlob extends StatelessWidget {
final Color color;
final double size;
final double? top;
final double? left;
final double? right;
final double? bottom;
const _BgBlob({
required this.color,
required this.size,
this.top,
this.left,
this.right,
this.bottom,
});
@override
Widget build(BuildContext context) {
return Positioned(
top: top,
left: left,
right: right,
bottom: bottom,
child: IgnorePointer(
child: Container(
width: size,
height: size,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
blurRadius: 70,
color: color,
offset: const Offset(0, 18),
),
],
),
),
),
);
}
}