- created snackbar, step indicator, money text, text field and progress bar components.

- created wallet balance block, wallet item and kid line chart widgets.
- restructured onboarding, signup and device signup screens into layouts and main screens.
- updated signup and kid wallet screens to 17/11 design.
This commit is contained in:
2025-11-21 15:28:46 +01:00
parent 4225f7510b
commit 8201bff0a7
56 changed files with 1991 additions and 1491 deletions

View File

@@ -4,3 +4,4 @@ export 'src/widgets/deposit_block.dart';
export 'src/screens/connection_error_screen.dart';
export 'src/screens/server_error_screen.dart';
export 'src/screens/no_plan_error_screen.dart';
export 'src/widgets/wallet_balance_block.dart';

View File

@@ -1,10 +1,11 @@
class Kid {
final String name;
final double balance;
final double savings;
const Kid({
required this.name,
required this.balance,
required this.savings,
});
}

View File

@@ -1,6 +1,5 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class DepositBlock extends ConsumerWidget {
@@ -20,40 +19,56 @@ class DepositBlock extends ConsumerWidget {
),
margin: EdgeInsets.only(top: 10),
child: Column(
spacing: 24,
children: [
Text(
"Ingresar dinero en el wallet",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Ingresar dinero en el wallet",
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
),
),
Row(
spacing: 10,
Column(
spacing: 16,
children: [
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: "Cantidad",
hintText: "0€",
border: OutlineInputBorder(),
Row(
spacing: 10,
children: [
Expanded(
child: CustomTextField(
label: "Cantidad",
hint: "0€",
numeric: true,
),
),
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
),
FilledButton(
onPressed: () => {},
style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll<Color>(
theme.getColorFor(ThemeCode.buttonPrimary),
),
shape: WidgetStatePropertyAll(RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(18))
))
),
child: SizedBox(
height: 60,
child: Center(child: Text("Ingresar", style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)))),
),
],
),
FilledButton(
onPressed: () => {},
style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll<Color>(
theme.getColorFor(ThemeCode.buttonPrimary),
),
),
child: Text("Ingresar"),
Align(
alignment: Alignment.topLeft,
child: Row(
spacing: 4,
children: [
Icon(Icons.info_outline, size: 16),
Text("Máximo que puedes añadir: $max"),
],
)
),
],
),
Align(
alignment: Alignment.topLeft,
child: Text("Máximo que puedes añadir: $max"),
),
)
],
),
);

View File

@@ -4,10 +4,7 @@ import 'package:fl_chart/fl_chart.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class LineGraph extends ConsumerStatefulWidget {
final lines = [
[0, 1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 1, 0, 1],
];
final lines = [[0,1,0,1,0,1,0],[1,0,1,0,1,0,1]];
LineGraph({super.key});
@@ -16,7 +13,8 @@ class LineGraph extends ConsumerStatefulWidget {
}
class LineGraphState extends ConsumerState<LineGraph> {
final weekDays = ["L", "M", "X", "J", "V", "S", "D"];
final weekDays = ["L", "M", "X", "J", "V", "S", "D"];
String? timeSpan;
late var days = weekDays;
@@ -31,148 +29,146 @@ class LineGraphState extends ConsumerState<LineGraph> {
final theme = ref.watch(themePortProvider);
return Container(
padding: EdgeInsets.all(15),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
border: BoxBorder.fromLTRB(
left: BorderSide(color: Colors.cyan, width: 5),
),
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
border: BoxBorder.fromLTRB(left: BorderSide(color: Colors.cyan, width: 5)),
borderRadius: BorderRadius.all(Radius.circular(13)),
color: theme.getColorFor(ThemeCode.backgroundPrimary)
),
child: Column(
spacing: 10,
spacing: 32,
children: [
Row(
children: [
Text("Gastos", style: TextStyle(fontWeight: FontWeight.bold)),
Spacer(),
Container(
padding: EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: theme.getColorFor(ThemeCode.backgroundSecondary),
),
child: DropdownButton(
underline: Container(),
value: timeSpan,
onChanged: (String? value) {
setState(() {
timeSpan = value;
});
Row(children: [
Text("Gastos", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 18)),
Spacer(),
Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8)),
color: theme.getColorFor(ThemeCode.backgroundSecondary),
),
child: DropdownButton(
underline: Container(),
value: timeSpan,
onChanged: (String? value) {
setState(() {
timeSpan = value;
});
},
dropdownColor: theme.getColorFor(ThemeCode.backgroundPrimary),
items: [
DropdownMenuItem(value: "day", child: Text("Hoy", style: TextStyle(fontSize: 14, letterSpacing: 0))),
DropdownMenuItem(value: "week", child: Text("Esta semana", style: TextStyle(fontSize: 14, letterSpacing: 0))),
DropdownMenuItem(value: "month", child: Text("Este mes", style: TextStyle(fontSize: 14, letterSpacing: 0))),
]
),
)
]),
SizedBox(
height: 160,
child: LineChart(LineChartData(
gridData: FlGridData(
show: true,
drawHorizontalLine: false,
drawVerticalLine: true,
verticalInterval: 1,
getDrawingVerticalLine: (value)=>FlLine(strokeWidth: 43, color: theme.getColorFor(ThemeCode.backgroundSecondary))
),
titlesData: FlTitlesData(
bottomTitles: AxisTitles(
sideTitles: SideTitles(
interval: 1,
showTitles: true,
reservedSize: 40,
getTitlesWidget: (double value, TitleMeta meta){
String text = weekDays[value.toInt()];
return SideTitleWidget(
space: 4,
meta: meta,
child: Expanded(child: Center(child: Text(text, style: TextStyle(fontSize: 12)))),
);
},
dropdownColor: theme.getColorFor(ThemeCode.backgroundPrimary),
items: [
DropdownMenuItem(value: "day", child: Text("Hoy")),
DropdownMenuItem(value: "week", child: Text("Esta semana")),
DropdownMenuItem(value: "month", child: Text("Este mes")),
],
),
),
],
),
Expanded(
child: LineChart(
LineChartData(
gridData: FlGridData(
show: true,
drawHorizontalLine: false,
drawVerticalLine: true,
verticalInterval: 1,
leftTitles: AxisTitles(),
topTitles: AxisTitles(),
rightTitles: AxisTitles()
),
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
tooltipBorderRadius: BorderRadius.all(Radius.circular(7)),
getTooltipColor: (spot) => theme.getColorFor(ThemeCode.buttonSecondary),
getTooltipItems: (List<LineBarSpot> touchedSpots) {
return touchedSpots.map((LineBarSpot touchedSpot) {
return LineTooltipItem(
"${touchedSpot.y}",
TextStyle(fontWeight: FontWeight.w500, fontSize: 14, color: theme.getColorFor(ThemeCode.textSecondary)),
);
}).toList();
},
),
getTouchedSpotIndicator: (
_,
indicators,
) {
return indicators
.map((int index) => const TouchedSpotIndicatorData(
FlLine(color: Colors.transparent),
FlDotData(show: false),
))
.toList();
},
touchSpotThreshold: 25,
distanceCalculator:
(Offset touchPoint, Offset spotPixelCoordinates) =>
(touchPoint - spotPixelCoordinates).distance,
),
borderData: FlBorderData(
show: true,
border: Border(
bottom: BorderSide(
color: Colors.lightBlue.withValues(alpha: 0.2),
width: 4
),
titlesData: FlTitlesData(
//show: false,
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 40,
getTitlesWidget: (double value, TitleMeta meta) =>
SideTitleWidget(
space: 4,
meta: meta,
/*fitInside: fitInsideBottomTitle
? SideTitleFitInsideData.fromTitleMeta(meta, distanceFromEdge: 0)
: SideTitleFitInsideData.disable(),*/
child: Text(weekDays[value.toInt()]),
),
),
),
leftTitles: AxisTitles(),
topTitles: AxisTitles(),
rightTitles: AxisTitles(),
),
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
getTooltipColor: (touchedSpot) =>
theme.getColorFor(ThemeCode.buttonSecondary),
getTooltipItems: (List<LineBarSpot> touchedBarSpots) {
return touchedBarSpots.map((barSpot) {
return LineTooltipItem(
"${barSpot.y}",
TextStyle(
color: theme.getColorFor(ThemeCode.textSecondary),
),
);
}).toList();
},
),
),
borderData: FlBorderData(
show: true,
border: Border(
bottom: BorderSide(
color: Colors.lightBlue.withValues(alpha: 0.2),
width: 4,
),
left: const BorderSide(color: Colors.transparent),
right: const BorderSide(color: Colors.transparent),
top: const BorderSide(color: Colors.transparent),
),
),
lineBarsData: [
LineChartBarData(
isCurved: true,
color: Colors.pink,
barWidth: 5,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(show: false),
spots: const [
FlSpot(0, 1),
FlSpot(1, 0),
FlSpot(2, 1),
FlSpot(3, 0),
FlSpot(4, 1),
FlSpot(5, 0),
FlSpot(6, 1),
],
),
LineChartBarData(
isCurved: true,
color: Colors.cyan,
barWidth: 5,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(show: false),
spots: const [
FlSpot(0, 0),
FlSpot(1, 1),
FlSpot(2, 0),
FlSpot(3, 1),
FlSpot(4, 0),
FlSpot(5, 1),
FlSpot(6, 0),
],
),
],
minX: 0,
maxX: days.length - 1,
maxY: 1,
minY: 0,
left: const BorderSide(color: Colors.transparent),
right: const BorderSide(color: Colors.transparent),
top: const BorderSide(color: Colors.transparent),
),
),
),
lineBarsData: [
LineChartBarData(
isCurved: true,
color: Colors.pink,
barWidth: 5,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(show: false),
spots: List<FlSpot>.generate(days.length, (int index){
return FlSpot(index.toDouble(), (index+1)%2);
})
),
LineChartBarData(
isCurved: true,
color: Colors.cyan,
barWidth: 5,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(show: false),
spots: List<FlSpot>.generate(days.length, (int index){
return FlSpot(index.toDouble(), index%2);
})
),
],
minX: 0,
maxX: days.length-1,
maxY: 1,
minY: 0,
))
)
],
),
);
}
}
}

View File

@@ -0,0 +1,59 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class WalletBalanceBlock extends ConsumerWidget {
final double max;
final double value;
final double savings;
final double savingsPlan;
const WalletBalanceBlock({
super.key,
required this.max,
required this.value,
required this.savings,
this.savingsPlan = 30.0
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Column(
spacing: 16,
children: [
Row(
children: [
Text(
"Wallet",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Spacer(),
MoneyText(
text: "$max€ total",
size: 26,
secondarySize: 16,
color: theme.getColorFor(ThemeCode.textPrimary),
),
],
),
Row(children: [
Text("Objetivos de ahorro"),
Spacer(),
Text("$savingsPlan")
]),
ProgressBar(savingsPlan, savings, 24, 16, 12, theme.getColorFor(ThemeCode.backgroundSecondary), theme.getColorFor(ThemeCode.backgroundTertiary), theme.getColorFor(ThemeCode.textPrimary)),
ProgressBar(max, value, 83, 40, 24, theme.getColorFor(ThemeCode.backgroundTertiary), theme.getColorFor(ThemeCode.buttonPrimary), theme.getColorFor(ThemeCode.textSecondary)),
Center(child: Text("Disponible")),
],
),
);
}
}