diff --git a/example/lib/home.dart b/example/lib/home.dart index 6e555557..0ba17a0f 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -187,10 +187,14 @@ class _HomePageState extends State { title: "GS Navigation Rail", routePath: "/navigation-rail-preview", ), - const NavButton( + const NavButton( title: "GS Slider", routePath: "/slider-example", ), + const NavButton( + title: "GS Modal", + routePath: "/modal-example", + ), // // ===== Internal Testing Widgets ===== // const NavButton( diff --git a/example/lib/routes/router.dart b/example/lib/routes/router.dart index b8608de1..aaf79207 100644 --- a/example/lib/routes/router.dart +++ b/example/lib/routes/router.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:gluestack_ui_example/home.dart'; import 'package:gluestack_ui_example/widgets/components/widgets/accordian_example.dart'; import 'package:gluestack_ui_example/widgets/components/widgets/bottom_sheet_example.dart'; +import 'package:gluestack_ui_example/widgets/components/widgets/modal_example.dart'; import 'package:gluestack_ui_example/widgets/components/widgets/navigation_rail_example.dart'; import 'package:gluestack_ui_example/widgets/components/widgets/slider_example.dart'; import 'package:gluestack_ui_example/widgets/storybook/storybook.dart'; @@ -198,6 +199,10 @@ final GoRouter router = GoRouter( path: "slider-example", builder: (context, state) => const SliderExample(), ), + GoRoute( + path: "modal-example", + builder: (context, state) => const ModalExample(), + ), // Generate individual Storybook screens for every widget. This is referenced in docs website iframe. ...kStories.map( diff --git a/example/lib/widgets/components/widgets/modal_example.dart b/example/lib/widgets/components/widgets/modal_example.dart new file mode 100644 index 00000000..e57247cc --- /dev/null +++ b/example/lib/widgets/components/widgets/modal_example.dart @@ -0,0 +1,204 @@ +import 'package:flutter/material.dart'; +import 'package:gluestack_ui/gluestack_ui.dart'; +import 'package:gluestack_ui_example/widgets/components/layout/base_layout.dart'; +import 'package:gluestack_ui_example/widgets/components/layout/custom_gs_layout.dart'; +import 'package:gluestack_ui_example/widgets/components/layout/drop_down.dart'; +import 'package:gluestack_ui_example/widgets/components/layout/toggle.dart'; + +class ModalExample extends StatefulWidget { + const ModalExample({super.key}); + + @override + State createState() => _ModalExampleState(); +} + +class _ModalExampleState extends State { + bool barrierDismissible = false; + bool isOpen = false; + bool showBackdrop = false; + final List dropdownSizeOptions = [ + GSModalSizes.$xs, + GSModalSizes.$sm, + GSModalSizes.$md, + GSModalSizes.$lg, + GSModalSizes.$full, + ]; + GSModalSizes selectedSizeOption = GSModalSizes.$md; + + void updateSizeSelectedOption(dynamic newOption) { + setState(() { + selectedSizeOption = newOption; + }); + } + + void updateBarrierDismissible(bool value) { + setState(() { + barrierDismissible = value; + }); + } + + void updateShowBackdrop(bool value) { + setState(() { + showBackdrop = value; + }); + } + + @override + Widget build(BuildContext context) { + var code = ''' +GSModal( + isOpen: $isOpen, + barrierDismissible: $barrierDismissible, + showBackdrop: $showBackdrop, + size: $selectedSizeOption, + content: GSModalContent( + header: GSModalHeader( + closeButton: GSModalCloseButton( + icon: GSIcon(icon: Icons.close), + ), + child: GSText( + text: "Invite your team", + style: GSStyle( + textStyle: TextStyle( + color: GSTheme.of(context).background900, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + body: GSModalBody( + child: GSText( + text: "Elevate user interactions with our versatile modals. Seamlessly integrate notifications, forms, and media displays. Make an impact effortlessly.", + ), + ), + footer: GSModalFooter( + child: GSButtonGroup( + buttons: [ + GSButton( + action: GSButtonActions.primary, + variant: GSButtonVariants.outline, + child: GSText( + text: "Cancel", + ), + onPressed: () { + setState(() { + isOpen = false; + }); + }, + ), + GSButton( + action: GSButtonActions.primary, + variant: GSButtonVariants.solid, + child: GSText( + text: "Explore", + ), + onPressed: () { + setState(() { + isOpen = false; + }); + }, + ), + ], + ), + ), + ), + child: GSBadge( + text: GSBadgeText("Show Modal"), + ), +) +'''; + + return CustomGSLayout( + title: "Modal", + style: GSStyle( + dark: GSStyle(bg: $GSColors.black), + ), + body: BaseLayout( + code: code, + component: GSModal( + onClose: () { + setState(() {}); + }, + barrierDismissible: barrierDismissible, + showBackdrop: showBackdrop, + isOpen: isOpen, + size: selectedSizeOption, + content: GSModalContent( + header: GSModalHeader( + closeButton: const GSModalCloseButton( + icon: GSIcon(icon: Icons.close), + ), + child: GSText( + text: "Invite your team", + style: GSStyle( + textStyle: TextStyle( + color: GSTheme.of(context).background900, + fontWeight: FontWeight.bold)), + ), + ), + body: const GSModalBody( + child: GSText( + text: + "Elevate user interactions with our versatile modals. Seamlessly integrate notifications, forms, and media displays. Make an impact effortlessly.", + ), + ), + footer: GSModalFooter( + child: GSButtonGroup( + buttons: [ + GSButton( + action: GSButtonActions.primary, + variant: GSButtonVariants.outline, + child: const GSText( + text: "Cancel", + ), + onPressed: () { + setState(() { + isOpen = false; + }); + }), + GSButton( + action: GSButtonActions.primary, + variant: GSButtonVariants.solid, + child: const GSText( + text: "Explore", + ), + onPressed: () { + setState(() { + isOpen = false; + }); + }), + ], + ), + ), + ), + child: const GSBadge( + text: GSBadgeText("Show Modal"), + )), + controls: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomDropDown( + title: "size", + dropdownOptions: dropdownSizeOptions, + selectedOption: selectedSizeOption, + onChanged: updateSizeSelectedOption, + ), + const SizedBox(height: 20), + CustomToggle( + title: "barrierDismissible", + value: barrierDismissible, + onToggle: updateBarrierDismissible, + ), + const SizedBox(height: 20), + CustomToggle( + title: "showBackdrop", + value: showBackdrop, + onToggle: updateShowBackdrop, + ), + ], + ), + ), + ); + } +} diff --git a/example/lib/widgets/storybook/widgets/modal_story.dart b/example/lib/widgets/storybook/widgets/modal_story.dart new file mode 100644 index 00000000..0b3e9be7 --- /dev/null +++ b/example/lib/widgets/storybook/widgets/modal_story.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:gluestack_ui/gluestack_ui.dart'; +import 'base_story_widget.dart'; +import 'package:storybook_flutter/storybook_flutter.dart'; + +final List> sizeOptions = generateEnumOptions(GSModalSizes.values); + +final class ModalStory extends StoryWidget { + @override + Story createStoryWidget() { + return Story( + name: storyName, + builder: (context) => GSModal( + size: GSModalSizes.values[context.knobs + .options(label: 'Size', initial: 0, options: sizeOptions)], + barrierDismissible: context.knobs + .boolean(label: "barrierDismissible", initial: true), + showBackdrop: + context.knobs.boolean(label: "showBackdrop", initial: true), + content: GSModalContent( + header: GSModalHeader( + closeButton: const GSModalCloseButton( + icon: GSIcon(icon: Icons.close), + ), + style: GSStyle( + textStyle: TextStyle( + color: GSTheme.of(context).background900, + fontWeight: FontWeight.bold)), + child: const GSText( + text: "Invite your team", + ), + ), + body: const GSModalBody( + child: GSText( + text: + "Elevate user interactions with our versatile modals. Seamlessly integrate notifications, forms, and media displays. Make an impact effortlessly.", + ), + ), + footer: GSModalFooter( + child: GSButtonGroup( + buttons: [ + GSButton( + action: GSButtonActions.primary, + variant: GSButtonVariants.outline, + child: const GSText( + text: "Cancel", + ), + onPressed: () {}), + GSButton( + action: GSButtonActions.primary, + variant: GSButtonVariants.solid, + child: const GSText( + text: "Explore", + ), + onPressed: () {}), + ], + ), + ), + ), + child: const GSBadge( + text: GSBadgeText("Show Modal"), + ))); + } + + @override + String get routePath => "modal-preview"; + + @override + String get storyName => "Modal"; +} diff --git a/example/lib/widgets/storybook/widgets/public.dart b/example/lib/widgets/storybook/widgets/public.dart index db3f37b5..ba172980 100644 --- a/example/lib/widgets/storybook/widgets/public.dart +++ b/example/lib/widgets/storybook/widgets/public.dart @@ -1,4 +1,5 @@ import 'package:gluestack_ui_example/widgets/storybook/widgets/header_story.dart'; +import 'package:gluestack_ui_example/widgets/storybook/widgets/modal_story.dart'; import 'package:gluestack_ui_example/widgets/storybook/widgets/slider_story.dart'; import 'package:gluestack_ui_example/widgets/storybook/widgets/tab_story.dart'; import 'alert_dialog_story.dart'; @@ -68,6 +69,7 @@ final List kStories = [ ToastStory(), VStackStory(), HeaderStory(), + ModalStory(), ModalBottomSheetStory(), NavigationRailStory(), StepperStory(), diff --git a/lib/gluestack_ui.dart b/lib/gluestack_ui.dart index edb7233b..18fdb370 100644 --- a/lib/gluestack_ui.dart +++ b/lib/gluestack_ui.dart @@ -47,3 +47,4 @@ export 'src/widgets/gs_tabs/public.dart'; export 'src/widgets/gs_layout/public.dart'; export 'src/widgets/gs_header/public.dart'; export 'src/widgets/gs_slider/public.dart'; +export 'src/widgets/gs_modal/public.dart'; diff --git a/lib/src/provider/gluestack_provider.dart b/lib/src/provider/gluestack_provider.dart index 772523d6..4ba3c583 100644 --- a/lib/src/provider/gluestack_provider.dart +++ b/lib/src/provider/gluestack_provider.dart @@ -146,6 +146,15 @@ class GluestackCustomConfig { Map? sliderThumb; Map? sliderFilledTrack; + //modal + Map? modal; + Map? modalHeader; + Map? modalFooter; + Map? modalContent; + Map? modalCloseButton; + Map? modalBody; + Map? modalBackdrop; + //GS Layout Map? layout; @@ -156,6 +165,16 @@ class GluestackCustomConfig { this.sliderTrack, this.sliderThumb, this.sliderFilledTrack, + + // modal + + this.modal, + this.modalBackdrop, + this.modalBody, + this.modalCloseButton, + this.modalContent, + this.modalFooter, + this.modalHeader, //tabs this.tabs, this.tabsTab, @@ -316,6 +335,16 @@ class GluestackCustomConfig { sliderThumb = mergeConfigs(sliderThumbData, sliderThumb); sliderFilledTrack = mergeConfigs(sliderFilledTrackData, sliderFilledTrack); + // modal + + modal = mergeConfigs(modalData, modal); + modalBackdrop = mergeConfigs(modalBackdropData, modalBackdrop); + modalBody = mergeConfigs(modalBodyData, modalBody); + modalFooter = mergeConfigs(modalFooterData, modalFooter); + modalHeader = mergeConfigs(modalHeaderData, modalHeader); + modalContent = mergeConfigs(modalContentData, modalContent); + modalCloseButton = mergeConfigs(modalCloseButtonData, modalCloseButton); + //tabs tabs = mergeConfigs(tabsData, tabs); tabsTab = mergeConfigs(tabsTabData, tabsTab); diff --git a/lib/src/provider/provider.dart b/lib/src/provider/provider.dart index 63703b99..dffde10e 100644 --- a/lib/src/provider/provider.dart +++ b/lib/src/provider/provider.dart @@ -84,3 +84,10 @@ export 'package:gluestack_ui/src/theme/config/slider/slider_track.dart'; export 'package:gluestack_ui/src/theme/config/slider/slider_thumb.dart'; export 'package:gluestack_ui/src/theme/config/slider/slider_thumb_interaction.dart'; export 'package:gluestack_ui/src/theme/config/slider/slider_filled_track.dart'; +export 'package:gluestack_ui/src/theme/config/modal/modal.dart'; +export 'package:gluestack_ui/src/theme/config/modal/modal_backdrop.dart'; +export 'package:gluestack_ui/src/theme/config/modal/modal_body.dart'; +export 'package:gluestack_ui/src/theme/config/modal/modal_close_button.dart'; +export 'package:gluestack_ui/src/theme/config/modal/modal_content.dart'; +export 'package:gluestack_ui/src/theme/config/modal/modal_footer.dart'; +export 'package:gluestack_ui/src/theme/config/modal/modal_header.dart'; diff --git a/lib/src/style/gs_config_style_internal.dart b/lib/src/style/gs_config_style_internal.dart index 513fbd51..242ac31c 100644 --- a/lib/src/style/gs_config_style_internal.dart +++ b/lib/src/style/gs_config_style_internal.dart @@ -343,6 +343,7 @@ class GSConfigStyle extends BaseStyle { // String? highlightColor; // String? splashColor; GSConfigStyle? badge; + GSConfigStyle? modal; GSTextTransform? textTransform; GSSizes? iconSize; @@ -425,6 +426,7 @@ class GSConfigStyle extends BaseStyle { this.alignment, this.maxWidth, this.badge, + this.modal, // this.highlightColor, // this.splashColor, this.textTransform, @@ -565,6 +567,7 @@ class GSConfigStyle extends BaseStyle { width: overrideStyle?.width ?? width, height: overrideStyle?.height ?? height, badge: overrideStyle?.badge ?? badge, + modal: overrideStyle?.modal ?? modal, dark: dark != null ? dark?.merge(overrideStyle?.dark) : overrideStyle?.dark, @@ -684,6 +687,9 @@ class GSConfigStyle extends BaseStyle { badge: gsStyle.badge != null ? GSConfigStyle.fromGSStyle(gsStyle.badge!) : null, + modal: gsStyle.modal != null + ? GSConfigStyle.fromGSStyle(gsStyle.modal!) + : null, item: gsStyle.item != null ? GSConfigStyle.fromGSStyle(gsStyle.item!) : null, @@ -738,6 +744,18 @@ class GSConfigStyle extends BaseStyle { data?['_badge']?['w'], ), ), + modal: GSConfigStyle( + maxWidth: data?['_content']?['maxWidth'], + width: data?['_content']?['width'] != null + ? data?['_content']?['width']?.contains('100%') + ? double.infinity + : data?['_content']?['width']?.contains('%') + ? double.tryParse( + data?['_content']?['width']?.replaceAll('%', ''))! / + 100 + : resolveSpaceFromString(data?['_content']?['width']) + : resolveSpaceFromString(data?['_content']?['width']), + ), textTransform: resolveTextTransformFromString(data?['textTransform']), maxWidth: data?['maxWidth'] != null ? double.tryParse(data?['maxWidth']?.toString() ?? "") @@ -747,16 +765,24 @@ class GSConfigStyle extends BaseStyle { : data?['px'] != null && data?['py'] != null ? resolvePaddingFromString(data?['px'].toString(), 'symmetric', paddingy: data?['py'].toString()) - : data?['px'] != null - ? resolvePaddingFromString( - data?['px'].toString(), 'horizontal') - : data?['py'] != null + : data?['px'] != null && + data?["paddingTop"] != null && + data?["paddingBottom"] != null + ? resolvePaddingFromString(data?['px'].toString(), 'only', + paddingTop: data?["paddingTop"].toString(), + paddingBottom: data?["paddingBottom"].toString(), + paddingLeft: data?['px'].toString(), + paddingRight: data?['px'].toString()) + : data?['px'] != null ? resolvePaddingFromString( - data?['py'].toString(), 'vertical') - : data?['pb'] != null + data?['px'].toString(), 'horizontal') + : data?['py'] != null ? resolvePaddingFromString( - data?['pb'].toString(), 'only') - : null, + data?['py'].toString(), 'vertical') + : data?['pb'] != null + ? resolvePaddingFromString( + data?['pb'].toString(), 'only') + : null, margin: data?['m'] != null ? resolvePaddingFromString(data?['m'].toString(), 'all') : data?['mx'] != null && data?['my'] != null @@ -998,7 +1024,7 @@ class GSConfigStyle extends BaseStyle { ), onDisabled: GSConfigStyle( opacity: data?[':disabled']?['opacity'], - bg : resolveColorTokenFromString(data?[':disabled']?['bg']), + bg: resolveColorTokenFromString(data?[':disabled']?['bg']), // textStyle: TextStyle( // color: resolveColorFromString(data?[':disabled']?['color']), // ), @@ -1147,7 +1173,7 @@ class GSConfigStyle extends BaseStyle { ), ), onDisabled: GSConfigStyle( - bg : resolveColorTokenFromString(data?[':disabled']?['bg']), + bg: resolveColorTokenFromString(data?[':disabled']?['bg']), borderColor: resolveColorTokenFromString( data?['_dark']?[':disabled']?['borderColor']), trackColorTrue: resolveColorTokenFromString( @@ -1260,7 +1286,7 @@ class GSConfigStyle extends BaseStyle { ? (data?['transform'].first as Map)['scale'] : null : null, - trackHeight: data?['_track'] != null + trackHeight: data?['_track'] != null ? (data?['_track']['height'] is int ? double.parse('${data?['_track']['height']}.0') : resolveSpaceFromString( @@ -1268,7 +1294,7 @@ class GSConfigStyle extends BaseStyle { data?['_track']['height'].toString(), )) : null, - trackWidth: data?['_track'] != null + trackWidth: data?['_track'] != null ? (data?['_track']['width'] is int ? double.parse('${data?['_track']['width']}.0') : resolveSpaceFromString( diff --git a/lib/src/style/gs_style_external_inline.dart b/lib/src/style/gs_style_external_inline.dart index 9e398baa..26487246 100644 --- a/lib/src/style/gs_style_external_inline.dart +++ b/lib/src/style/gs_style_external_inline.dart @@ -33,6 +33,7 @@ class GSStyle extends BaseStyle { // Color? highlightColor; // Color? splashColor; GSStyle? badge; + GSStyle? modal; GSTextTransform? textTransform; GSSizes? iconSize; @@ -110,6 +111,7 @@ class GSStyle extends BaseStyle { this.alignment, this.maxWidth, this.badge, + this.modal, // this.highlightColor, // this.splashColor, this.textTransform, @@ -215,6 +217,7 @@ class GSStyle extends BaseStyle { width: overrideStyle?.width ?? width, height: overrideStyle?.height ?? height, badge: overrideStyle?.badge ?? badge, + modal : overrideStyle?.modal ?? modal, dark: dark != null ? dark?.merge(overrideStyle?.dark) : overrideStyle?.dark, @@ -336,6 +339,9 @@ class GSStyle extends BaseStyle { badge: styler.badge != null ? fromGSConfigStyle(styler.badge!, context) : null, + modal: styler.modal != null + ? fromGSConfigStyle(styler.modal!, context) + : null, item: styler.item != null ? fromGSConfigStyle(styler.item!, context) : null, checked: styler.checked != null diff --git a/lib/src/theme/config/enums.dart b/lib/src/theme/config/enums.dart index 7a7cdb7d..51f278cc 100644 --- a/lib/src/theme/config/enums.dart +++ b/lib/src/theme/config/enums.dart @@ -299,4 +299,6 @@ enum GSSliderSizes { $sm, $md, $lg, -} \ No newline at end of file +} + +enum GSModalSizes { $xs, $sm, $md, $lg, $full } diff --git a/lib/src/theme/config/modal/modal.dart b/lib/src/theme/config/modal/modal.dart new file mode 100644 index 00000000..dad864b1 --- /dev/null +++ b/lib/src/theme/config/modal/modal.dart @@ -0,0 +1,32 @@ +const Map modalData = { + "width": '\$full', + "height": '\$full', + "justifyContent": 'center', + "alignItems": 'center', + "variants": { + "size": { + "xs": { + "_content": {"width": '60%', "maxWidth": 360} + }, + "sm": { + "_content": {"width": '70%', "maxWidth": 420} + }, + "md": { + "_content": {"width": '80%', "maxWidth": 510} + }, + "lg": { + "_content": {"width": '90%', "maxWidth": 640} + }, + "full": { + "_content": {"width": '100%'} + }, + }, + }, + "defaultProps": {"size": 'md'}, + "_web": { + "pointerEvents": 'box-none', + }, +}; + // { + // descendantStyle: ['_content'], + // } diff --git a/lib/src/theme/config/modal/modal_backdrop.dart b/lib/src/theme/config/modal/modal_backdrop.dart new file mode 100644 index 00000000..8dbda180 --- /dev/null +++ b/lib/src/theme/config/modal/modal_backdrop.dart @@ -0,0 +1,36 @@ +const Map modalBackdropData = { + ':initial': { + "opacity": 0, + }, + + ':animate': { + "opacity": 0.5, + }, + + ':exit': { + "opacity": 0, + }, + + ':transition': { + // @ts-ignore + "type": 'spring', + "damping": 18, + "stiffness": 250, + "opacity": { + "type": 'timing', + "duration": 250, + }, + }, + + 'position': 'absolute', + 'left': 0, + 'top': 0, + 'right': 0, + 'bottom': 0, + 'bg': '\$background950', + + // @ts-ignore + '_web': { + "cursor": 'default', + }, +}; diff --git a/lib/src/theme/config/modal/modal_body.dart b/lib/src/theme/config/modal/modal_body.dart new file mode 100644 index 00000000..6d214336 --- /dev/null +++ b/lib/src/theme/config/modal/modal_body.dart @@ -0,0 +1,5 @@ +const Map modalBodyData = { + "px": '\$4', + "paddingTop": 0, + "paddingBottom": '\$2' +}; diff --git a/lib/src/theme/config/modal/modal_close_button.dart b/lib/src/theme/config/modal/modal_close_button.dart new file mode 100644 index 00000000..0a355111 --- /dev/null +++ b/lib/src/theme/config/modal/modal_close_button.dart @@ -0,0 +1,42 @@ +const Map modalCloseButtonData = { + 'zIndex': 1, + 'p': '\$2', + 'rounded': '\$sm', + '_icon': { + "color": '\$background400', + }, + '_text': { + "color": '\$background400', + }, + ':hover': { + "_icon": { + "color": '\$background700', + }, + "_text": { + "color": '\$background700', + }, + }, + ':active': { + "_icon": { + "color": '\$background900', + }, + "_text": { + "color": '\$background900', + }, + }, + ':focusVisible': { + "bg": '\$background100', + "_icon": { + "color": '\$background900', + }, + "_text": { + "color": '\$background900', + }, + }, + '_web': { + "outlineWidth": 0, + "cursor": 'pointer', + }, +}; + // { descendantStyle: ['_icon', '_text'] } + diff --git a/lib/src/theme/config/modal/modal_content.dart b/lib/src/theme/config/modal/modal_content.dart new file mode 100644 index 00000000..6cb5b41d --- /dev/null +++ b/lib/src/theme/config/modal/modal_content.dart @@ -0,0 +1,39 @@ + +const Map modalContentData = { + 'bg': '\$background50', + 'rounded': '\$lg', + 'overflow': 'hidden', + + ':initial': { + "opacity": 0, + "scale": 0.9, + }, + ':animate': { + "opacity": 1, + "scale": 1, + }, + + ':exit': { + "opacity": 0, + }, + ':transition': { + "type": 'spring', + "damping": 18, + "stiffness": 250, + // @ts-ignore + "opacity": { + "type": 'timing', + "duration": 250, + }, + }, + + 'defaultProps': { + "softShadow": '3', + }, + }; + // { ancestorStyle: ['_content'] } + + + + + diff --git a/lib/src/theme/config/modal/modal_footer.dart b/lib/src/theme/config/modal/modal_footer.dart new file mode 100644 index 00000000..7a01a814 --- /dev/null +++ b/lib/src/theme/config/modal/modal_footer.dart @@ -0,0 +1,7 @@ +const Map modalFooterData = { + "p": '\$4', + "flexDirection": 'row', + "justifyContent": 'flex-end', + "alignItems": 'center', + "flexWrap": 'wrap', +}; diff --git a/lib/src/theme/config/modal/modal_header.dart b/lib/src/theme/config/modal/modal_header.dart new file mode 100644 index 00000000..6cbbdcac --- /dev/null +++ b/lib/src/theme/config/modal/modal_header.dart @@ -0,0 +1,8 @@ +const Map modalHeaderData = { + "px": '\$4', + "paddingTop": '\$4', + "paddingBottom": '\$2', + "justifyContent": 'space-between', + "alignItems": 'center', + "flexDirection": 'row', +}; diff --git a/lib/src/utils/resolver.dart b/lib/src/utils/resolver.dart index 0f2e7efe..be73b545 100644 --- a/lib/src/utils/resolver.dart +++ b/lib/src/utils/resolver.dart @@ -137,7 +137,12 @@ double? resolveSpaceFromString(String? space) { } EdgeInsetsGeometry? resolvePaddingFromString(String? padding, String type, - {String? paddingy, String? side = 'b'}) { + {String? paddingy, + String? side = 'b', + String? paddingTop, + String? paddingBottom, + String? paddingLeft, + String? paddingRight}) { if (padding == null) { return null; } @@ -157,7 +162,17 @@ EdgeInsetsGeometry? resolvePaddingFromString(String? padding, String type, return EdgeInsets.symmetric(vertical: resolveSpaceFromString(padding)!); } if (type == 'only') { - if (side == 'b') { + if (paddingTop != null && + paddingBottom != null && + paddingLeft != null && + paddingRight != null) { + return EdgeInsets.only( + top: resolveSpaceFromString(paddingTop)!, + left: resolveSpaceFromString(paddingLeft)!, + right: resolveSpaceFromString(paddingRight)!, + bottom: resolveSpaceFromString(paddingBottom)!, + ); + } else if (side == 'b') { return EdgeInsets.only(bottom: resolveSpaceFromString(padding)!); } else if (side == 'r') { return EdgeInsets.only(right: resolveSpaceFromString(padding)!); diff --git a/lib/src/widgets/gs_app/gs_app.dart b/lib/src/widgets/gs_app/gs_app.dart index fab61f54..7c047e05 100644 --- a/lib/src/widgets/gs_app/gs_app.dart +++ b/lib/src/widgets/gs_app/gs_app.dart @@ -284,8 +284,8 @@ class _GSAppState extends State { localeListResolutionCallback: widget.localeListResolutionCallback, supportedLocales: widget.supportedLocales, showPerformanceOverlay: widget.showPerformanceOverlay, - checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, - checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, + // checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, + // checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, showSemanticsDebugger: widget.showSemanticsDebugger, debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner, shortcuts: widget.shortcuts, @@ -310,8 +310,8 @@ class _GSAppState extends State { localeResolutionCallback: widget.localeResolutionCallback, supportedLocales: widget.supportedLocales, showPerformanceOverlay: widget.showPerformanceOverlay, - checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, - checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, + // checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, + // checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, showSemanticsDebugger: widget.showSemanticsDebugger, debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner, shortcuts: widget.shortcuts, diff --git a/lib/src/widgets/gs_form_control/gs_form_control.dart b/lib/src/widgets/gs_form_control/gs_form_control.dart index bd4de956..fe5c19da 100644 --- a/lib/src/widgets/gs_form_control/gs_form_control.dart +++ b/lib/src/widgets/gs_form_control/gs_form_control.dart @@ -16,7 +16,7 @@ Form Control Compatible Components: class GSFormControl extends StatefulWidget { final GlobalKey formKey; final Widget child; - final PopInvokedCallback? onPopInvoked; + // final PopInvokedCallback? onPopInvoked; final VoidCallback? onChanged; final AutovalidateMode autovalidateMode; final bool? canPop; @@ -32,7 +32,7 @@ class GSFormControl extends StatefulWidget { const GSFormControl({ super.key, required this.child, - this.onPopInvoked, + // this.onPopInvoked, this.onChanged, this.autovalidateMode = AutovalidateMode.disabled, this.canPop, @@ -84,7 +84,7 @@ class _GSFormControlState extends State { key: widget.formKey, canPop: widget.canPop, onChanged: widget.onChanged, - onPopInvoked: widget.onPopInvoked, + // onPopInvoked: widget.onPopInvoked, autovalidateMode: widget.autovalidateMode, child: widget.child, ), diff --git a/lib/src/widgets/gs_modal/gs_modal.dart b/lib/src/widgets/gs_modal/gs_modal.dart new file mode 100644 index 00000000..a8c2e9cd --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal.dart @@ -0,0 +1,171 @@ +import 'package:gluestack_ui/gluestack_ui.dart'; +import 'package:gluestack_ui/src/style/style_resolver.dart'; +import 'package:gluestack_ui/src/widgets/gs_modal/gs_modal_backdrop_style.dart'; +import 'package:gluestack_ui/src/widgets/gs_modal/gs_modal_provider.dart'; +import 'package:gluestack_ui/src/widgets/gs_modal/gs_modal_style.dart'; +import 'package:gluestack_ui/src/widgets/gs_style_builder/gs_style_builder.dart'; + +class GSModal extends StatefulWidget { + final GSModalSizes size; + final Widget child; + final GSStyle? style; + final GSModalContent content; + final AlignmentGeometry? alignment; + final bool barrierDismissible; + final bool isOpen; + final Function()? onClose; + final bool showBackdrop; + + const GSModal({ + super.key, + required this.size, + required this.child, + this.style, + required this.content, + this.barrierDismissible = true, + this.alignment, + this.onClose, + this.isOpen = false, + this.showBackdrop = true, + }); + + @override + State createState() => _GSModalState(); +} + +class _GSModalState extends State { + OverlayEntry? _overlayEntry; + late bool _isOpen; + + @override + void initState() { + super.initState(); + _isOpen = widget.isOpen; + if (_isOpen) { + _showModal(); + } + } + + @override + void didUpdateWidget(GSModal oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.isOpen && !_isOpen) { + _showModal(); + } else if (!widget.isOpen && _isOpen) { + _removeModal(); + } + } + + void _showModal() { + _overlayEntry = _createOverlayEntry(); + Overlay.of(context).insert(_overlayEntry!); + setState(() { + _isOpen = true; + }); + } + + OverlayEntry _createOverlayEntry() { + return OverlayEntry( + builder: (context) { + final modalSize = widget.size.toGSSize ?? modalStyle.props?.size; + + return GSStyleBuilder( + child: Builder( + builder: (context) { + GSConfigStyle styler = resolveStyles( + context: context, + styles: [ + modalStyle, + modalStyle.sizeMap(modalSize), + ], + inlineStyle: widget.style, + ); + + GSConfigStyle backdropStyler = resolveStyles( + context: context, + styles: [ + gsModalBackdropStyle, + ], + inlineStyle: widget.style, + ); + + return GSModalProvider( + removeModal: _removeModal, + child: GSAncestor( + decedentStyles: styler.descendantStyles, + child: Stack( + children: [ + MouseRegion( + cursor: SystemMouseCursors.basic, + child: GestureDetector( + onTap: () { + if (widget.barrierDismissible == true) { + _removeModal(); + } + }, + child: Container( + color: widget.showBackdrop + ? backdropStyler.bg + ?.getColor(context) + .withOpacity(0.5) ?? + const Color.fromRGBO(0, 0, 0, 0.5) + : const Color.fromRGBO(0, 0, 0, 0), + ), + ), + ), + GestureDetector( + onTap: () { + // Do nothing to prevent the modal from closing when tapped + }, + child: Align( + alignment: styler.alignment ?? Alignment.center, + child: SizedBox( + width: (styler.modal?.maxWidth ?? + 1 * (styler.modal?.width ?? 1)), + height: styler.modal?.height, + child: widget.content, + ), + ), + ), + ], + ), + ), + ); + }, + ), + ); + }, + ); + } + + _removeModal() { + // Guard clause to prevent double execution + if (!_isOpen || _overlayEntry == null) { + return; // Exit if modal is already closed or there's no overlay to remove + } + // Remove the overlay entry + _overlayEntry!.remove(); + _overlayEntry = null; + + // Update the state to closed + _isOpen = false; + + // Defer the onClose callback to avoid conflicts with the current build + WidgetsBinding.instance.addPostFrameCallback((_) { + if (widget.onClose != null) { + widget.onClose!(); + } + }); + } + + @override + Widget build(BuildContext context) { + return GsGestureDetector( + onPressed: () { + _isOpen ? _removeModal() : _showModal(); + }, + child: widget.child, + ); + } +} diff --git a/lib/src/widgets/gs_modal/gs_modal_backdrop_style.dart b/lib/src/widgets/gs_modal/gs_modal_backdrop_style.dart new file mode 100644 index 00000000..04267f54 --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_backdrop_style.dart @@ -0,0 +1,4 @@ +import 'package:gluestack_ui/gluestack_ui.dart'; + +final GSConfigStyle gsModalBackdropStyle = + GSConfigStyle.fromMap(data: getIt().modalBackdrop); diff --git a/lib/src/widgets/gs_modal/gs_modal_body.dart b/lib/src/widgets/gs_modal/gs_modal_body.dart new file mode 100644 index 00000000..69b6d99d --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_body.dart @@ -0,0 +1,26 @@ +import 'package:gluestack_ui/src/style/gs_config_style_internal.dart'; +import 'package:gluestack_ui/src/style/style_resolver.dart'; +import 'package:gluestack_ui/src/widgets/gs_modal/gs_modal_body_style.dart'; + +class GSModalBody extends StatelessWidget { + final Widget? child; + final GSStyle? style; + const GSModalBody({super.key, this.child, this.style}); + + @override + Widget build(BuildContext context) { + GSConfigStyle styler = resolveStyles( + context: context, + styles: [gsModalBodyStyle], + inlineStyle: style, + isFirst: true, + ); + return Container( + color: styler.bg?.getColor(context), + padding: styler.padding, + height: styler.height, + width: styler.width ?? double.infinity, + child: child, + ); + } +} diff --git a/lib/src/widgets/gs_modal/gs_modal_body_style.dart b/lib/src/widgets/gs_modal/gs_modal_body_style.dart new file mode 100644 index 00000000..6840185b --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_body_style.dart @@ -0,0 +1,4 @@ +import 'package:gluestack_ui/gluestack_ui.dart'; + +final GSConfigStyle gsModalBodyStyle = + GSConfigStyle.fromMap(data: getIt().modalBody); diff --git a/lib/src/widgets/gs_modal/gs_modal_close_button.dart b/lib/src/widgets/gs_modal/gs_modal_close_button.dart new file mode 100644 index 00000000..bee4ea95 --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_close_button.dart @@ -0,0 +1,162 @@ +import 'package:flutter/gestures.dart'; +import 'package:gluestack_ui/gluestack_ui.dart'; +import 'package:gluestack_ui/src/style/style_resolver.dart'; +import 'package:gluestack_ui/src/widgets/gs_modal/gs_modal_close_button_style.dart'; +import 'package:gluestack_ui/src/widgets/gs_modal/gs_modal_provider.dart'; + +/// predefined sizes for [GSModalCloseButton], providing a consistent set of size options for icon buttons. +enum GSModalCloseButtonSizes { + $xs, + $sm, + $md, + $lg, +} + +/// A widget that represents an icon button, +/// [GSModalCloseButton] allows for the creation of buttons with icon content, supporting various interactions +/// such as taps, long presses, and double taps. +class GSModalCloseButton extends StatefulWidget { + /// The icon to display within the button. + final GSIcon? icon; + + final GSText? text; + + /// The callback that is called when the button is long-pressed. + final VoidCallback? onLongPress; + + /// The callback that is called when the button is double-tapped. + final GestureDoubleTapCallback? onDoubleTap; + + /// An optional semantic label for the button, used by screen readers. + final String? semanticsLabel; + + /// The size of the icon button, affecting its overall dimensions. + final GSModalCloseButtonSizes? size; + + /// Custom [GSConfigStyle] to apply to the button, enabling detailed customization of its appearance. + final GSStyle? style; + + final GSButtonVariants? variant; + + final GSButtonActions? action; + + ///Constructor for [GSModalCloseButton] + const GSModalCloseButton({ + super.key, + this.icon, + this.text, + this.onLongPress, + this.onDoubleTap, + this.style, + this.semanticsLabel, + this.action, + this.variant, + this.size = GSModalCloseButtonSizes.$md, + }); + + @override + State createState() => _GSModalCloseButtonState(); +} + +class _GSModalCloseButtonState extends State { + bool hovered = false; + @override + Widget build(BuildContext context) { + GSButtonSizes? sizeAdapt(GSModalCloseButtonSizes buttonSize) { + for (GSButtonSizes bSize in GSButtonSizes.values) { + if (bSize.name == buttonSize.name) { + return bSize; + } + } + return null; + } + + GSConfigStyle styler = resolveStyles( + context: context, + styles: [gsModalCloseButtonStyle], + inlineStyle: widget.style, + ); + final removeModal = GSModalProvider.of(context)?.removeModal; + + void handleHoveHighlight(bool value) { + setState(() { + hovered = value; + }); + } + + return FocusableActionDetector( + onShowHoverHighlight: (value) { + handleHoveHighlight(value); + }, + child: GSButton( + variant: widget.variant ?? GSButtonVariants.link, + action: widget.action ?? GSButtonActions.primary, + onPressed: () { + if (widget.icon == null && widget.text == null) { + } else { + removeModal!(); + } + }, + onLongPress: widget.onLongPress, + onDoubleTap: widget.onDoubleTap, + semanticsLabel: widget.semanticsLabel, + style: widget.style ?? + GSStyle( + color: styler.color?.getColor(context), + iconColor: styler.iconColor?.getColor(context), + padding: styler.padding, + borderRadius: widget.style?.borderRadius, + ), + size: sizeAdapt(widget.size!), + child: widget.icon == null && widget.text == null + ? const SizedBox.shrink() + : widget.icon != null + ? Icon( + widget.icon?.icon, + size: widget.icon?.style?.width ?? + styler.width ?? + widget.icon?.style?.height ?? + styler.height, + color: hovered == true + ? widget.icon?.style?.onHover?.color ?? + styler.onHover?.iconColor?.getColor(context) + : widget.icon?.style?.iconColor ?? + styler.iconColor?.getColor(context), + fill: widget.icon?.fill, + grade: widget.icon?.grade, + opticalSize: widget.icon?.opticalSize, + semanticLabel: widget.icon?.semanticLabel, + shadows: widget.icon?.shadows, + textDirection: widget.icon?.textDirection, + weight: widget.icon?.weight, + ) + : Text(widget.text!.text, + locale: widget.text!.locale, + maxLines: widget.text!.maxLines, + overflow: widget.text!.overflow, + selectionColor: widget.text!.selectionColor, + semanticsLabel: widget.text!.semanticsLabel, + softWrap: widget.text!.softWrap, + strutStyle: widget.text!.strutStyle, + textAlign: widget.text!.style?.textAlign, + textDirection: widget.text!.textDirection, + textHeightBehavior: widget.text!.textHeightBehavior, + textScaler: widget.text!.textScaler, + textWidthBasis: widget.text!.textWidthBasis, + style: widget.text?.style?.textStyle?.copyWith( + color: hovered == true + ? styler.onHover?.color?.getColor(context) + : styler.color?.getColor(context), + ) ?? + TextStyle( + color: hovered == true + ? styler.onHover?.color?.getColor(context) + : styler.color?.getColor(context), + fontSize: + widget.text?.style?.textStyle?.fontSize, + fontWeight: + widget.text?.style?.textStyle?.fontWeight, + fontFamily: widget + .text?.style?.textStyle?.fontFamily)))); + } +} diff --git a/lib/src/widgets/gs_modal/gs_modal_close_button_style.dart b/lib/src/widgets/gs_modal/gs_modal_close_button_style.dart new file mode 100644 index 00000000..356ced1b --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_close_button_style.dart @@ -0,0 +1,9 @@ +import 'package:gluestack_ui/gluestack_ui.dart'; +import 'package:gluestack_ui/src/style/gs_style_config.dart'; + +const GSStyleConfig gsModalCloseButtonConfig = + GSStyleConfig(componentName: 'Modal', descendantStyle: ['_icon', '_text']); + +final GSConfigStyle gsModalCloseButtonStyle = GSConfigStyle.fromMap( + data: getIt().modalCloseButton, + descendantStyle: gsModalCloseButtonConfig.descendantStyle); diff --git a/lib/src/widgets/gs_modal/gs_modal_content.dart b/lib/src/widgets/gs_modal/gs_modal_content.dart new file mode 100644 index 00000000..90021c16 --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_content.dart @@ -0,0 +1,36 @@ +import 'package:gluestack_ui/src/style/gs_config_style_internal.dart'; +import 'package:gluestack_ui/src/style/style_resolver.dart'; +import 'package:gluestack_ui/src/widgets/gs_modal/gs_modal_content_style.dart'; + +class GSModalContent extends StatelessWidget { + final GSModalHeader? header; + final GSModalBody? body; + final GSModalFooter? footer; + final GSStyle? style; + const GSModalContent( + {super.key, this.header, this.body, this.footer, this.style}); + + @override + Widget build(BuildContext context) { + GSConfigStyle styler = resolveStyles( + context: context, + styles: [gsModalContentStyle], + inlineStyle: style, + ); + + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(styler.borderRadius ?? 8), + color: styler.bg?.getColor(context), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + header ?? const SizedBox(), + body ?? const SizedBox(), + footer ?? const SizedBox(), + ], + ), + ); + } +} diff --git a/lib/src/widgets/gs_modal/gs_modal_content_style.dart b/lib/src/widgets/gs_modal/gs_modal_content_style.dart new file mode 100644 index 00000000..795a99ab --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_content_style.dart @@ -0,0 +1,10 @@ +import 'package:gluestack_ui/gluestack_ui.dart'; +import 'package:gluestack_ui/src/style/gs_style_config.dart'; + +const GSStyleConfig gsModalContentConfig = GSStyleConfig( + componentName: 'Modal', + ancestorStyle: ['_content'], +); + +final GSConfigStyle gsModalContentStyle = + GSConfigStyle.fromMap(data: getIt().modalContent); diff --git a/lib/src/widgets/gs_modal/gs_modal_footer.dart b/lib/src/widgets/gs_modal/gs_modal_footer.dart new file mode 100644 index 00000000..c0f89862 --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_footer.dart @@ -0,0 +1,36 @@ +import 'package:gluestack_ui/src/style/gs_config_style_internal.dart'; +import 'package:gluestack_ui/src/style/style_resolver.dart'; +import 'package:gluestack_ui/src/utils/resolver.dart'; +import 'package:gluestack_ui/src/widgets/gs_modal/gs_modal_footer_style.dart'; + +class GSModalFooter extends StatelessWidget { + final Widget? child; + final GSStyle? style; + const GSModalFooter({super.key, this.child, this.style}); + + @override + Widget build(BuildContext context) { + return Builder(builder: (context) { + GSConfigStyle styler = resolveStyles( + context: context, + styles: [gsModalFooterStyle], + inlineStyle: style, + isFirst: true, + ); + final x = resolveAlignment(styler.justifyContent); + final alignment = resolveAlignmentFromNum(x); + return Container( + color: styler.bg?.getColor(context), + padding: styler.padding, + height: styler.height, + width: styler.width, + child: Row( + mainAxisAlignment: alignment, + children: [ + child ?? const SizedBox(), + ], + ), + ); + }); + } +} diff --git a/lib/src/widgets/gs_modal/gs_modal_footer_style.dart b/lib/src/widgets/gs_modal/gs_modal_footer_style.dart new file mode 100644 index 00000000..cc79de92 --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_footer_style.dart @@ -0,0 +1,4 @@ +import 'package:gluestack_ui/gluestack_ui.dart'; + +final GSConfigStyle gsModalFooterStyle = + GSConfigStyle.fromMap(data: getIt().modalFooter); diff --git a/lib/src/widgets/gs_modal/gs_modal_header.dart b/lib/src/widgets/gs_modal/gs_modal_header.dart new file mode 100644 index 00000000..7b37e6fe --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_header.dart @@ -0,0 +1,41 @@ +import 'package:gluestack_ui/src/style/gs_config_style_internal.dart'; +import 'package:gluestack_ui/src/style/style_resolver.dart'; +import 'package:gluestack_ui/src/utils/resolver.dart'; +import 'package:gluestack_ui/src/widgets/gs_modal/gs_modal_header_style.dart'; + +class GSModalHeader extends StatelessWidget { + final Widget? child; + final GSModalCloseButton? closeButton; + final GSStyle? style; + const GSModalHeader({super.key, this.child, this.closeButton, this.style}); + + @override + Widget build(BuildContext context) { + GSConfigStyle styler = resolveStyles( + context: context, + styles: [gsModalHeaderStyle], + inlineStyle: style, + ); + final y = resolveAlignment(styler.alignItems), + x = resolveAlignment(styler.justifyContent); + return Row( + children: [ + Expanded( + child: Container( + color: styler.bg?.getColor(context), + alignment: Alignment(x, y), + padding: styler.padding, + height: styler.height, + width: styler.width ?? double.infinity, + child: child, + ), + ), + Container( + color: styler.bg?.getColor(context), + alignment: Alignment(x, y), + padding: styler.padding, + child: closeButton ?? const SizedBox.shrink()), + ], + ); + } +} diff --git a/lib/src/widgets/gs_modal/gs_modal_header_style.dart b/lib/src/widgets/gs_modal/gs_modal_header_style.dart new file mode 100644 index 00000000..21db9b67 --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_header_style.dart @@ -0,0 +1,4 @@ +import 'package:gluestack_ui/gluestack_ui.dart'; + + GSConfigStyle gsModalHeaderStyle = + GSConfigStyle.fromMap(data: getIt().modalHeader); diff --git a/lib/src/widgets/gs_modal/gs_modal_provider.dart b/lib/src/widgets/gs_modal/gs_modal_provider.dart new file mode 100644 index 00000000..512a89b3 --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_provider.dart @@ -0,0 +1,20 @@ +import 'package:gluestack_ui/gluestack_ui.dart'; + +class GSModalProvider extends InheritedWidget { + final VoidCallback removeModal; + + const GSModalProvider({ + super.key, + required this.removeModal, + required super.child, + }); + + @override + bool updateShouldNotify(covariant GSModalProvider oldWidget) { + return removeModal != oldWidget.removeModal; + } + + static GSModalProvider? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } +} \ No newline at end of file diff --git a/lib/src/widgets/gs_modal/gs_modal_style.dart b/lib/src/widgets/gs_modal/gs_modal_style.dart new file mode 100644 index 00000000..5ce06883 --- /dev/null +++ b/lib/src/widgets/gs_modal/gs_modal_style.dart @@ -0,0 +1,8 @@ +import 'package:gluestack_ui/gluestack_ui.dart'; +import 'package:gluestack_ui/src/style/gs_style_config.dart'; + +const GSStyleConfig gsModalConfig = + GSStyleConfig(componentName: 'Modal', descendantStyle: ['_content']); + GSConfigStyle modalStyle = GSConfigStyle.fromMap( + data: getIt().modal, + descendantStyle: gsModalConfig.descendantStyle); diff --git a/lib/src/widgets/gs_modal/public.dart b/lib/src/widgets/gs_modal/public.dart new file mode 100644 index 00000000..9a676cb1 --- /dev/null +++ b/lib/src/widgets/gs_modal/public.dart @@ -0,0 +1,6 @@ +export 'gs_modal.dart'; +export 'gs_modal_body.dart'; +export 'gs_modal_footer.dart'; +export 'gs_modal_header.dart'; +export 'gs_modal_content.dart'; +export 'gs_modal_close_button.dart'; \ No newline at end of file diff --git a/lib/src/widgets/gs_radio/gs_radio_icon.dart b/lib/src/widgets/gs_radio/gs_radio_icon.dart index 9689cb76..6e8c1e58 100644 --- a/lib/src/widgets/gs_radio/gs_radio_icon.dart +++ b/lib/src/widgets/gs_radio/gs_radio_icon.dart @@ -2,8 +2,6 @@ import 'package:gluestack_ui/src/style/gs_config_style_internal.dart'; import 'package:gluestack_ui/src/style/style_resolver.dart'; import 'package:gluestack_ui/src/widgets/gs_radio/gs_radio_icon_style.dart'; -import 'package:gluestack_ui/src/widgets/gs_radio/gs_radio_raw.dart'; - class GSRadioIcon extends StatelessWidget { final Color? activeColor; final bool autofocus; diff --git a/lib/src/widgets/gs_radio/gs_radio_raw.dart b/lib/src/widgets/gs_radio/gs_radio_raw.dart index 4e29b440..00a078b5 100644 --- a/lib/src/widgets/gs_radio/gs_radio_raw.dart +++ b/lib/src/widgets/gs_radio/gs_radio_raw.dart @@ -1,7 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/cupertino.dart'; import 'package:gluestack_ui/gluestack_ui.dart'; -import 'gs_toggleable.dart'; const Size _size = Size(18.0, 18.0); const double _kOuterRadius = 8.0; diff --git a/lib/src/widgets/gs_radio/public.dart b/lib/src/widgets/gs_radio/public.dart index cec54ad8..35d80cfe 100644 --- a/lib/src/widgets/gs_radio/public.dart +++ b/lib/src/widgets/gs_radio/public.dart @@ -2,3 +2,4 @@ export 'gs_radio_icon.dart'; export 'gs_radio_provider.dart'; export 'gs_radio_text.dart'; export 'gs_radio.dart'; +export 'gs_radio_raw.dart';