209 lines
6.4 KiB
Dart
209 lines
6.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
void main() {
|
|
runApp(const SupermarketCalcApp());
|
|
}
|
|
|
|
class SupermarketCalcApp extends StatelessWidget {
|
|
const SupermarketCalcApp({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MaterialApp(
|
|
title: 'Calc Margen',
|
|
debugShowCheckedModeBanner: false,
|
|
// Configuración Material You (M3)
|
|
theme: ThemeData(
|
|
useMaterial3: true,
|
|
colorScheme: ColorScheme.fromSeed(
|
|
seedColor: Colors.red,
|
|
brightness: Brightness.light,
|
|
),
|
|
),
|
|
home: const HomePage(),
|
|
);
|
|
}
|
|
}
|
|
|
|
class HomePage extends StatefulWidget {
|
|
const HomePage({super.key});
|
|
|
|
@override
|
|
State<HomePage> createState() => _HomePageState();
|
|
}
|
|
|
|
class _HomePageState extends State<HomePage> {
|
|
final _costController = TextEditingController(text: "1.50");
|
|
final _marginController = TextEditingController(text: "40");
|
|
|
|
double _exactPrice = 2.10;
|
|
double _profit = 0.60;
|
|
String _suggestedPrice = "2.50 €";
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_calculate();
|
|
}
|
|
|
|
void _calculate() {
|
|
final double cost = double.tryParse(_costController.text) ?? 0;
|
|
final double marginPercent = double.tryParse(_marginController.text) ?? 0;
|
|
|
|
if (cost <= 0 || marginPercent <= 0) {
|
|
setState(() {
|
|
_suggestedPrice = "---";
|
|
});
|
|
return;
|
|
}
|
|
|
|
_profit = cost * (marginPercent / 100);
|
|
_exactPrice = cost + _profit;
|
|
|
|
// Lógica de Redondeo Psicológico (.50, .95, .99)
|
|
int floorPrice = _exactPrice.floor();
|
|
double finalVal;
|
|
|
|
if (_exactPrice <= 1) {
|
|
finalVal = (_exactPrice < 0.50) ? 0.99 : (_exactPrice.ceil() - 0.01);
|
|
} else {
|
|
if (_exactPrice <= floorPrice + 0.50) {
|
|
finalVal = floorPrice + 0.50;
|
|
} else if (_exactPrice <= floorPrice + 0.95) {
|
|
finalVal = floorPrice + 0.95;
|
|
} else {
|
|
finalVal = floorPrice + 0.99;
|
|
}
|
|
|
|
if (finalVal < _exactPrice) {
|
|
finalVal = _exactPrice.ceil() + 0.99;
|
|
}
|
|
}
|
|
|
|
setState(() {
|
|
_suggestedPrice = "${finalVal.toStringAsFixed(2)} €";
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colors = Theme.of(context).colorScheme;
|
|
|
|
return Scaffold(
|
|
backgroundColor: colors.surface,
|
|
appBar: AppBar(
|
|
title: const Text("Calculadora de Margen"),
|
|
centerTitle: true,
|
|
backgroundColor: colors.surface,
|
|
elevation: 0,
|
|
),
|
|
body: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(24.0),
|
|
child: Column(
|
|
children: [
|
|
// Card de entrada de datos
|
|
Card(
|
|
elevation: 0,
|
|
color: colors.surfaceContainerHighest.withOpacity(0.3),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20.0),
|
|
child: Column(
|
|
children: [
|
|
TextField(
|
|
controller: _costController,
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
onChanged: (_) => _calculate(),
|
|
decoration: InputDecoration(
|
|
labelText: "Costo Base",
|
|
prefixText: "€ ",
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16)),
|
|
filled: true,
|
|
fillColor: colors.surface,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
TextField(
|
|
controller: _marginController,
|
|
keyboardType: TextInputType.number,
|
|
onChanged: (_) => _calculate(),
|
|
decoration: InputDecoration(
|
|
labelText: "Margen Deseado",
|
|
suffixText: "%",
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16)),
|
|
filled: true,
|
|
fillColor: colors.surface,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Resultados Intermedios
|
|
Row(
|
|
children: [
|
|
_buildInfoTile("Precio Exacto", "${_exactPrice.toStringAsFixed(2)} €", colors),
|
|
const SizedBox(width: 12),
|
|
_buildInfoTile("Ganancia", "${_profit.toStringAsFixed(2)} €", colors),
|
|
],
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Card de Resultado Final (Material You Hero)
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16),
|
|
decoration: BoxDecoration(
|
|
color: colors.primaryContainer,
|
|
borderRadius: BorderRadius.circular(28),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Text(
|
|
"PRECIO SUGERIDO",
|
|
style: TextStyle(
|
|
color: colors.onPrimaryContainer,
|
|
fontWeight: FontWeight.w900,
|
|
letterSpacing: 1.2,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
_suggestedPrice,
|
|
style: TextStyle(
|
|
fontSize: 56,
|
|
fontWeight: FontWeight.bold,
|
|
color: colors.onPrimaryContainer,
|
|
),
|
|
),
|
|
Icon(Icons.auto_awesome, color: colors.primary, size: 30),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildInfoTile(String label, String value, ColorScheme colors) {
|
|
return Expanded(
|
|
child: Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: colors.outlineVariant),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(label, style: TextStyle(fontSize: 12, color: colors.secondary)),
|
|
Text(value, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |