kz_educa/lib/screens/budget_screen.dart

426 lines
19 KiB
Dart
Executable File

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:hive/hive.dart';
import '../models/budget.dart';
import '../models/transaction.dart';
import '../services/hive_service.dart';
import 'create_budget_screen.dart';
class BudgetScreen extends StatefulWidget {
const BudgetScreen({super.key});
@override
State<BudgetScreen> createState() => _BudgetScreenState();
}
class _BudgetScreenState extends State<BudgetScreen> {
late HiveService _hiveService;
List<MapEntry<int, Budget>> _budgetEntries = [];
BudgetPeriod _selectedPeriodFilter = BudgetPeriod.month;
@override
void initState() {
super.initState();
_hiveService = HiveService();
_loadBudgets();
}
Future<void> _loadBudgets() async {
final box = Hive.box<Budget>(HiveService.budgetsBox);
final entries = box.toMap().entries.map((e) => MapEntry(e.key as int, e.value)).toList();
if (mounted) {
setState(() {
_budgetEntries = entries;
});
}
}
Future<void> _deleteBudget(int key) async {
await _hiveService.deleteBudget(key);
_loadBudgets();
}
String _getCategoryName(TransactionCategory category) {
return {
TransactionCategory.food: 'Comida',
TransactionCategory.transport: 'Transporte',
TransactionCategory.social: 'Social',
TransactionCategory.education: 'Educação',
TransactionCategory.medical: 'Médico',
TransactionCategory.shopping: 'Compras',
TransactionCategory.salary: 'Salário',
TransactionCategory.invest: 'Investir',
TransactionCategory.business: 'Negócios',
TransactionCategory.others: 'Outros',
TransactionCategory.house: 'Casa',
TransactionCategory.utilities: 'Utilidades',
TransactionCategory.subscriptions: 'Assinaturas',
TransactionCategory.leisure: 'Lazer',
TransactionCategory.gym: 'Academia',
TransactionCategory.gifts: 'Presentes',
TransactionCategory.pets: 'Animais',
TransactionCategory.freelance: 'Freelance',
TransactionCategory.dividends: 'Dividendos',
TransactionCategory.loan: 'Empréstimo',
TransactionCategory.refund: 'Reembolso',
TransactionCategory.tax: 'Imposto',
TransactionCategory.bonus: 'Bônus',
TransactionCategory.allowance: 'Mesada',
}[category] ?? 'Desconhecido';
}
IconData _getCategoryIcon(TransactionCategory category) {
switch (category) {
case TransactionCategory.food:
return Icons.fastfood;
case TransactionCategory.transport:
return Icons.directions_car;
case TransactionCategory.social:
return Icons.group;
case TransactionCategory.education:
return Icons.school;
case TransactionCategory.medical:
return Icons.medical_services;
case TransactionCategory.shopping:
return Icons.shopping_bag;
case TransactionCategory.salary:
return Icons.attach_money;
case TransactionCategory.invest:
return Icons.trending_up;
case TransactionCategory.business:
return Icons.business_center;
case TransactionCategory.others:
return Icons.category;
case TransactionCategory.house:
return Icons.home;
case TransactionCategory.utilities:
return Icons.lightbulb;
case TransactionCategory.subscriptions:
return Icons.subscriptions;
case TransactionCategory.leisure:
return Icons.movie;
case TransactionCategory.gym:
return Icons.fitness_center;
case TransactionCategory.gifts:
return Icons.card_giftcard;
case TransactionCategory.pets:
return Icons.pets;
case TransactionCategory.freelance:
return Icons.work;
case TransactionCategory.dividends:
return Icons.monetization_on;
case TransactionCategory.loan:
return Icons.account_balance;
case TransactionCategory.refund:
return Icons.reply;
case TransactionCategory.tax:
return Icons.account_balance_wallet;
case TransactionCategory.bonus:
return Icons.star;
case TransactionCategory.allowance:
return Icons.money;
default:
return Icons.help;
}
}
String _getPeriodName(BudgetPeriod period) {
switch (period) {
case BudgetPeriod.week:
return 'Semana';
case BudgetPeriod.month:
return 'Mês';
case BudgetPeriod.quarter:
return 'Trimestre';
case BudgetPeriod.year:
return 'Ano';
}
}
@override
Widget build(BuildContext context) {
final filteredEntries = _budgetEntries.where((e) => e.value.period == _selectedPeriodFilter).toList();
final List<bool> isSelected = BudgetPeriod.values.map((period) => period == _selectedPeriodFilter).toList();
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
title: const Text(
'Orçamentos',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 15, 16, 24),
shadows: [
Shadow(
blurRadius: 10.0,
color: Color.fromARGB(137, 0, 76, 253),
offset: Offset(0, 0),
),
],
),
),
centerTitle: true,
),
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color(0xFF0A0A16),
Color(0xFF0F0F23),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 100.0, 16.0, 8.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
padding: const EdgeInsets.all(4.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: BudgetPeriod.values.asMap().entries.map((entry) {
int index = entry.key;
BudgetPeriod period = entry.value;
bool selected = isSelected[index];
return Expanded(
child: InkWell(
onTap: () {
setState(() {
_selectedPeriodFilter = period;
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(vertical: 12.0),
decoration: BoxDecoration(
gradient: selected
? const LinearGradient(
colors: [Color(0xFF4B77BE), Color(0xFF2E5894)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: null,
color: selected ? null : Colors.transparent,
borderRadius: BorderRadius.circular(12),
border: selected ? Border.all(color: Colors.blue.shade200) : null,
),
child: Text(
_getPeriodName(period),
style: TextStyle(
color: selected ? Colors.white : Colors.white70,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
),
),
);
}).toList(),
),
),
),
),
),
Expanded(
child: filteredEntries.isEmpty
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.data_usage_outlined, size: 120, color: Colors.blueGrey),
const SizedBox(height: 20),
const Text(
'Nenhum orçamento para este período.',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, color: Colors.white, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
const Text(
'Crie um orçamento e comece a controlar seus gastos!',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey, fontSize: 15),
),
],
),
),
)
: ListView.builder(
itemCount: filteredEntries.length,
itemBuilder: (context, index) {
final entry = filteredEntries[index];
final budget = entry.value;
final key = entry.key;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(15),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(_getCategoryIcon(budget.category), color: Colors.blue[300]),
),
const SizedBox(width: 12),
Text(
_getCategoryName(budget.category),
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
),
],
),
IconButton(
icon: const Icon(Icons.delete_outline, color: Colors.redAccent, size: 24),
onPressed: () async {
bool? confirm = await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: const Color(0xFF1A1A2E),
title: const Text('Confirmar Exclusão', style: TextStyle(color: Colors.white)),
content: const Text('Tem certeza que deseja excluir este orçamento?', style: TextStyle(color: Colors.white70)),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Cancelar', style: TextStyle(color: Colors.white54)),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Excluir', style: TextStyle(color: Colors.red)),
),
],
);
},
);
if (confirm == true) {
await _deleteBudget(key);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Orçamento excluído com sucesso!'),
backgroundColor: Colors.green,
),
);
}
}
},
),
],
),
const SizedBox(height: 15),
Text(
'Período: ${_getPeriodName(budget.period)}',
style: TextStyle(fontSize: 14, color: Colors.white54),
),
Text(
'De ${DateFormat('dd/MM/yyyy').format(budget.startDate)} a ${DateFormat('dd/MM/yyyy').format(budget.endDate)}',
style: TextStyle(fontSize: 14, color: Colors.white54),
),
const SizedBox(height: 20),
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: LinearProgressIndicator(
value: 0.0, // Placeholder
backgroundColor: Colors.white.withOpacity(0.1),
color: Colors.cyan,
minHeight: 12,
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Orçado: ${NumberFormat.currency(locale: 'pt_BR', symbol: 'KZ').format(budget.amount)}',
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: Colors.white),
),
],
),
const SizedBox(height: 15),
if (budget.note != null && budget.note!.isNotEmpty)
Text(
'Nota: ${budget.note}',
style: const TextStyle(fontSize: 14, color: Colors.white38, fontStyle: FontStyle.italic),
),
],
),
),
),
),
);
},
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const CreateBudgetScreen()),
);
if (result == true) {
_loadBudgets();
}
},
tooltip: 'Adicionar Orçamento',
shape: const CircleBorder(),
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
colors: [Color(0xFF4B77BE), Color(0xFF2E5894)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: const Color(0xFF4B77BE).withOpacity(0.5),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: const Icon(Icons.add, color: Colors.white, size: 28),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
);
}
}