- created custom text block, dropdown,
- created extract, goals and block card screen. - generated tests for design system components. - updated restore password and home screens to 17/11 design.
This commit is contained in:
@@ -4,4 +4,8 @@ export 'src/steps/step_indicator.dart';
|
||||
export 'src/texts/money_text.dart';
|
||||
export 'src/progress_bars/progress_bar.dart';
|
||||
export 'src/inputs/textfields.dart';
|
||||
export 'src/snackbars/snackbar.dart';
|
||||
export 'src/snackbars/snackbar.dart';
|
||||
export 'src/buttons/primary_button.dart';
|
||||
export 'src/buttons/secondary_button.dart';
|
||||
export 'src/buttons/custom_text_button.dart';
|
||||
export 'src/dropdowns/dropdown.dart';
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomTextButton extends StatelessWidget{
|
||||
|
||||
final onPressed;
|
||||
final String text;
|
||||
final double size;
|
||||
final FontWeight weight;
|
||||
final Color? color;
|
||||
|
||||
@override
|
||||
const CustomTextButton({
|
||||
super.key,
|
||||
required this.onPressed,
|
||||
required this.text,
|
||||
this.size = 14,
|
||||
this.weight = FontWeight.normal,
|
||||
this.color
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return TextButton(
|
||||
style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.zero)),
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: size,
|
||||
fontWeight: weight,
|
||||
letterSpacing: 0,
|
||||
color: color?? Color(0xFF4B4B4B),
|
||||
decoration: TextDecoration.underline
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
54
packages/design_system/lib/src/buttons/primary_button.dart
Normal file
54
packages/design_system/lib/src/buttons/primary_button.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class PrimaryButton extends StatelessWidget{
|
||||
final onPressed;
|
||||
final String text;
|
||||
final Color color;
|
||||
final double height;
|
||||
final double? width;
|
||||
final double size;
|
||||
final double radius;
|
||||
final double padding;
|
||||
|
||||
PrimaryButton({
|
||||
required this.onPressed,
|
||||
required this.text,
|
||||
required this.color,
|
||||
this.height = 60,
|
||||
this.width,
|
||||
this.size = 18,
|
||||
this.radius = 18,
|
||||
this.padding = 0,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return FilledButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStatePropertyAll<Color>(color),
|
||||
padding: WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: padding)),
|
||||
shape: WidgetStatePropertyAll(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(radius)),
|
||||
)),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: Center(child: Text(
|
||||
text,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: size,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: 0,
|
||||
color: Colors.white//theme.getColorFor(ThemeCode.textSecondary)
|
||||
)
|
||||
))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
67
packages/design_system/lib/src/buttons/secondary_button.dart
Normal file
67
packages/design_system/lib/src/buttons/secondary_button.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class SecondaryButton extends StatelessWidget {
|
||||
|
||||
final onPressed;
|
||||
final String? text;
|
||||
final IconData? icon;
|
||||
final String? label;
|
||||
final Color? color;
|
||||
final double radius;
|
||||
final double padding;
|
||||
final double height;
|
||||
final double? width;
|
||||
final double? size;
|
||||
|
||||
@override
|
||||
SecondaryButton({
|
||||
required this.onPressed,
|
||||
this.text,
|
||||
this.icon,
|
||||
this.label,
|
||||
this.color,
|
||||
this.radius = 18,
|
||||
this.padding = 0,
|
||||
this.height = 60,
|
||||
this.width,
|
||||
this.size,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return OutlinedButton(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: padding)),
|
||||
shape: WidgetStatePropertyAll(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(radius)),
|
||||
side: BorderSide(color: Color(0xFF4B4B4B))
|
||||
)),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: Center(child: text!=null ? Text(
|
||||
text!,
|
||||
textAlign: TextAlign.center,
|
||||
semanticsLabel: label,
|
||||
style: TextStyle(
|
||||
fontSize: size ?? 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: 0,
|
||||
color: Color(0xFF4B4B4B)
|
||||
)
|
||||
) : Icon(
|
||||
icon,
|
||||
semanticLabel: label,
|
||||
size: size ?? 24,
|
||||
color: color ?? Color(0xFF4B4B4B)
|
||||
))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
67
packages/design_system/lib/src/dropdowns/dropdown.dart
Normal file
67
packages/design_system/lib/src/dropdowns/dropdown.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class CustomDropdown extends StatelessWidget{
|
||||
final List<Widget> items;
|
||||
final values;
|
||||
final onChanged;
|
||||
final value;
|
||||
final String? hint;
|
||||
final String? label;
|
||||
final double radius;
|
||||
final double height;
|
||||
final double width;
|
||||
final Color? color;
|
||||
|
||||
const CustomDropdown({
|
||||
super.key,
|
||||
required this.items,
|
||||
this.values,
|
||||
required this.onChanged,
|
||||
this.value,
|
||||
this.hint,
|
||||
this.label,
|
||||
this.radius = 12,
|
||||
this.width = double.infinity,
|
||||
this.height = 70,
|
||||
this.color
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (label != null) Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: Text(
|
||||
label!,
|
||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: Center(child: DropdownButtonFormField(
|
||||
dropdownColor: Colors.white,
|
||||
decoration: InputDecoration(
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(radius)),
|
||||
borderSide: BorderSide(color: color??Color(0xFF4B4B4B))
|
||||
)
|
||||
),
|
||||
//underline: Container(),
|
||||
initialValue: value,
|
||||
onChanged: onChanged,
|
||||
hint: Text(hint??""),
|
||||
items: List<DropdownMenuItem>.generate(items.length, (int index){
|
||||
return DropdownMenuItem(value: (values!=null)?values[index]:index, child: items[index]);
|
||||
})
|
||||
)),
|
||||
)
|
||||
],
|
||||
) ;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class CustomTextField extends ConsumerStatefulWidget{
|
||||
class CustomTextField extends StatefulWidget{
|
||||
bool? showPassword;
|
||||
final bool numeric;
|
||||
final String hint;
|
||||
@@ -24,49 +24,60 @@ class CustomTextField extends ConsumerStatefulWidget{
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<CustomTextField> createState() => CustomTextFieldState();
|
||||
State<CustomTextField> createState() => CustomTextFieldState();
|
||||
}
|
||||
|
||||
class CustomTextFieldState extends ConsumerState<CustomTextField>{
|
||||
class CustomTextFieldState extends State<CustomTextField>{
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
|
||||
return TextFormField(
|
||||
keyboardType: widget.numeric? TextInputType.number : TextInputType.text,
|
||||
obscureText: !(widget.showPassword ?? true),
|
||||
enableSuggestions: widget.showPassword ?? true,
|
||||
autocorrect: !(widget.showPassword ?? false),
|
||||
style: TextStyle(color: theme.getColorFor(ThemeCode.buttonSecondary)),
|
||||
inputFormatters: widget.numeric? [
|
||||
FilteringTextInputFormatter.digitsOnly
|
||||
] : [],
|
||||
decoration: InputDecoration(
|
||||
counterText: "",
|
||||
hintText: widget.hint,
|
||||
labelText: widget.label,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderSide: BorderSide(color: theme.getColorFor(ThemeCode.textPrimary)),
|
||||
gapPadding: 16
|
||||
return Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
?widget.label == '' ? null : Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: Text(
|
||||
widget.label,
|
||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||
)
|
||||
),
|
||||
suffixIcon: widget.showPassword!=null ? IconButton(
|
||||
icon: Icon(widget.showPassword!
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
widget.showPassword = !widget.showPassword!;
|
||||
});
|
||||
},
|
||||
) : null,
|
||||
),
|
||||
minLines: widget.lines ?? 1,
|
||||
maxLines: widget.lines ?? 1,
|
||||
maxLength: widget.length,
|
||||
onChanged: widget.onChanged ?? (_)=>{},
|
||||
TextFormField(
|
||||
keyboardType: widget.numeric? TextInputType.number : TextInputType.text,
|
||||
obscureText: !(widget.showPassword ?? true),
|
||||
enableSuggestions: widget.showPassword ?? true,
|
||||
autocorrect: !(widget.showPassword ?? false),
|
||||
style: TextStyle(color: Color(0xFF4B4B4B)),
|
||||
inputFormatters: widget.numeric? [
|
||||
FilteringTextInputFormatter.digitsOnly
|
||||
] : [],
|
||||
decoration: InputDecoration(
|
||||
counterText: "",
|
||||
hintText: widget.hint,
|
||||
//labelText: widget.label,
|
||||
//floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderSide: BorderSide(color: Color(0xFF4B4B4B)),
|
||||
gapPadding: 16
|
||||
),
|
||||
suffixIcon: widget.showPassword!=null ? IconButton(
|
||||
icon: Icon(widget.showPassword!
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
widget.showPassword = !widget.showPassword!;
|
||||
});
|
||||
},
|
||||
) : null,
|
||||
),
|
||||
minLines: widget.lines ?? 1,
|
||||
maxLines: widget.lines ?? 1,
|
||||
maxLength: widget.length,
|
||||
onChanged: widget.onChanged ?? (_)=>{},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,15 @@ class ProgressBar extends ConsumerWidget{
|
||||
final Color foregroundColor;
|
||||
final Color textColor;
|
||||
|
||||
const ProgressBar(
|
||||
this.max,
|
||||
this.value,
|
||||
this.height,
|
||||
this.textSize,
|
||||
this.textSecondarySize,
|
||||
this.backgroundColor,
|
||||
this.foregroundColor,
|
||||
this.textColor,
|
||||
const ProgressBar({
|
||||
required this.max,
|
||||
required this.value,
|
||||
required this.height,
|
||||
required this.textSize,
|
||||
required this.textSecondarySize,
|
||||
required this.backgroundColor,
|
||||
required this.foregroundColor,
|
||||
required this.textColor,}
|
||||
);
|
||||
|
||||
@override
|
||||
|
||||
@@ -9,25 +9,24 @@ enum MessageType {
|
||||
success
|
||||
}
|
||||
|
||||
class CustomSnackBar extends ConsumerWidget{
|
||||
final MessageType type;
|
||||
class CustomSnackBar extends StatelessWidget{
|
||||
final MessageType? type;
|
||||
final String message;
|
||||
|
||||
CustomSnackBar({
|
||||
const CustomSnackBar({
|
||||
super.key,
|
||||
this.type = MessageType.info,
|
||||
this.type,
|
||||
required this.message,
|
||||
});
|
||||
|
||||
@override
|
||||
SnackBar build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
SnackBar build(BuildContext context) {
|
||||
|
||||
late final Color foregroundColor;
|
||||
late final Color backgroundColor;
|
||||
late final IconData icon;
|
||||
|
||||
switch (type){
|
||||
switch (type??MessageType.info){
|
||||
case MessageType.info:
|
||||
backgroundColor = Color(0xFFE3EFFD);
|
||||
foregroundColor = Color(0xFF1F4ECF);
|
||||
@@ -57,7 +56,7 @@ class CustomSnackBar extends ConsumerWidget{
|
||||
spacing: 8,
|
||||
children: [
|
||||
Icon(icon, color: foregroundColor),
|
||||
Expanded(child: Text(message, style: TextStyle(color: theme.getColorFor(ThemeCode.textPrimary), fontSize: 14)))
|
||||
Expanded(child: Text(message, style: TextStyle(color: Color(0xFF4B4B4B), fontSize: 14)))
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -18,11 +18,12 @@ class MoneyText extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final units = text.split(".")[0];
|
||||
final cents = ",${text.split(".")[1]}";
|
||||
final split = text.contains(".") ? text.split(".") : text.split("€");
|
||||
final mainText = split[0];
|
||||
var secondaryText = text.contains(".") ? ",${split[1]}" : "€${split[1]}";
|
||||
|
||||
return Text.rich(TextSpan(
|
||||
text: units,
|
||||
text: mainText,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: size,
|
||||
@@ -31,7 +32,7 @@ class MoneyText extends StatelessWidget {
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: cents,
|
||||
text: secondaryText,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: secondarySize ?? size,
|
||||
|
||||
@@ -20,12 +20,14 @@ dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^5.0.0
|
||||
golden_toolkit: ^0.15.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your package, add an assets section, like this:
|
||||
# assets:
|
||||
|
||||
201
packages/design_system/test/widget_test.dart
Normal file
201
packages/design_system/test/widget_test.dart
Normal file
@@ -0,0 +1,201 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
|
||||
void main() {
|
||||
themePackages();
|
||||
|
||||
testGoldens('Step Indicator', (tester) async {
|
||||
final widget = (int step)=>StepIndicator(max: 3, current: step, color: Colors.blueAccent);
|
||||
final builder = GoldenBuilder.column()
|
||||
..addScenario('step -1', widget(-1))
|
||||
..addScenario('step 0', widget(0))
|
||||
..addScenario('step 1', widget(1))
|
||||
..addScenario('step 2', widget(2))
|
||||
..addScenario('step 3', widget(3))
|
||||
..addScenario('step 4', widget(4));
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
await screenMatchesGolden(tester, 'step_indicator');
|
||||
});
|
||||
|
||||
testGoldens('Progress Bar - Small', (tester) async {
|
||||
final widget = (double value, double max) => ProgressBar(max: max, value: value, height: 24, textSize: 16, textSecondarySize: 12, backgroundColor: Colors.blueGrey, foregroundColor: Colors.blueAccent, textColor: Colors.black87);
|
||||
|
||||
final builder = GoldenBuilder.column()
|
||||
..addScenario('full', widget(100, 100))
|
||||
..addScenario('empty', widget(0, 100))
|
||||
..addScenario('half', widget(75, 150))
|
||||
..addScenario('overflowing', widget(200, 150))
|
||||
//..addScenario('negative', widget(-20, 83))
|
||||
..addScenario('tiny value', widget(2.15, 150));
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
await screenMatchesGolden(tester, 'progress_bar_small');
|
||||
});
|
||||
|
||||
testGoldens('Progress Bar - Large', (tester) async {
|
||||
final widget = (double value, double max) => ProgressBar(max: max, value: value, height: 83, textSize: 40, textSecondarySize: 24, backgroundColor: Colors.blueGrey, foregroundColor: Colors.blueAccent, textColor: Colors.black87);
|
||||
|
||||
final builder = GoldenBuilder.grid(columns: 3, widthToHeightRatio: 1)
|
||||
..addScenario('full', widget(100, 100))
|
||||
..addScenario('empty', widget(0, 100))
|
||||
..addScenario('half', widget(75, 150))
|
||||
..addScenario('overflowing', widget(200, 150))
|
||||
..addScenario('tiny value', widget(2.15, 150));
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
await screenMatchesGolden(tester, 'progress_bar_large');
|
||||
});
|
||||
|
||||
testGoldens('Text Field', (tester) async {
|
||||
|
||||
final builder = GoldenBuilder.grid(columns: 3, widthToHeightRatio: 1)
|
||||
..addScenario('basic', SizedBox(height: 70, width: 250, child: CustomTextField()))
|
||||
..addScenario('hint', SizedBox(height: 70, width: 250, child: CustomTextField(hint: "type something")))
|
||||
..addScenario('label', SizedBox(height: 100, width: 250, child: CustomTextField(hint: "type something", label: "text input")))
|
||||
..addScenario('numeric', SizedBox(height: 70, width: 250, child: CustomTextField(numeric: true)))
|
||||
..addScenario('password', SizedBox(height: 70, width: 250, child: CustomTextField(showPassword: false)))
|
||||
..addScenario('multiline', SizedBox(height: 200, width: 250, child: CustomTextField(lines: 4)));
|
||||
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
await screenMatchesGolden(tester, 'textfield');
|
||||
});
|
||||
|
||||
testGoldens('Primary Button', (tester) async {
|
||||
final widget = ({onPressed, text}) => SizedBox(
|
||||
height: 70, width: 250,
|
||||
child:PrimaryButton(onPressed: onPressed, text: text, color: Colors.blueAccent)
|
||||
);
|
||||
|
||||
final builder = GoldenBuilder.grid(columns: 2, widthToHeightRatio: 1)
|
||||
..addScenario('empty', SizedBox(height: 70, width: 250, child: PrimaryButton(onPressed: ()=>{}, text: "", color: Colors.blueAccent)))
|
||||
..addScenario('basic', SizedBox(height: 70, width: 250, child: PrimaryButton(onPressed: ()=>{}, text: "press me", color: Colors.blueAccent)))
|
||||
..addScenario('round', SizedBox(height: 70, width: 250, child: PrimaryButton(onPressed: ()=>{}, radius: 100, text: "press me", color: Colors.blueAccent)))
|
||||
..addScenario('small', SizedBox(height: 70, width: 250, child: PrimaryButton(onPressed: ()=>{}, width: 100, text: "press me", color: Colors.blueAccent)));
|
||||
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
|
||||
await screenMatchesGolden(tester, 'primary_button');
|
||||
});
|
||||
|
||||
testGoldens('Text Button', (tester) async {
|
||||
final tapTarget = Key("tap-target");
|
||||
|
||||
final builder = GoldenBuilder.column()
|
||||
..addScenario('empty', CustomTextButton(onPressed: ()=>{}, text: ""))
|
||||
..addScenario('basic', CustomTextButton(onPressed: ()=>{}, text: "press me"))
|
||||
..addScenario('tapped', CustomTextButton(onPressed: ()=>{}, text: "press me", key: tapTarget))
|
||||
..addScenario('large text', CustomTextButton(onPressed: ()=>{}, text: "press me", size: 40, weight: FontWeight.w500))
|
||||
..addScenario('small text', CustomTextButton(onPressed: ()=>{}, text: "press me", size: 10))
|
||||
..addScenario('colored', CustomTextButton(onPressed: ()=>{}, text: "press me", color: Colors.blueAccent));
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
await tester.tap(find.byKey(tapTarget));
|
||||
await tester.pump();
|
||||
await screenMatchesGolden(tester, 'text_button');
|
||||
});
|
||||
|
||||
testGoldens('Money Text', (tester) async {
|
||||
final builder = GoldenBuilder.column()
|
||||
..addScenario('basic', MoneyText(text: "29.13€", size: 20, color: Colors.blueAccent))
|
||||
..addScenario('without cents', MoneyText(text: "50€", size: 20, color: Colors.blueAccent))
|
||||
..addScenario('different sizes', MoneyText(text: "29.13€", size: 30, secondarySize: 15, color: Colors.blueAccent))
|
||||
..addScenario('different sizes without cents', MoneyText(text: "50€", size: 30, secondarySize: 15, color: Colors.blueAccent));
|
||||
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
|
||||
await screenMatchesGolden(tester, 'money_text');
|
||||
});
|
||||
|
||||
testGoldens('Secondary Button', (tester) async {
|
||||
final widget = ({onPressed, text}) => SizedBox(
|
||||
height: 70, width: 250,
|
||||
child:SecondaryButton(onPressed: onPressed, text: text)
|
||||
);
|
||||
|
||||
final builder = GoldenBuilder.grid(columns: 3, widthToHeightRatio: 1)
|
||||
..addScenario('empty', SizedBox(height: 70, width: 250, child:SecondaryButton(onPressed: ()=>{}, text: "")))
|
||||
..addScenario('text', SizedBox(height: 70, width: 250, child:SecondaryButton(onPressed: ()=>{}, text: "press me")))
|
||||
..addScenario('icon', SizedBox(height: 70, width: 250, child:SecondaryButton(onPressed: ()=>{}, icon: Icons.account_circle_outlined)))
|
||||
..addScenario('colored', SizedBox(height: 70, width: 250, child:SecondaryButton(onPressed: ()=>{}, text: "press me", color: Colors.blueAccent,)))
|
||||
..addScenario('small', SizedBox(height: 70, width: 250, child:SecondaryButton(onPressed: ()=>{}, width: 100, text: "press me")))
|
||||
..addScenario('round', SizedBox(height: 70, width: 250, child:SecondaryButton(onPressed: ()=>{}, radius: 100, text: "press me", color: Colors.blueAccent,)));
|
||||
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
|
||||
await screenMatchesGolden(tester, 'secondary_button');
|
||||
});
|
||||
|
||||
final snackbarTest = ({message, type, testName}) {
|
||||
testGoldens('Snackbar $testName', (tester) async {
|
||||
const Key tapTarget = Key('tap-target');
|
||||
|
||||
final widget = () =>
|
||||
SizedBox(
|
||||
width: 800,
|
||||
height: 600,
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
CustomSnackBar(
|
||||
message: message,
|
||||
type: type,
|
||||
).build(context)
|
||||
);
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
key: tapTarget,
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final builder = DeviceBuilder()
|
||||
..overrideDevicesForAllScenarios(devices: [
|
||||
Device(size: Size(750, 550), name: 'base')
|
||||
])
|
||||
..addScenario(name: testName, widget: widget());
|
||||
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
await tester.tap(find.byKey(tapTarget));
|
||||
|
||||
await screenMatchesGolden(tester, 'snackbar/$testName');
|
||||
});
|
||||
};
|
||||
|
||||
final shortText = "Mensaje de prueba";
|
||||
final longText = "Mensaje de prueba largo para comprobar los casos en los que el texto ocupa varias líneas";
|
||||
|
||||
snackbarTest(message: "", type: null, testName: "default_empty");
|
||||
snackbarTest(message: shortText, type: null, testName: "default");
|
||||
snackbarTest(message: longText, type: null, testName: "default_long_text");
|
||||
snackbarTest(message: "", type: MessageType.info, testName: "info_empty");
|
||||
snackbarTest(message: shortText, type: MessageType.info, testName: "info");
|
||||
snackbarTest(message: longText, type: MessageType.info, testName: "info_long_text");
|
||||
snackbarTest(message: "", type: MessageType.success, testName: "success_empty");
|
||||
snackbarTest(message: shortText, type: MessageType.success, testName: "success");
|
||||
snackbarTest(message: longText, type: MessageType.success, testName: "success_long_text");
|
||||
snackbarTest(message: "", type: MessageType.warning, testName: "warning_empty");
|
||||
snackbarTest(message: shortText, type: MessageType.warning, testName: "warning");
|
||||
snackbarTest(message: longText, type: MessageType.warning, testName: "warning_long_text");
|
||||
snackbarTest(message: "", type: MessageType.error, testName: "error_empty");
|
||||
snackbarTest(message: shortText, type: MessageType.error, testName: "error");
|
||||
snackbarTest(message: longText, type: MessageType.error, testName: "error_long_text");
|
||||
|
||||
testGoldens('Dropdown', (tester) async {
|
||||
final List<Widget> emptyList = [];
|
||||
final List<Widget> shortList = [Text("A"), Text("B"), Text("C")];
|
||||
|
||||
final builder = GoldenBuilder.column()
|
||||
..addScenario('empty', CustomDropdown(items: emptyList, onChanged: (_)=>{}, width: 200))
|
||||
..addScenario('initial value', CustomDropdown(items: shortList, value: 1, onChanged: (_)=>{}, width: 300))
|
||||
..addScenario('hint', CustomDropdown(items: shortList, hint: "choose an option", onChanged: (_)=>{}))
|
||||
..addScenario('label', CustomDropdown(items: shortList, label: "select", onChanged: (_)=>{}, width: 200));
|
||||
await tester.pumpWidgetBuilder(builder.build());
|
||||
await screenMatchesGolden(tester, 'dropdown');
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
export 'src/models/kid.dart';
|
||||
export 'src/models/task.dart';
|
||||
export 'src/models/savings_goal.dart';
|
||||
export 'src/widgets/line_graph.dart';
|
||||
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';
|
||||
export 'src/widgets/wallet_balance_block.dart';
|
||||
12
packages/sf_shared/lib/src/models/savings_goal.dart
Normal file
12
packages/sf_shared/lib/src/models/savings_goal.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
class SavingsGoal{
|
||||
|
||||
final String name;
|
||||
final double goal;
|
||||
final double saved;
|
||||
|
||||
const SavingsGoal({
|
||||
required this.name,
|
||||
required this.goal,
|
||||
required this.saved
|
||||
});
|
||||
}
|
||||
21
packages/sf_shared/lib/src/models/task.dart
Normal file
21
packages/sf_shared/lib/src/models/task.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
class Task{
|
||||
|
||||
final double rewardAmount;
|
||||
final List<Subtask> subtasks;
|
||||
|
||||
const Task({
|
||||
required this.rewardAmount,
|
||||
this.subtasks = const []
|
||||
});
|
||||
}
|
||||
|
||||
class Subtask{
|
||||
|
||||
final String name;
|
||||
final bool completed;
|
||||
|
||||
const Subtask({
|
||||
required this.name,
|
||||
required this.completed
|
||||
});
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class DepositBlock extends ConsumerWidget {
|
||||
spacing: 16,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 10,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomTextField(
|
||||
@@ -41,20 +41,15 @@ class DepositBlock extends ConsumerWidget {
|
||||
numeric: true,
|
||||
),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => {},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStatePropertyAll<Color>(
|
||||
theme.getColorFor(ThemeCode.buttonPrimary),
|
||||
),
|
||||
shape: WidgetStatePropertyAll(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(18))
|
||||
))
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: PrimaryButton(
|
||||
onPressed: () => {},
|
||||
text: "Ingresar",
|
||||
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||
padding: 24,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 60,
|
||||
child: Center(child: Text("Ingresar", style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)))),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Align(
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
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,3,0,1,0],[1,0,1,0,4,0,1]];
|
||||
late final maxValue = lines.map((x)=>x.reduce(max)).reduce(max);
|
||||
|
||||
LineGraph({super.key});
|
||||
|
||||
@@ -42,25 +45,22 @@ class LineGraphState extends ConsumerState<LineGraph> {
|
||||
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(),
|
||||
child:
|
||||
CustomDropdown(
|
||||
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))),
|
||||
]
|
||||
Text("Hoy", style: TextStyle(fontSize: 14, letterSpacing: 0)),
|
||||
Text("Esta semana", style: TextStyle(fontSize: 14, letterSpacing: 0)),
|
||||
Text("Este mes", style: TextStyle(fontSize: 14, letterSpacing: 0))
|
||||
],
|
||||
values: ["day", "week", "month"],
|
||||
onChanged: (value)=>{},
|
||||
color: Colors.transparent,
|
||||
width: 151,
|
||||
),
|
||||
)
|
||||
]),
|
||||
@@ -136,33 +136,26 @@ class LineGraphState extends ConsumerState<LineGraph> {
|
||||
top: const BorderSide(color: Colors.transparent),
|
||||
),
|
||||
),
|
||||
lineBarsData: [
|
||||
lineBarsData: List<LineChartBarData>.generate(widget.lines.length, (int i)=>
|
||||
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);
|
||||
})
|
||||
isCurved: true,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
colors: theme.getCardColorFor(i)
|
||||
),
|
||||
barWidth: 5,
|
||||
isStrokeCapRound: true,
|
||||
dotData: const FlDotData(show: false),
|
||||
belowBarData: BarAreaData(show: false),
|
||||
spots: List<FlSpot>.generate(widget.lines[i].length, (int j){
|
||||
return FlSpot(j.toDouble(), widget.lines[i][j].toDouble());
|
||||
})
|
||||
),
|
||||
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,
|
||||
maxY: widget.maxValue.toDouble(),
|
||||
minY: 0,
|
||||
))
|
||||
)
|
||||
|
||||
@@ -49,8 +49,26 @@ class WalletBalanceBlock extends ConsumerWidget {
|
||||
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)),
|
||||
ProgressBar(
|
||||
max: savingsPlan,
|
||||
value: savings,
|
||||
height: 24,
|
||||
textSize: 16,
|
||||
textSecondarySize: 12,
|
||||
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
|
||||
foregroundColor: theme.getColorFor(ThemeCode.backgroundTertiary),
|
||||
textColor: theme.getColorFor(ThemeCode.textPrimary)
|
||||
),
|
||||
ProgressBar(
|
||||
max: max,
|
||||
value: value,
|
||||
height: 83,
|
||||
textSize: 40,
|
||||
textSecondarySize: 24,
|
||||
backgroundColor: theme.getColorFor(ThemeCode.backgroundTertiary),
|
||||
foregroundColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||
textColor: theme.getColorFor(ThemeCode.textSecondary)
|
||||
),
|
||||
Center(child: Text("Disponible")),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user