calcolatrice/lib/features/calculator/calculator_screen.dart

355 lines
No EOL
14 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 '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(),
),
),
],
);
}
}