calcolatrice/lib/features/calculator/calculator_screen.dart

355 lines
14 KiB
Dart
Raw Permalink Normal View History

2026-03-04 14:33:15 +01:00
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/utils/shared_provider.dart';
import '../pdf_export/pdf_service.dart';
import 'calculator_provider.dart';
import '../../widgets/custom_button.dart';
import '../converter/converter_provider.dart';
import '../splitter/splitter_provider.dart';
class MainNavigationScreen extends ConsumerStatefulWidget {
const MainNavigationScreen({super.key});
@override
ConsumerState<MainNavigationScreen> createState() => _MainNavigationScreenState();
}
class _MainNavigationScreenState extends ConsumerState<MainNavigationScreen> {
int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
final currentVal = ref.watch(sharedValueProvider);
final calcState = ref.watch(calculatorProvider);
final history = ref.watch(historyProvider);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text("Quote", style: TextStyle(fontWeight: FontWeight.bold)),
centerTitle: true,
actions: [
IconButton(
icon: const Icon(Icons.picture_as_pdf_rounded),
onPressed: () {
PdfService.generateSplitterReport(
currentVal,
ref.read(splitterProvider).friends,
ref.read(splitterProvider.notifier).calculateSettlements(currentVal),
history,
);
},
)
],
),
body: Column(
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: Colors.deepPurple.withOpacity(0.05),
border: Border(bottom: BorderSide(color: Colors.deepPurple.withOpacity(0.1))),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
_selectedIndex == 2 ? "LA TUA SPESA ATTUALE" : "VALORE ATTIVO",
style: const TextStyle(fontSize: 10, fontWeight: FontWeight.bold, color: Colors.deepPurple)
),
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
_selectedIndex == 0
? calcState.equation.replaceAll('.', ',')
: currentVal.toStringAsFixed(2).replaceAll('.', ','),
style: const TextStyle(fontSize: 48, fontWeight: FontWeight.w900, color: Colors.black87),
),
),
],
),
),
Expanded(
child: IndexedStack(
index: _selectedIndex,
children: [
CalculatorModule(),
ConverterModule(),
SplitterModule(),
],
),
),
],
),
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) => setState(() => _selectedIndex = index),
destinations: const [
NavigationDestination(icon: Icon(Icons.calculate_outlined), label: 'Calcola'),
NavigationDestination(icon: Icon(Icons.currency_exchange_outlined), label: 'Converti'),
NavigationDestination(icon: Icon(Icons.group_outlined), label: 'Dividi'),
],
),
);
}
}
class CalculatorModule extends ConsumerWidget {
const CalculatorModule({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final calcLogic = ref.read(calculatorProvider.notifier);
// Layout richiesto:
// Riga 1: C | ⌫ | % | ÷
// Riga 2: 7 | 8 | 9 | ×
// Riga 3: 4 | 5 | 6 | -
// Riga 4: 1 | 2 | 3 | +
// Riga 5: ± | 0 | , | =
final List<String> labels = [
'C', '', '%', '÷',
'7', '8', '9', '×',
'4', '5', '6', '-',
'1', '2', '3', '+',
'±', '0', ',', '='
];
return Padding(
padding: const EdgeInsets.all(12),
child: GridView.builder(
// Usiamo 4 colonne
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
),
itemCount: labels.length,
itemBuilder: (context, index) {
final label = labels[index];
// --- LOGICA COLORI ---
Color? bgColor;
Color textColor = Colors.black87;
if (RegExp(r'[0-9]').hasMatch(label) || label == ',') {
// Numeri e virgola: Arancione Chiaro
bgColor = const Color(0xFFFFCC80);
} else if (['÷', '×', '-', '+', '=', '%'].contains(label)) {
// Segni e Uguale: Azzurro
bgColor = const Color(0xFF81D4FA);
if (label == '=') bgColor = const Color(0xFF4FC3F7); // Azzurro più scuro per l'uguale
} else if (label == 'C' || label == '') {
// Comandi di cancellazione: Rosso chiaro per distinguerli
bgColor = const Color(0xFFFFCDD2);
textColor = const Color(0xFFB71C1C);
} else if (label == '±') {
// Segno +/-: Grigio neutro
bgColor = Colors.grey[300];
}
return CustomButton(
label: label,
onPressed: () => calcLogic.onBtnPressed(label),
color: bgColor,
textColor: textColor,
);
},
),
);
}
}
class ConverterModule extends StatelessWidget {
@override
Widget build(BuildContext context) => const Center(child: Text("Modulo Convertitore pronto"));
}
class SplitterModule extends ConsumerStatefulWidget {
const SplitterModule({super.key});
@override
ConsumerState<SplitterModule> createState() => _SplitterModuleState();
}
class _SplitterModuleState extends ConsumerState<SplitterModule> {
final TextEditingController _nameController = TextEditingController();
final TextEditingController _amountController = TextEditingController();
final TextEditingController _noteController = TextEditingController();
void _showHistoryDetails(BuildContext context, WidgetRef ref) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(25))),
builder: (context) {
return Consumer(builder: (context, ref, _) {
final history = ref.watch(historyProvider);
final historyLogic = ref.read(historyProvider.notifier);
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
left: 20, right: 20, top: 20
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text("Dettaglio tua spesa", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const Text("Aggiungi una nota per ogni importo", style: TextStyle(fontSize: 12, color: Colors.grey)),
const SizedBox(height: 15),
if (history.isEmpty)
const Padding(padding: EdgeInsets.all(20), child: Text("Nessun dato in calcolatrice")),
ConstrainedBox(
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.4),
child: ListView.builder(
shrinkWrap: true,
itemCount: history.length,
itemBuilder: (context, idx) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
Text("${history[idx].result}", style: const TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(width: 15),
Expanded(
child: TextField(
decoration: const InputDecoration(hintText: "Nota...", isDense: true),
controller: TextEditingController.fromValue(
TextEditingValue(
text: history[idx].note,
selection: TextSelection.collapsed(offset: history[idx].note.length),
),
),
onChanged: (val) => historyLogic.updateNote(idx, val),
),
),
],
),
);
},
),
),
const SizedBox(height: 20),
ElevatedButton(
style: ElevatedButton.styleFrom(minimumSize: const Size(double.infinity, 50)),
onPressed: () => Navigator.pop(context),
child: const Text("Salva e Chiudi")
),
const SizedBox(height: 20),
],
),
);
});
},
);
}
@override
Widget build(BuildContext context) {
final mySpent = ref.watch(sharedValueProvider);
final splitter = ref.watch(splitterProvider);
final history = ref.watch(historyProvider);
final logic = ref.read(splitterProvider.notifier);
return Column(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
Row(
children: [
Expanded(flex: 2, child: TextField(controller: _nameController, decoration: const InputDecoration(labelText: "Amico", isDense: true, border: OutlineInputBorder()))),
const SizedBox(width: 5),
Expanded(flex: 1, child: TextField(controller: _amountController, keyboardType: TextInputType.number, decoration: const InputDecoration(labelText: "", isDense: true, border: OutlineInputBorder()))),
const SizedBox(width: 5),
Expanded(flex: 2, child: TextField(controller: _noteController, decoration: const InputDecoration(labelText: "Nota", isDense: true, border: OutlineInputBorder()))),
const SizedBox(width: 5),
IconButton.filled(
onPressed: () {
if (_nameController.text.isNotEmpty) {
double amount = double.tryParse(_amountController.text.replaceAll(',', '.')) ?? 0.0;
logic.addFriend(_nameController.text, amount, _noteController.text);
_nameController.clear(); _amountController.clear(); _noteController.clear();
FocusScope.of(context).unfocus();
}
},
icon: const Icon(Icons.add),
),
],
),
],
),
),
Expanded(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(color: Colors.grey[50], borderRadius: const BorderRadius.vertical(top: Radius.circular(20))),
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Card(
elevation: 0,
color: Colors.deepPurple.withOpacity(0.05),
margin: const EdgeInsets.all(12),
child: InkWell(
onTap: () => _showHistoryDetails(context, ref),
borderRadius: BorderRadius.circular(12),
child: ListTile(
dense: true,
leading: const CircleAvatar(radius: 15, child: Icon(Icons.receipt_long, size: 16)),
title: const Text("Il Tuo Anticipo Totale", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.deepPurple)),
subtitle: Text("Clicca per i singoli calcoli (${history.length})"),
trailing: Text("${mySpent.toStringAsFixed(2)}", style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.deepPurple, fontSize: 18)),
),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final friend = splitter.friends[index];
return ListTile(
dense: true,
title: Text(friend.name, style: const TextStyle(fontWeight: FontWeight.w600)),
subtitle: Text(friend.note.isEmpty ? "Anticipo spesa" : friend.note),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("${friend.spent.toStringAsFixed(2)}"),
IconButton(icon: const Icon(Icons.remove_circle_outline, color: Colors.red, size: 18), onPressed: () => logic.removeFriend(index)),
],
),
);
},
childCount: splitter.friends.length,
),
),
const SliverToBoxAdapter(child: SizedBox(height: 100)),
],
),
),
),
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, -5))],
borderRadius: const BorderRadius.vertical(top: Radius.circular(30)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: logic.calculateSettlements(mySpent).map((text) =>
Padding(padding: const EdgeInsets.symmetric(vertical: 2), child: Text(text, textAlign: TextAlign.center, style: TextStyle(
fontWeight: text.contains('') ? FontWeight.bold : FontWeight.normal,
color: text.contains('') ? Colors.deepPurple : Colors.black87,
fontSize: text.contains('') ? 16 : 13,
)))
).toList(),
),
),
],
);
}
}