From 19832a3db9196c3d0eb22b2b5a9b80ebd99b1c7e Mon Sep 17 00:00:00 2001 From: Sol Mendiola <57235692+SolMendiola@users.noreply.github.com> Date: Tue, 19 Dec 2023 15:07:57 -0300 Subject: [PATCH 1/2] feat: add text fields, text area, dropdown comun, dropdown multiple choice, dialog and selection control (#197) --- .../lib/catalog/catalog_app_checkbox.dart | 27 +++++ .../lib/catalog/catalog_app_dialog.dart | 24 ++++ .../catalog/catalog_app_dropdown_screen.dart | 33 ++++++ .../lib/catalog/catalog_app_radio_button.dart | 26 +++++ .../catalog_app_text_fields_screen.dart | 65 +++++++++++ .../catalog/catalog_text_fields_screen.dart | 15 --- .../gallery/lib/main/catalog_main_screen.dart | 20 ++++ .../gallery/lib/router/catalog_router.dart | 10 +- .../gallery/lib/router/catalog_router.gr.dart | 80 +++++++++++++ catalog/gallery/pubspec.yaml | 3 + catalog/lib/theme/app_theme.dart | 106 ++++++++++++++++++ catalog/lib/widgets/app_checkbox.dart | 67 +++++++++++ catalog/lib/widgets/app_dialog.dart | 92 +++++++++++++++ catalog/lib/widgets/app_dropdown.dart | 53 +++++++++ catalog/lib/widgets/app_radio_button.dart | 64 +++++++++++ catalog/lib/widgets/app_text_fields.dart | 93 +++++++++++++++ 16 files changed, 762 insertions(+), 16 deletions(-) create mode 100644 catalog/gallery/lib/catalog/catalog_app_checkbox.dart create mode 100644 catalog/gallery/lib/catalog/catalog_app_dialog.dart create mode 100644 catalog/gallery/lib/catalog/catalog_app_dropdown_screen.dart create mode 100644 catalog/gallery/lib/catalog/catalog_app_radio_button.dart create mode 100644 catalog/gallery/lib/catalog/catalog_app_text_fields_screen.dart delete mode 100644 catalog/gallery/lib/catalog/catalog_text_fields_screen.dart create mode 100644 catalog/lib/widgets/app_checkbox.dart create mode 100644 catalog/lib/widgets/app_dialog.dart create mode 100644 catalog/lib/widgets/app_dropdown.dart create mode 100644 catalog/lib/widgets/app_radio_button.dart create mode 100644 catalog/lib/widgets/app_text_fields.dart diff --git a/catalog/gallery/lib/catalog/catalog_app_checkbox.dart b/catalog/gallery/lib/catalog/catalog_app_checkbox.dart new file mode 100644 index 00000000..54fd83af --- /dev/null +++ b/catalog/gallery/lib/catalog/catalog_app_checkbox.dart @@ -0,0 +1,27 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:catalog/widgets/app_checkbox.dart'; +import 'package:flutter/material.dart'; +import 'package:gallery/catalog/catalog_scaffold_screen.dart'; + +@RoutePage() +class CatalogAppCheckboxScreen extends StatelessWidget { + const CatalogAppCheckboxScreen({super.key}); + + @override + Widget build(BuildContext context) => CatalogScaffold( + title: 'APP CHECKBOX', + child: AppCheckbox( + shrinkWrap: true, + initialValue: true, + onPressed: (bool? value) {}, + items: const [ + (value: false, title: 'Option 1', subtitle: null), + (value: false, title: 'Option 2', subtitle: null), + (value: false, title: 'Option 3', subtitle: null), + (value: false, title: 'Option 4', subtitle: null), + (value: false, title: 'Option 5', subtitle: null), + (value: false, title: 'Option 6', subtitle: null), + ], + ), + ); +} diff --git a/catalog/gallery/lib/catalog/catalog_app_dialog.dart b/catalog/gallery/lib/catalog/catalog_app_dialog.dart new file mode 100644 index 00000000..c0b366fe --- /dev/null +++ b/catalog/gallery/lib/catalog/catalog_app_dialog.dart @@ -0,0 +1,24 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:catalog/widgets/app_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:gallery/catalog/catalog_scaffold_screen.dart'; + +@RoutePage() +class CatalogDialogScreen extends StatelessWidget { + const CatalogDialogScreen({super.key}); + + @override + Widget build(BuildContext context) => const CatalogScaffold( + title: 'DIALOG', + child: AppDialog( + title: 'Modal title', + content: 'This is place holder text. The basic dialog for modals ' + 'should contain only valuable and relevant information. Simplify ' + 'dialogs by removing unnecessary elements or content that does ' + 'not support user tasks. If you find that the number of required ' + 'elements for your design are making ', + cancelButtonText: 'Cancel', + actionButtonText: 'Confirm', + ), + ); +} diff --git a/catalog/gallery/lib/catalog/catalog_app_dropdown_screen.dart b/catalog/gallery/lib/catalog/catalog_app_dropdown_screen.dart new file mode 100644 index 00000000..41694268 --- /dev/null +++ b/catalog/gallery/lib/catalog/catalog_app_dropdown_screen.dart @@ -0,0 +1,33 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:catalog/widgets/app_dropdown.dart'; +import 'package:flutter/material.dart'; +import 'package:gallery/catalog/catalog_scaffold_screen.dart'; + +@RoutePage() +class CatalogDropdownScreen extends StatelessWidget { + const CatalogDropdownScreen({super.key}); + + @override + Widget build(BuildContext context) => CatalogScaffold( + title: 'DROPDOWN', + child: Container( + margin: const EdgeInsets.all(20), + child: Column( + children: [ + AppDropdownMenu( + initialValue: 1, + dropdownMenuEntries: const [ + (value: 1, label: 'Option 1'), + (value: 2, label: 'Option 2'), + (value: 3, label: 'Option 3'), + (value: 4, label: 'Option 4'), + (value: 5, label: 'Option 5'), + (value: 6, label: 'Option 6'), + ], + onSelected: (int? value) {}, + ), + ], + ), + ), + ); +} diff --git a/catalog/gallery/lib/catalog/catalog_app_radio_button.dart b/catalog/gallery/lib/catalog/catalog_app_radio_button.dart new file mode 100644 index 00000000..e8a647f8 --- /dev/null +++ b/catalog/gallery/lib/catalog/catalog_app_radio_button.dart @@ -0,0 +1,26 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:catalog/widgets/app_radio_button.dart'; +import 'package:flutter/material.dart'; +import 'package:gallery/catalog/catalog_scaffold_screen.dart'; + +@RoutePage() +class CatalogAppRadioButtonScreen extends StatelessWidget { + const CatalogAppRadioButtonScreen({super.key}); + + @override + Widget build(BuildContext context) => CatalogScaffold( + title: 'APP RADIO BUTTON', + child: AppRadioButton( + initialValue: 1, + onPressed: (int? value) {}, + items: const [ + (value: 1, label: 'Option 1'), + (value: 2, label: 'Option 2'), + (value: 3, label: 'Option 3'), + (value: 4, label: 'Option 4'), + (value: 5, label: 'Option 5'), + (value: 6, label: 'Option 6'), + ], + ), + ); +} diff --git a/catalog/gallery/lib/catalog/catalog_app_text_fields_screen.dart b/catalog/gallery/lib/catalog/catalog_app_text_fields_screen.dart new file mode 100644 index 00000000..ddfe29fe --- /dev/null +++ b/catalog/gallery/lib/catalog/catalog_app_text_fields_screen.dart @@ -0,0 +1,65 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:catalog/catalog.dart'; +import 'package:catalog/widgets/app_text_fields.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gallery/catalog/catalog_scaffold_screen.dart'; + +@RoutePage() +class CatalogTextFieldsScreen extends StatefulWidget { + const CatalogTextFieldsScreen({super.key}); + + @override + State createState() => + _CatalogTextFieldsScreenState(); +} + +class _CatalogTextFieldsScreenState extends State { + int _characterCount = 0; + final labelTextController = TextEditingController(); + final textAreaTextController = TextEditingController(); + + @override + Widget build(BuildContext context) => CatalogScaffold( + title: 'TEXT FIELDS', + child: Container( + margin: const EdgeInsets.all(20), + child: Column( + children: [ + AppTextField( + controller: labelTextController, + labelText: 'Label', + helperText: 'Helper text', + hintText: 'Text', + suffixIcon: Icon( + Icons.close, + color: context.theme.colors.textColor.shade200, + ), + prefixIcon: Icon( + Icons.close, + color: context.theme.colors.textColor.shade200, + ), + keyboardType: TextInputType.emailAddress, + ), + SizedBox(height: 10.h), + AppTextField( + keyboardType: TextInputType.multiline, + controller: textAreaTextController, + maxLength: 100, + labelText: 'Label', + hintText: 'Text', + currentLength: _characterCount, + onChange: (value) { + setState(() { + _characterCount = value.length; + }); + }, + minLines: 8, + maxLines: 10, + ), + SizedBox(height: 10.h), + ], + ), + ), + ); +} diff --git a/catalog/gallery/lib/catalog/catalog_text_fields_screen.dart b/catalog/gallery/lib/catalog/catalog_text_fields_screen.dart deleted file mode 100644 index 82f69aca..00000000 --- a/catalog/gallery/lib/catalog/catalog_text_fields_screen.dart +++ /dev/null @@ -1,15 +0,0 @@ -//ignore_for_file: unused-files, unused-code -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:gallery/catalog/catalog_scaffold_screen.dart'; - -@RoutePage() -class CatalogTextFieldsScreen extends StatelessWidget { - const CatalogTextFieldsScreen({super.key}); - - @override - Widget build(BuildContext context) => CatalogScaffold( - title: 'TEXT FIELDS', - child: Container(), - ); -} diff --git a/catalog/gallery/lib/main/catalog_main_screen.dart b/catalog/gallery/lib/main/catalog_main_screen.dart index bfaf89be..c29a75ea 100644 --- a/catalog/gallery/lib/main/catalog_main_screen.dart +++ b/catalog/gallery/lib/main/catalog_main_screen.dart @@ -40,6 +40,10 @@ enum _CatalogScreen { textFields, colors, typography, + dropdown, + dialog, + radioButtons, + checkbox, } extension _CatalogScreenExtensions on _CatalogScreen { @@ -53,6 +57,14 @@ extension _CatalogScreenExtensions on _CatalogScreen { return 'Colors'; case _CatalogScreen.typography: return 'Typography'; + case _CatalogScreen.dropdown: + return 'Dropdown'; + case _CatalogScreen.dialog: + return 'Dialog'; + case _CatalogScreen.radioButtons: + return 'Radio Buttons'; + case _CatalogScreen.checkbox: + return 'Checkbox'; } } @@ -66,6 +78,14 @@ extension _CatalogScreenExtensions on _CatalogScreen { return const CatalogAppColorsRoute(); case _CatalogScreen.typography: return const CatalogAppTypographyRoute(); + case _CatalogScreen.dropdown: + return const CatalogDropdownRoute(); + case _CatalogScreen.dialog: + return const CatalogDialogRoute(); + case _CatalogScreen.radioButtons: + return const CatalogAppRadioButtonRoute(); + case _CatalogScreen.checkbox: + return const CatalogAppCheckboxRoute(); } } } diff --git a/catalog/gallery/lib/router/catalog_router.dart b/catalog/gallery/lib/router/catalog_router.dart index 51d1b7ba..146a988c 100644 --- a/catalog/gallery/lib/router/catalog_router.dart +++ b/catalog/gallery/lib/router/catalog_router.dart @@ -1,8 +1,12 @@ import 'package:auto_route/auto_route.dart'; import 'package:gallery/catalog/catalog_app_button_screen.dart'; import 'package:gallery/catalog/catalog_app_colors_screen.dart'; +import 'package:gallery/catalog/catalog_app_radio_button.dart'; import 'package:gallery/catalog/catalog_app_typography_screen.dart'; -import 'package:gallery/catalog/catalog_text_fields_screen.dart'; +import 'package:gallery/catalog/catalog_app_checkbox.dart'; +import 'package:gallery/catalog/catalog_app_dropdown_screen.dart'; +import 'package:gallery/catalog/catalog_app_text_fields_screen.dart'; +import 'package:gallery/catalog/catalog_app_dialog.dart'; import 'package:gallery/main/catalog_main_screen.dart'; part 'catalog_router.gr.dart'; @@ -21,5 +25,9 @@ class CatalogRouter extends _$CatalogRouter { AutoRoute(page: CatalogTextFieldsRoute.page), AutoRoute(page: CatalogAppColorsRoute.page), AutoRoute(page: CatalogAppTypographyRoute.page), + AutoRoute(page: CatalogDropdownRoute.page), + AutoRoute(page: CatalogDialogRoute.page), + AutoRoute(page: CatalogAppRadioButtonRoute.page), + AutoRoute(page: CatalogAppCheckboxRoute.page), ]; } diff --git a/catalog/gallery/lib/router/catalog_router.gr.dart b/catalog/gallery/lib/router/catalog_router.gr.dart index d2fb78a3..aecaf11e 100644 --- a/catalog/gallery/lib/router/catalog_router.gr.dart +++ b/catalog/gallery/lib/router/catalog_router.gr.dart @@ -21,18 +21,42 @@ abstract class _$CatalogRouter extends RootStackRouter { child: const CatalogAppButtonScreen(), ); }, + CatalogAppCheckboxRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const CatalogAppCheckboxScreen(), + ); + }, CatalogAppColorsRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, child: const CatalogAppColorsScreen(), ); }, + CatalogAppRadioButtonRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const CatalogAppRadioButtonScreen(), + ); + }, CatalogAppTypographyRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, child: const CatalogAppTypographyScreen(), ); }, + CatalogDialogRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const CatalogDialogScreen(), + ); + }, + CatalogDropdownRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const CatalogDropdownScreen(), + ); + }, CatalogMainRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, @@ -62,6 +86,20 @@ class CatalogAppButtonRoute extends PageRouteInfo { static const PageInfo page = PageInfo(name); } +/// generated route for +/// [CatalogAppCheckboxScreen] +class CatalogAppCheckboxRoute extends PageRouteInfo { + const CatalogAppCheckboxRoute({List? children}) + : super( + CatalogAppCheckboxRoute.name, + initialChildren: children, + ); + + static const String name = 'CatalogAppCheckboxRoute'; + + static const PageInfo page = PageInfo(name); +} + /// generated route for /// [CatalogAppColorsScreen] class CatalogAppColorsRoute extends PageRouteInfo { @@ -76,6 +114,20 @@ class CatalogAppColorsRoute extends PageRouteInfo { static const PageInfo page = PageInfo(name); } +/// generated route for +/// [CatalogAppRadioButtonScreen] +class CatalogAppRadioButtonRoute extends PageRouteInfo { + const CatalogAppRadioButtonRoute({List? children}) + : super( + CatalogAppRadioButtonRoute.name, + initialChildren: children, + ); + + static const String name = 'CatalogAppRadioButtonRoute'; + + static const PageInfo page = PageInfo(name); +} + /// generated route for /// [CatalogAppTypographyScreen] class CatalogAppTypographyRoute extends PageRouteInfo { @@ -90,6 +142,34 @@ class CatalogAppTypographyRoute extends PageRouteInfo { static const PageInfo page = PageInfo(name); } +/// generated route for +/// [CatalogDialogScreen] +class CatalogDialogRoute extends PageRouteInfo { + const CatalogDialogRoute({List? children}) + : super( + CatalogDialogRoute.name, + initialChildren: children, + ); + + static const String name = 'CatalogDialogRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [CatalogDropdownScreen] +class CatalogDropdownRoute extends PageRouteInfo { + const CatalogDropdownRoute({List? children}) + : super( + CatalogDropdownRoute.name, + initialChildren: children, + ); + + static const String name = 'CatalogDropdownRoute'; + + static const PageInfo page = PageInfo(name); +} + /// generated route for /// [CatalogMainScreen] class CatalogMainRoute extends PageRouteInfo { diff --git a/catalog/gallery/pubspec.yaml b/catalog/gallery/pubspec.yaml index eb567213..ba533454 100644 --- a/catalog/gallery/pubspec.yaml +++ b/catalog/gallery/pubspec.yaml @@ -36,6 +36,9 @@ dev_dependencies: build_runner: 2.4.6 lints: 3.0.0 +flutter: + uses-material-design: true + flutter_launcher_icons: android: true ios: true diff --git a/catalog/lib/theme/app_theme.dart b/catalog/lib/theme/app_theme.dart index d055e19f..dc80291a 100644 --- a/catalog/lib/theme/app_theme.dart +++ b/catalog/lib/theme/app_theme.dart @@ -3,6 +3,7 @@ import 'package:catalog/theme/app_text_styles.dart'; import 'package:flutter/material.dart'; import 'package:catalog/theme/app_colors.dart'; import 'package:catalog/theme/app_dimensions.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; //TODO: add theme extensions late AppColors _colors; @@ -19,6 +20,94 @@ class AppTheme { _buttonStyles = AppButtonsStyle.getButtonTheme(); return ThemeData( + dialogTheme: DialogTheme( + backgroundColor: _colors.surface.shade100, + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.r), + side: BorderSide(color: _colors.surface.shade500), + ), + titleTextStyle: _styles.customOverline + .copyWith(color: _colors.textColor.shade300) + .semibold(), + contentTextStyle: + _styles.bodyMedium?.copyWith(color: _colors.textColor.shade400), + ), + inputDecorationTheme: InputDecorationTheme( + labelStyle: + _styles.bodyMedium?.copyWith(color: _colors.textColor.shade400), + filled: true, + helperStyle: + _styles.bodySmall?.copyWith(color: _colors.textColor.shade300), + hintStyle: + _styles.bodyMedium?.copyWith(color: _colors.textColor.shade300), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(4.r), + borderSide: BorderSide( + width: 2, + color: _colors.textColor.shade100, + ), + ), + fillColor: _colors.surface.shade100, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(4.r), + borderSide: BorderSide( + width: 2, + color: _colors.textColor.shade200, + ), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(4.r), + borderSide: BorderSide( + width: 2, + color: _colors.danger.shade300, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(4.r), + borderSide: BorderSide( + width: 2, + color: _colors.primary.shade800, + ), + ), + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(4.r), + borderSide: BorderSide( + width: 2, + color: _colors.textColor.shade200, + ), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(4.r), + borderSide: BorderSide( + width: 2, + color: _colors.error, + ), + ), + errorStyle: _styles.labelSmall?.copyWith(color: _colors.danger), + errorMaxLines: 2, + hoverColor: _colors.primary.shade400, + focusColor: _colors.primary.shade800, + ), + dropdownMenuTheme: DropdownMenuThemeData( + textStyle: _styles.bodyMedium?.copyWith( + color: _colors.primary.shade500, + ), + menuStyle: MenuStyle( + maximumSize: MaterialStateProperty.resolveWith( + (Set states) => Size(1.sw, 35.h * 6), + ), + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: _colors.onSurface.shade200, + ), + borderRadius: BorderRadius.all(Radius.circular(4.r)), + ), + ), + ), primaryColor: _colors.primary, colorScheme: _colors, filledButtonTheme: FilledButtonThemeData( @@ -38,6 +127,23 @@ class AppTheme { backgroundColor: _colors.primary.shade400, titleTextStyle: TextStyle(color: _colors.textColor.shade100), ), + checkboxTheme: CheckboxThemeData( + checkColor: MaterialStateProperty.resolveWith( + (Set states) => _colors.primary.shade600, + ), + fillColor: MaterialStateProperty.resolveWith( + (Set states) => _colors.textColor.shade100, + ), + side: BorderSide( + width: 2, + color: _colors.textColor.shade400, + ), + ), + radioTheme: RadioThemeData( + fillColor: MaterialStateProperty.resolveWith( + (Set states) => _colors.textColor.shade400, + ), + ), ); } } diff --git a/catalog/lib/widgets/app_checkbox.dart b/catalog/lib/widgets/app_checkbox.dart new file mode 100644 index 00000000..93a01a5c --- /dev/null +++ b/catalog/lib/widgets/app_checkbox.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +typedef CheckboxList = ({bool value, String title, String? subtitle}); + +class AppCheckbox extends StatefulWidget { + final bool initialValue; + final List items; + final void Function(bool?) onPressed; + final bool? shrinkWrap; + final Axis? scrollDirection; + + const AppCheckbox({ + required this.onPressed, + required this.items, + required this.initialValue, + this.shrinkWrap = false, + this.scrollDirection = Axis.vertical, + super.key, + }); + + @override + State> createState() => _AppCheckboxState(); +} + +class _AppCheckboxState extends State> { + late List isCheckedList; + late bool shrinkWrap; + late Axis scrollDirection; + + @override + void initState() { + isCheckedList = widget.items + .map( + (item) => item.value, + ) + .toList(); + shrinkWrap = widget.shrinkWrap!; + scrollDirection = widget.scrollDirection!; + super.initState(); + } + + @override + Widget build(BuildContext context) => ListView.builder( + scrollDirection: widget.scrollDirection!, + shrinkWrap: shrinkWrap, + itemCount: widget.items.length, + itemBuilder: (BuildContext context, int index) { + final element = widget.items[index]; + return SizedBox( + width: 1.sw, + child: CheckboxListTile( + value: isCheckedList[index], + onChanged: (bool? value) { + setState(() { + isCheckedList[index] = value!; + }); + widget.onPressed(value); + }, + title: Text(element.title), + subtitle: + element.subtitle != null ? Text(element.subtitle!) : null, + ), + ); + }, + ); +} diff --git a/catalog/lib/widgets/app_dialog.dart b/catalog/lib/widgets/app_dialog.dart new file mode 100644 index 00000000..d4943644 --- /dev/null +++ b/catalog/lib/widgets/app_dialog.dart @@ -0,0 +1,92 @@ +import 'package:catalog/catalog.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class AppDialog extends StatelessWidget { + final String title; + final String? content; + final String? actionButtonText; + final VoidCallback? onActionPressed; + final String? cancelButtonText; + + const AppDialog({ + required this.title, + this.content, + this.cancelButtonText, + this.actionButtonText, + this.onActionPressed, + super.key, + }); + + @override + Widget build(BuildContext context) => AlertDialog( + title: Row( + children: [ + Text(title), + const Spacer(), + IconButton( + onPressed: Navigator.of(context).pop, + icon: Icon( + Icons.close, + color: context.theme.colors.textColor.shade300, + ), + ), + ], + ), + content: content != null ? Text(content!) : null, + actionsAlignment: _getAligment(), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + if (cancelButtonText != null) + Expanded( + child: TextButton( + onPressed: Navigator.of(context).pop, + child: Text( + cancelButtonText!, + style: context.theme.textStyles.buttonMedium.copyWith( + color: context.theme.colors.textColor.shade300, + ), + ), + ), + ), + if (actionButtonText != null) + Expanded( + child: FilledButton( + child: Text( + actionButtonText!, + style: context.theme.textStyles.buttonMedium.copyWith( + color: context.theme.colors.textColor.shade100, + ), + ), + onPressed: () { + if (onActionPressed != null) { + onActionPressed!(); + } + Navigator.of(context).pop(); + }, + ), + ), + ], + ), + ], + ); + + MainAxisAlignment _getAligment() { + if (actionButtonText == null) { + return MainAxisAlignment.start; + } else if (cancelButtonText == null) { + return MainAxisAlignment.end; + } else { + return MainAxisAlignment.spaceAround; + } + } + + double? _getActionButtonsWidth() { + if (actionButtonText == null || cancelButtonText == null) { + return 130.w; + } + return null; + } +} diff --git a/catalog/lib/widgets/app_dropdown.dart b/catalog/lib/widgets/app_dropdown.dart new file mode 100644 index 00000000..a55d98d5 --- /dev/null +++ b/catalog/lib/widgets/app_dropdown.dart @@ -0,0 +1,53 @@ +import 'package:catalog/catalog.dart'; +import 'package:flutter/material.dart'; + +typedef AppDropdownItems = ({T value, String label}); + +class AppDropdownMenu extends StatefulWidget { + final List> dropdownMenuEntries; + final void Function(T?) onSelected; + final T? initialValue; + + const AppDropdownMenu({ + required this.dropdownMenuEntries, + required this.onSelected, + this.initialValue, + super.key, + }); + + @override + State> createState() => _AppDropdownMenuState(); +} + +class _AppDropdownMenuState extends State> { + final TextEditingController _controller = TextEditingController(); + + @override + Widget build(BuildContext context) => DropdownMenu( + initialSelection: widget.initialValue, + trailingIcon: Icon( + Icons.arrow_drop_down_outlined, + color: context.theme.colors.textColor.shade200, + ), + controller: _controller, + onSelected: (T? value) => widget.onSelected(value), + dropdownMenuEntries: widget.dropdownMenuEntries + .map>( + (AppDropdownItems item) => DropdownMenuEntry( + value: item.value, + label: item.label, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => + context.theme.colors.surface.shade100, + ), + foregroundColor: MaterialStateProperty.resolveWith( + (Set states) => + context.theme.colors.textColor.shade400, + ), + ), + ), + ) + .toList(), + ); +} diff --git a/catalog/lib/widgets/app_radio_button.dart b/catalog/lib/widgets/app_radio_button.dart new file mode 100644 index 00000000..221ba6aa --- /dev/null +++ b/catalog/lib/widgets/app_radio_button.dart @@ -0,0 +1,64 @@ +import 'package:catalog/catalog.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +typedef RadioButtonList = ({T value, String label}); + +class AppRadioButton extends StatefulWidget { + final T initialValue; + final List items; + final void Function(T?) onPressed; + final bool? shrinkWrap; + final Axis? scrollDirection; + + const AppRadioButton({ + required this.onPressed, + required this.items, + required this.initialValue, + this.shrinkWrap = false, + this.scrollDirection = Axis.vertical, + super.key, + }); + + @override + State> createState() => _AppRadioButtonState(); +} + +class _AppRadioButtonState extends State> { + late T? selectedValue; + late bool shrinkWrap; + late Axis scrollDirection; + + @override + void initState() { + selectedValue = widget.initialValue; + shrinkWrap = widget.shrinkWrap!; + scrollDirection = widget.scrollDirection!; + super.initState(); + } + + @override + Widget build(BuildContext context) => ListView.builder( + scrollDirection: widget.scrollDirection!, + shrinkWrap: shrinkWrap, + itemCount: widget.items.length, + itemBuilder: (BuildContext context, int index) { + final element = widget.items[index]; + return SizedBox( + width: 1.sw, + child: RadioListTile( + activeColor: context.theme.colors.primary.shade600, + title: Text(element.label), + value: element.value, + groupValue: selectedValue, + onChanged: (T? value) { + setState(() { + selectedValue = value; + }); + widget.onPressed(value); + }, + ), + ); + }, + ); +} diff --git a/catalog/lib/widgets/app_text_fields.dart b/catalog/lib/widgets/app_text_fields.dart new file mode 100644 index 00000000..6e0a50da --- /dev/null +++ b/catalog/lib/widgets/app_text_fields.dart @@ -0,0 +1,93 @@ +//ignore_for_file: prefer-moving-to-variable, unused-files, unused-code +import 'package:catalog/catalog.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class AppTextField extends StatefulWidget { + final TextEditingController controller; + final void Function(String value)? onChange; + final String? errorText; + final String? labelText; + final String? hintText; + final String? helperText; + final Widget? prefixIcon; + final Widget? suffixIcon; + final void Function()? onTrailingIconPress; + final TextInputType? keyboardType; + final bool enabled; + final bool obscureText; + final int? maxLength; + final int minLines; + final int maxLines; + final bool hasScreenBottomNavigation; + final int? currentLength; + + const AppTextField({ + required this.controller, + super.key, + this.onChange, + this.errorText, + this.labelText, + this.prefixIcon, + this.suffixIcon, + this.onTrailingIconPress, + this.keyboardType, + this.enabled = true, + this.obscureText = false, + this.maxLength, + this.minLines = 1, + this.helperText, + this.hintText, + this.maxLines = 1, + this.currentLength = 0, + this.hasScreenBottomNavigation = true, + }); + + @override + State createState() => _AppTextFieldState(); +} + +class _AppTextFieldState extends State { + @override + Widget build(BuildContext context) { + final colors = context.theme.colors; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.labelText != null) + Padding( + padding: EdgeInsets.symmetric(vertical: 5.h), + child: Text( + widget.labelText!, + textAlign: TextAlign.left, + style: context.theme.textStyles.labelMedium! + .bold() + .copyWith(color: colors.textColor.shade400), + ), + ), + SizedBox(height: 5.h), + TextField( + maxLength: widget.maxLength, + minLines: widget.minLines, + maxLines: widget.maxLines, + cursorHeight: 24, + textAlignVertical: TextAlignVertical.center, + controller: widget.controller, + onChanged: widget.onChange, + style: context.theme.textStyles.bodyMedium + ?.copyWith(color: colors.textColor.shade400), + decoration: InputDecoration( + helperText: widget.helperText, + hintText: widget.hintText, + prefixIcon: widget.prefixIcon, + suffixIcon: widget.suffixIcon, + errorText: widget.errorText, + ), + keyboardType: widget.keyboardType, + enabled: widget.enabled, + obscureText: widget.obscureText, + ), + ], + ); + } +} From 308b6a8a04eaaea72386252fd65a544d7519eddd Mon Sep 17 00:00:00 2001 From: Sol Mendiola <57235692+SolMendiola@users.noreply.github.com> Date: Tue, 19 Dec 2023 18:39:55 -0300 Subject: [PATCH 2/2] feat: readme catalog and gallery (#201) --- README.md | 4 --- catalog/README.md | 71 ++++++++++++++++++++++++--------------- catalog/gallery/README.md | 20 +++++++++++ 3 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 catalog/gallery/README.md diff --git a/README.md b/README.md index 5357bf40..d8ad2083 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,6 @@ The router has two subgraphs, the `UnauthenticatedRouter` used for unauthenticat The [app router][app_router] is provided by [auto_route][auto_route], and contains the previous sections with some nested screens. -The [theme folder][theme] contains the theme definitions, for example colors, styles and dimensions. -Another important plugin related to the UI is [flutter_screenutil][flutter_screenutil], used for adapting screen and font size. - ### Core section The models are defined in the [models folder][models]. If you need to use different models for database or networking, you can create them in `db` and `service` folders respectively. @@ -114,7 +111,6 @@ In order to setup pre-push hook you need to go to the root of the project and ru [app_router]: https://github.com/xmartlabs/flutter-template/blob/main/lib/ui/app_router.dart [bloc]: https://bloclibrary.dev [auto_route]: https://pub.dev/packages/auto_route -[theme]: https://github.com/xmartlabs/flutter-template/tree/main/lib/ui/theme [flutter_screenutil]: https://pub.dev/packages/flutter_screenutil [models]: https://github.com/xmartlabs/flutter-template/tree/main/lib/core/model [repository_folder]: https://github.com/xmartlabs/flutter-template/tree/main/lib/core/repository diff --git a/catalog/README.md b/catalog/README.md index 02fe8eca..648185fc 100644 --- a/catalog/README.md +++ b/catalog/README.md @@ -1,39 +1,56 @@ - - -TODO: Put a short description of the package here that helps potential users -know whether this package might be useful for them. +Catalog is a Flutter package that provides a base implementation for UX/UI components based on the [design template of Xmartlabs][figma]. +It aims to simplify and expedite the development process for Flutter projects, ensuring a consistent and visually appealing user interface. ## Features -TODO: List what your package can do. Maybe include images, gifs, or videos. +- Ready-to-use main UI components inspired by Xmartlabs design principles. +- Customizable components to fit your project's specific requirements. ## Getting started -TODO: List prerequisites and provide or point to information on how to -start using the package. +### Installation -## Usage +Integrate the package in your project, adding the following line to your `pubspec.yaml` file: -TODO: Include short and useful examples for package users. Add longer examples -to `/example` folder. - -```dart -const like = 'sample'; +```yaml +dependencies flutter: + catalog: + path: ../ ``` -## Additional information +## Arch Overview + +The catalog project is organized into a singular folder named 'Theme.' +Within the [theme folder][theme], various files contribute to the overall theme definition: + +- App Theme: +This file outlines the app's styles, encompassing elements such as the app bar, dialogs, radio buttons, and other components. + +- App Buttons: +Styles for the default buttons of the app are defined within this file. + +- App Color Scheme: +Default color schemes for the app are established in this file. + +- Custom Colors: +As a Theme Extension, this file incorporates custom colors specific to the app. + +- App Dimensions: +Default dimensions for various elements within the app are specified in this file. + +- App Text Styles: +Default text styles across the app are defined in this file. + +- Custom Text Styles: +As another Theme Extension, this file articulates the custom text styles unique to the app. + +This organized structure within the 'Theme' folder ensures a systematic approach to managing and defining the visual aspects of the app. + +### Contributing +We welcome contributions! If you find a bug or have a feature request, please open an [issue]. -TODO: Tell users more about the package: where to find more information, how to -contribute to the package, how to file issues, what response they can expect -from the package authors, and more. +[issue]: https://github.com/xmartlabs/flutter-template/issues +[theme]: https://github.com/xmartlabs/flutter-template/tree/main/lib/ui/theme +[figma]: https://www.figma.com/file/OWDUY4WaDjyfiOE6Z8mfsC/XL---Tourmaline?node-id=0%3A1&mode=dev \ No newline at end of file diff --git a/catalog/gallery/README.md b/catalog/gallery/README.md new file mode 100644 index 00000000..9c7f8871 --- /dev/null +++ b/catalog/gallery/README.md @@ -0,0 +1,20 @@ +# Gallery App + +Gallery is a Flutter app designed to showcase and share the design system of our project. +This app serves as a visual reference for developers and provides a streamlined process for the design team to approve the correct implementation of components. + +## Features + +- **Design System Showcase:** View and explore the design system components used in the app. +- **Interactive Examples:** Interact with live examples of components to see them in action. +- **Easy Sharing:** Share specific components or screens with the design team for review. + + +## Design System Documentation +For comprehensive documentation on our design system, including guidelines, principles, and usage instructions, please refer to [Design System]. + +### Contributing +We welcome contributions! If you find a bug or have a feature request, please open an [issue]. + +[issue]: https://github.com/xmartlabs/flutter-template/issues +[Design System]: https://www.figma.com/file/OWDUY4WaDjyfiOE6Z8mfsC/XL---Tourmaline?node-id=0%3A1&mode=dev