diff --git a/example/assets/code_snippets/combobox_multi_select.md b/example/assets/code_snippets/combobox_multi_select.md index 944567f7..d76d0634 100644 --- a/example/assets/code_snippets/combobox_multi_select.md +++ b/example/assets/code_snippets/combobox_multi_select.md @@ -113,7 +113,7 @@ class _ComboboxMultiSelectState extends State { }, ), ), - child: MoonTextInput( + child: MoonInput( focusNode: _focusNode, hintText: "Select multiple components", controller: _searchController, diff --git a/example/assets/code_snippets/combobox_single_select.md b/example/assets/code_snippets/combobox_single_select.md index a6f09dd4..c5ab1af0 100644 --- a/example/assets/code_snippets/combobox_single_select.md +++ b/example/assets/code_snippets/combobox_single_select.md @@ -115,7 +115,7 @@ class _ComboboxSingleSelectState extends State { }, ), ), - child: MoonTextInput( + child: MoonInput( focusNode: _focusNode, hintText: "Select single component", controller: _searchController, diff --git a/example/assets/code_snippets/dropdown.md b/example/assets/code_snippets/dropdown.md index 60e8bfb7..f2f09d1a 100644 --- a/example/assets/code_snippets/dropdown.md +++ b/example/assets/code_snippets/dropdown.md @@ -61,7 +61,7 @@ class _DropdownState extends State { ), ), ), - child: MoonTextInput( + child: MoonInput( width: 250, readOnly: true, canRequestFocus: false, diff --git a/example/assets/code_snippets/text_input.md b/example/assets/code_snippets/input.md similarity index 83% rename from example/assets/code_snippets/text_input.md rename to example/assets/code_snippets/input.md index bb4430ee..4e7a3ba5 100644 --- a/example/assets/code_snippets/text_input.md +++ b/example/assets/code_snippets/input.md @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; import 'package:moon_design/moon_design.dart'; -class TextInput extends StatefulWidget { - const TextInput({super.key}); +class Input extends StatefulWidget { + const Input({super.key}); @override - State createState() => _TextInputState(); + State createState() => _InputState(); } -class _TextInputState extends State { +class _InputState extends State { final TextEditingController _textController = TextEditingController(); @override @@ -18,7 +18,7 @@ class _TextInputState extends State { builder: (BuildContext context) { return Column( children: [ - MoonFormTextInput( + MoonInput.form( controller: _textController, validator: (String? value) => value != null && value.length < 5 ? "The text should be longer than 5 characters." diff --git a/example/assets/code_snippets/text_input_group.md b/example/assets/code_snippets/input_group.md similarity index 81% rename from example/assets/code_snippets/text_input_group.md rename to example/assets/code_snippets/input_group.md index af961e36..b7d35b8b 100644 --- a/example/assets/code_snippets/text_input_group.md +++ b/example/assets/code_snippets/input_group.md @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; import 'package:moon_design/moon_design.dart'; -class TextInputGroup extends StatefulWidget { - const TextInputGroup({super.key}); +class InputGroup extends StatefulWidget { + const InputGroup({super.key}); @override - State createState() => _TextInputGroupState(); + State createState() => _InputGroupState(); } -class _TextInputGroupState extends State { +class _InputGroupState extends State { final TextEditingController _textController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); @@ -21,10 +21,10 @@ class _TextInputGroupState extends State { builder: (context) { return Column( children: [ - MoonTextInputGroup( + MoonInputGroup( children: [ - MoonFormTextInput( - textInputSize: MoonTextInputSize.xl, + MoonInput.form( + inputSize: MoonInputSize.xl, controller: _textController, validator: (String? value) => value?.length != null && value!.length < 5 ? "The text should be longer than 5 characters." @@ -35,8 +35,8 @@ class _TextInputGroupState extends State { child: const Icon(MoonIcons.controls_close_small_24_light), ), ), - MoonFormTextInput( - textInputSize: MoonTextInputSize.xl, + MoonInput.form( + inputSize: MoonInputSize.xl, obscureText: _hidePassword, controller: _passwordController, validator: (String? value) => value != "123" ? "Wrong password." : null, diff --git a/example/assets/code_snippets/search_with_dropdown.md b/example/assets/code_snippets/search_with_dropdown.md index ed5ea76f..6dcc45d1 100644 --- a/example/assets/code_snippets/search_with_dropdown.md +++ b/example/assets/code_snippets/search_with_dropdown.md @@ -117,7 +117,7 @@ class _SearchWithDropdownState extends State { }, ), ), - child: MoonTextInput( + child: MoonInput( hintText: "Search components", controller: _searchController, borderRadius: _showDropdown ? const BorderRadius.vertical(top: Radius.circular(8)) : null, diff --git a/example/assets/code_snippets/search_with_list.md b/example/assets/code_snippets/search_with_list.md index b373761f..883403e0 100644 --- a/example/assets/code_snippets/search_with_list.md +++ b/example/assets/code_snippets/search_with_list.md @@ -65,7 +65,7 @@ class _SearchWithListState extends State { children: [ Column( children: [ - MoonTextInput( + MoonInput( hintText: "Search components", controller: _searchController, // The onTap() and onChanged() properties are used instead of a listener to initiate search on every input tap. diff --git a/example/assets/components/text_input.png b/example/assets/components/input.png similarity index 100% rename from example/assets/components/text_input.png rename to example/assets/components/input.png diff --git a/example/assets/components/text_input_group.png b/example/assets/components/input_group.png similarity index 100% rename from example/assets/components/text_input_group.png rename to example/assets/components/input_group.png diff --git a/example/lib/src/storybook/common/component_options.dart b/example/lib/src/storybook/common/component_options.dart index 7a74c5e9..2b29a4cb 100644 --- a/example/lib/src/storybook/common/component_options.dart +++ b/example/lib/src/storybook/common/component_options.dart @@ -16,6 +16,8 @@ import 'package:example/src/storybook/stories/primitives/dot_indicator.dart'; import 'package:example/src/storybook/stories/primitives/drawer.dart'; import 'package:example/src/storybook/stories/primitives/dropdown.dart'; import 'package:example/src/storybook/stories/primitives/icons.dart'; +import 'package:example/src/storybook/stories/primitives/input.dart'; +import 'package:example/src/storybook/stories/primitives/input_group.dart'; import 'package:example/src/storybook/stories/primitives/linear_loader.dart'; import 'package:example/src/storybook/stories/primitives/linear_progress.dart'; import 'package:example/src/storybook/stories/primitives/menu_item.dart'; @@ -28,8 +30,6 @@ import 'package:example/src/storybook/stories/primitives/tab_bar.dart'; import 'package:example/src/storybook/stories/primitives/table.dart'; import 'package:example/src/storybook/stories/primitives/tag.dart'; import 'package:example/src/storybook/stories/primitives/text_area.dart'; -import 'package:example/src/storybook/stories/primitives/text_input.dart'; -import 'package:example/src/storybook/stories/primitives/text_input_group.dart'; import 'package:example/src/storybook/stories/primitives/toast.dart'; import 'package:example/src/storybook/stories/primitives/tooltip.dart'; @@ -51,6 +51,8 @@ enum Component { drawer, dropdown, icons, + input, + inputGroup, linearLoader, linearProgress, menuItem, @@ -64,8 +66,6 @@ enum Component { table, tag, textArea, - textInput, - textInputGroup, toast, tooltip; @@ -111,6 +111,9 @@ enum Component { "Dropdown list allows users to choose one value from a menu by clicking.", icons => 'Commonly used interface icons provided by Moon Design System.', + input => 'Input fields allow users to enter text.', + inputGroup => + 'Combine different types of inputs into groups to save vertical space on designs.', linearLoader => "Loaders provide visual feedback for in-progress tasks.", linearProgress => @@ -135,9 +138,6 @@ enum Component { tag => "Tags are interactive keywords used to organize and categorize objects.", textArea => 'A form control for entering and editing multi-line text.', - textInput => 'Text input fields allow users to enter text.', - textInputGroup => - 'Combine different types of inputs into groups to save vertical space on designs.', toast => "Toast provides brief feedback message without interrupting the interface.", tooltip => 'Tooltip displays additional information about an element.', @@ -161,6 +161,8 @@ enum Component { drawer => DrawerStory.path, dropdown => DropdownStory.path, icons => IconsStory.path, + input => InputStory.path, + inputGroup => InputGroupStory.path, linearLoader => LinearLoaderStory.path, linearProgress => LinearProgressStory.path, menuItem => MenuItemStory.path, @@ -174,8 +176,6 @@ enum Component { table => TableStory.path, tag => TagStory.path, textArea => TextAreaStory.path, - textInput => TextInputStory.path, - textInputGroup => TextInputGroupStory.path, toast => ToastStory.path, tooltip => TooltipStory.path, }; diff --git a/example/lib/src/storybook/routing/app_router.dart b/example/lib/src/storybook/routing/app_router.dart index ea2f1e14..f7fbe03b 100644 --- a/example/lib/src/storybook/routing/app_router.dart +++ b/example/lib/src/storybook/routing/app_router.dart @@ -2,6 +2,7 @@ import 'package:example/src/storybook/common/pages/colors_page.dart'; import 'package:example/src/storybook/common/pages/home_page.dart'; import 'package:example/src/storybook/common/pages/typography_page.dart'; import 'package:example/src/storybook/common/widgets/routing_error_widget.dart'; + import 'package:example/src/storybook/stories/composites/combobox_multi_select.dart'; import 'package:example/src/storybook/stories/composites/combobox_single_select.dart'; import 'package:example/src/storybook/stories/composites/search_with_dropdown.dart'; @@ -22,6 +23,8 @@ import 'package:example/src/storybook/stories/primitives/dot_indicator.dart'; import 'package:example/src/storybook/stories/primitives/drawer.dart'; import 'package:example/src/storybook/stories/primitives/dropdown.dart'; import 'package:example/src/storybook/stories/primitives/icons.dart'; +import 'package:example/src/storybook/stories/primitives/input.dart'; +import 'package:example/src/storybook/stories/primitives/input_group.dart'; import 'package:example/src/storybook/stories/primitives/linear_loader.dart'; import 'package:example/src/storybook/stories/primitives/linear_progress.dart'; import 'package:example/src/storybook/stories/primitives/menu_item.dart'; @@ -34,12 +37,11 @@ import 'package:example/src/storybook/stories/primitives/tab_bar.dart'; import 'package:example/src/storybook/stories/primitives/table.dart'; import 'package:example/src/storybook/stories/primitives/tag.dart'; import 'package:example/src/storybook/stories/primitives/text_area.dart'; -import 'package:example/src/storybook/stories/primitives/text_input.dart'; -import 'package:example/src/storybook/stories/primitives/text_input_group.dart'; import 'package:example/src/storybook/stories/primitives/toast.dart'; import 'package:example/src/storybook/stories/primitives/tooltip.dart'; import 'package:flutter/material.dart'; + import 'package:go_router/go_router.dart'; const String primitivesDirectory = '/primitives'; @@ -214,6 +216,18 @@ GoRouter router = GoRouter( child: IconsStory(), ), ), + GoRoute( + path: InputStory.path, + pageBuilder: (BuildContext _, GoRouterState __) => const NoTransitionPage( + child: InputStory(), + ), + ), + GoRoute( + path: InputGroupStory.path, + pageBuilder: (BuildContext _, GoRouterState __) => const NoTransitionPage( + child: InputGroupStory(), + ), + ), GoRoute( path: LinearLoaderStory.path, pageBuilder: (BuildContext _, GoRouterState __) => const NoTransitionPage( @@ -302,18 +316,6 @@ GoRouter router = GoRouter( child: TextAreaStory(), ), ), - GoRoute( - path: TextInputStory.path, - pageBuilder: (BuildContext _, GoRouterState __) => const NoTransitionPage( - child: TextInputStory(), - ), - ), - GoRoute( - path: TextInputGroupStory.path, - pageBuilder: (BuildContext _, GoRouterState __) => const NoTransitionPage( - child: TextInputGroupStory(), - ), - ), GoRoute( path: ToastStory.path, pageBuilder: (BuildContext _, GoRouterState __) => const NoTransitionPage( diff --git a/example/lib/src/storybook/routing/route_aware_stories.dart b/example/lib/src/storybook/routing/route_aware_stories.dart index 58138591..86be6f09 100644 --- a/example/lib/src/storybook/routing/route_aware_stories.dart +++ b/example/lib/src/storybook/routing/route_aware_stories.dart @@ -1,7 +1,9 @@ import 'package:example/src/storybook/common/pages/colors_page.dart'; import 'package:example/src/storybook/common/pages/home_page.dart'; import 'package:example/src/storybook/common/pages/typography_page.dart'; + import 'package:example/src/storybook/routing/app_router.dart'; + import 'package:example/src/storybook/stories/composites/combobox_multi_select.dart'; import 'package:example/src/storybook/stories/composites/combobox_single_select.dart'; import 'package:example/src/storybook/stories/composites/search_with_dropdown.dart'; @@ -22,6 +24,8 @@ import 'package:example/src/storybook/stories/primitives/dot_indicator.dart'; import 'package:example/src/storybook/stories/primitives/drawer.dart'; import 'package:example/src/storybook/stories/primitives/dropdown.dart'; import 'package:example/src/storybook/stories/primitives/icons.dart'; +import 'package:example/src/storybook/stories/primitives/input.dart'; +import 'package:example/src/storybook/stories/primitives/input_group.dart'; import 'package:example/src/storybook/stories/primitives/linear_loader.dart'; import 'package:example/src/storybook/stories/primitives/linear_progress.dart'; import 'package:example/src/storybook/stories/primitives/menu_item.dart'; @@ -34,12 +38,11 @@ import 'package:example/src/storybook/stories/primitives/tab_bar.dart'; import 'package:example/src/storybook/stories/primitives/table.dart'; import 'package:example/src/storybook/stories/primitives/tag.dart'; import 'package:example/src/storybook/stories/primitives/text_area.dart'; -import 'package:example/src/storybook/stories/primitives/text_input.dart'; -import 'package:example/src/storybook/stories/primitives/text_input_group.dart'; import 'package:example/src/storybook/stories/primitives/toast.dart'; import 'package:example/src/storybook/stories/primitives/tooltip.dart'; import 'package:flutter/services.dart'; + import 'package:storybook_flutter/storybook_flutter.dart'; const String directory = 'assets/code_snippets/'; @@ -188,6 +191,18 @@ final List routeAwareStories = [ router: router, codeString: fetchAsset('icons.md'), ), + Story.asRoute( + name: 'Primitives/Input', + routePath: InputStory.path, + router: router, + codeString: fetchAsset('input.md'), + ), + Story.asRoute( + name: 'Primitives/InputGroup', + routePath: InputGroupStory.path, + router: router, + codeString: fetchAsset('input_group.md'), + ), Story.asRoute( name: 'Primitives/Loader/LinearLoader', routePath: LinearLoaderStory.path, @@ -260,18 +275,6 @@ final List routeAwareStories = [ router: router, codeString: fetchAsset('text_area.md'), ), - Story.asRoute( - name: 'Primitives/TextInput', - routePath: TextInputStory.path, - router: router, - codeString: fetchAsset('text_input.md'), - ), - Story.asRoute( - name: 'Primitives/TextInputGroup', - routePath: TextInputGroupStory.path, - router: router, - codeString: fetchAsset('text_input_group.md'), - ), Story.asRoute( name: 'Primitives/Toast', routePath: ToastStory.path, diff --git a/example/lib/src/storybook/stories/composites/combobox_multi_select.dart b/example/lib/src/storybook/stories/composites/combobox_multi_select.dart index 9acb7c56..9386dcb8 100644 --- a/example/lib/src/storybook/stories/composites/combobox_multi_select.dart +++ b/example/lib/src/storybook/stories/composites/combobox_multi_select.dart @@ -76,22 +76,22 @@ class _ComboboxMultiSelectStoryState extends State { @override Widget build(BuildContext context) { - final textInputSizeKnob = context.knobs.nullable.options( - label: "textInputSize", - description: "Size variants for MoonTextInput.", + final inputSizeKnob = context.knobs.nullable.options( + label: "inputSize", + description: "Size variants for MoonInput.", enabled: false, - initial: MoonTextInputSize.md, + initial: MoonInputSize.md, options: const [ - Option(label: "sm", value: MoonTextInputSize.sm), - Option(label: "md", value: MoonTextInputSize.md), - Option(label: "lg", value: MoonTextInputSize.lg), - Option(label: "xl", value: MoonTextInputSize.xl), + Option(label: "sm", value: MoonInputSize.sm), + Option(label: "md", value: MoonInputSize.md), + Option(label: "lg", value: MoonInputSize.lg), + Option(label: "xl", value: MoonInputSize.xl), ], ); final activeBorderColorKnob = context.knobs.nullable.options( label: "activeBorderColor", - description: "MoonColors variants for MoonTextInput active border.", + description: "MoonColors variants for MoonInput active border.", enabled: false, initial: 0, // piccolo @@ -102,7 +102,7 @@ class _ComboboxMultiSelectStoryState extends State { final inactiveBorderColorKnob = context.knobs.nullable.options( label: "inactiveBorderColor", - description: "MoonColors variants for MoonTextInput inactive border.", + description: "MoonColors variants for MoonInput inactive border.", enabled: false, initial: 0, // piccolo @@ -112,21 +112,21 @@ class _ComboboxMultiSelectStoryState extends State { final inactiveBorderColor = colorTable(context)[inactiveBorderColorKnob ?? 40]; - final hoverBorderColorKnob = context.knobs.nullable.options( - label: "hoverBorderColor", - description: "MoonColors variants for MoonTextInput border on hover.", + final hoverColorKnob = context.knobs.nullable.options( + label: "hoverColor", + description: "MoonColors variants for MoonInput on hover.", enabled: false, initial: 0, // piccolo options: colorOptions, ); - final hoverBorderColor = colorTable(context)[hoverBorderColorKnob ?? 40]; + final hoverColor = colorTable(context)[hoverColorKnob ?? 40]; final backgroundColorKnob = context.knobs.nullable.options( label: "backgroundColor", description: - "MoonColors variants for MoonTextInput and MoonDropdown background.", + "MoonColors variants for MoonInput and MoonDropdown background.", enabled: false, initial: 0, // piccolo @@ -137,7 +137,7 @@ class _ComboboxMultiSelectStoryState extends State { final borderRadiusKnob = context.knobs.nullable.sliderInt( label: "borderRadius", - description: "Border radius for MoonTextInput and MoonDropdown", + description: "Border radius for MoonInput and MoonDropdown", enabled: false, initial: 8, max: 32, @@ -159,13 +159,13 @@ class _ComboboxMultiSelectStoryState extends State { final enabledKnob = context.knobs.boolean( label: "enabled", - description: "Switch between MoonTextInput enabled and disabled states.", + description: "Switch between MoonInput enabled and disabled states.", initial: true, ); final hasFloatingLabelKnob = context.knobs.boolean( label: "hasFloatingLabel", - description: "Whether MoonTextInput has floating label.", + description: "Whether MoonInput has floating label.", ); final BorderRadiusGeometry? borderRadius = borderRadiusKnob != null @@ -225,16 +225,16 @@ class _ComboboxMultiSelectStoryState extends State { ), ), ), - child: MoonTextInput( + child: MoonInput( enabled: enabledKnob, hasFloatingLabel: hasFloatingLabelKnob, width: 270, focusNode: _focusNode, - textInputSize: textInputSizeKnob, + inputSize: inputSizeKnob, activeBorderColor: activeBorderColor, inactiveBorderColor: inactiveBorderColor, backgroundColor: backgroundColor, - hoverBorderColor: hoverBorderColor, + hoverColor: hoverColor, borderRadius: borderRadius, hintText: "Select multiple components", controller: _searchController, diff --git a/example/lib/src/storybook/stories/composites/combobox_single_select.dart b/example/lib/src/storybook/stories/composites/combobox_single_select.dart index afe45259..243ba19a 100644 --- a/example/lib/src/storybook/stories/composites/combobox_single_select.dart +++ b/example/lib/src/storybook/stories/composites/combobox_single_select.dart @@ -82,22 +82,22 @@ class _ComboboxSingleSelectStoryState extends State { @override Widget build(BuildContext context) { - final textInputSizeKnob = context.knobs.nullable.options( - label: "textInputSize", - description: "Size variants for MoonTextInput.", + final inputSizeKnob = context.knobs.nullable.options( + label: "inputSize", + description: "Size variants for MoonInput.", enabled: false, - initial: MoonTextInputSize.md, + initial: MoonInputSize.md, options: const [ - Option(label: "sm", value: MoonTextInputSize.sm), - Option(label: "md", value: MoonTextInputSize.md), - Option(label: "lg", value: MoonTextInputSize.lg), - Option(label: "xl", value: MoonTextInputSize.xl), + Option(label: "sm", value: MoonInputSize.sm), + Option(label: "md", value: MoonInputSize.md), + Option(label: "lg", value: MoonInputSize.lg), + Option(label: "xl", value: MoonInputSize.xl), ], ); final activeBorderColorKnob = context.knobs.nullable.options( label: "activeBorderColor", - description: "MoonColors variants for MoonTextInput active border.", + description: "MoonColors variants for MoonInput active border.", enabled: false, initial: 0, // piccolo @@ -108,7 +108,7 @@ class _ComboboxSingleSelectStoryState extends State { final inactiveBorderColorKnob = context.knobs.nullable.options( label: "inactiveBorderColor", - description: "MoonColors variants for MoonTextInput inactive border.", + description: "MoonColors variants for MoonInput inactive border.", enabled: false, initial: 0, // piccolo @@ -118,21 +118,21 @@ class _ComboboxSingleSelectStoryState extends State { final inactiveBorderColor = colorTable(context)[inactiveBorderColorKnob ?? 40]; - final hoverBorderColorKnob = context.knobs.nullable.options( - label: "hoverBorderColor", - description: "MoonColors variants for MoonTextInput border on hover.", + final hoverColorKnob = context.knobs.nullable.options( + label: "hoverColor", + description: "MoonColors variants for MoonInput on hover.", enabled: false, initial: 0, // piccolo options: colorOptions, ); - final hoverBorderColor = colorTable(context)[hoverBorderColorKnob ?? 40]; + final hoverColor = colorTable(context)[hoverColorKnob ?? 40]; final backgroundColorKnob = context.knobs.nullable.options( label: "backgroundColor", description: - "MoonColors variants for MoonTextInput and MoonDropdown background.", + "MoonColors variants for MoonInput and MoonDropdown background.", enabled: false, initial: 0, // piccolo @@ -143,7 +143,7 @@ class _ComboboxSingleSelectStoryState extends State { final borderRadiusKnob = context.knobs.nullable.sliderInt( label: "borderRadius", - description: "Border radius for MoonTextInput and MoonDropdown", + description: "Border radius for MoonInput and MoonDropdown", enabled: false, initial: 8, max: 32, @@ -165,13 +165,13 @@ class _ComboboxSingleSelectStoryState extends State { final enabledKnob = context.knobs.boolean( label: "enabled", - description: "Switch between MoonTextInput enabled and disabled states.", + description: "Switch between MoonInput enabled and disabled states.", initial: true, ); final hasFloatingLabelKnob = context.knobs.boolean( label: "hasFloatingLabel", - description: "Whether MoonTextInput has floating label.", + description: "Whether MoonInput has floating label.", ); final BorderRadiusGeometry? borderRadius = borderRadiusKnob != null @@ -219,7 +219,7 @@ class _ComboboxSingleSelectStoryState extends State { ), ), ), - child: MoonTextInput( + child: MoonInput( enabled: enabledKnob, hasFloatingLabel: hasFloatingLabelKnob, width: 270, @@ -227,9 +227,9 @@ class _ComboboxSingleSelectStoryState extends State { activeBorderColor: activeBorderColor, inactiveBorderColor: inactiveBorderColor, backgroundColor: backgroundColor, - hoverBorderColor: hoverBorderColor, + hoverColor: hoverColor, borderRadius: borderRadius, - textInputSize: textInputSizeKnob, + inputSize: inputSizeKnob, hintText: "Select single component", controller: _searchController, onTap: () => _performSearch(), diff --git a/example/lib/src/storybook/stories/composites/search_with_dropdown.dart b/example/lib/src/storybook/stories/composites/search_with_dropdown.dart index 5473b39e..bbd8d055 100644 --- a/example/lib/src/storybook/stories/composites/search_with_dropdown.dart +++ b/example/lib/src/storybook/stories/composites/search_with_dropdown.dart @@ -77,7 +77,7 @@ class _SearchWithDropdownStoryState extends State { Widget build(BuildContext context) { final activeBorderColorKnob = context.knobs.nullable.options( label: "activeBorderColor", - description: "MoonColors variants for MoonTextInput active border.", + description: "MoonColors variants for MoonInput active border.", enabled: false, initial: 0, // piccolo @@ -88,7 +88,7 @@ class _SearchWithDropdownStoryState extends State { final inactiveBorderColorKnob = context.knobs.nullable.options( label: "inactiveBorderColor", - description: "MoonColors variants for MoonTextInput inactive border.", + description: "MoonColors variants for MoonInput inactive border.", enabled: false, initial: 0, // piccolo @@ -98,20 +98,20 @@ class _SearchWithDropdownStoryState extends State { final inactiveBorderColor = colorTable(context)[inactiveBorderColorKnob ?? 40]; - final hoverBorderColorKnob = context.knobs.nullable.options( - label: "hoverBorderColor", - description: "MoonColors variants for MoonTextInput border on hover.", + final hoverColorKnob = context.knobs.nullable.options( + label: "hoverColor", + description: "MoonColors variants for MoonInput on hover.", enabled: false, initial: 0, // piccolo options: colorOptions, ); - final hoverBorderColor = colorTable(context)[hoverBorderColorKnob ?? 40]; + final hoverColor = colorTable(context)[hoverColorKnob ?? 40]; final backgroundColorKnob = context.knobs.nullable.options( label: "backgroundColor", - description: "MoonColors variants for MoonTextInput background.", + description: "MoonColors variants for MoonInput background.", enabled: false, initial: 0, // piccolo @@ -122,7 +122,7 @@ class _SearchWithDropdownStoryState extends State { final borderRadiusKnob = context.knobs.nullable.sliderInt( label: "borderRadius", - description: "Border radius for MoonTextInput and MoonDropdown", + description: "Border radius for MoonInput and MoonDropdown", enabled: false, initial: 8, max: 32, @@ -144,13 +144,13 @@ class _SearchWithDropdownStoryState extends State { final enabledKnob = context.knobs.boolean( label: "enabled", - description: "Switch between MoonTextInput enabled and disabled states.", + description: "Switch between MoonInput enabled and disabled states.", initial: true, ); final hasFloatingLabelKnob = context.knobs.boolean( label: "hasFloatingLabel", - description: "Whether MoonTextInput has floating label.", + description: "Whether MoonInput has floating label.", ); BorderRadiusGeometry? getBorderRadius(_Section variant) { @@ -245,7 +245,7 @@ class _SearchWithDropdownStoryState extends State { ); }, ), - child: MoonTextInput( + child: MoonInput( enabled: enabledKnob, width: constraints.maxWidth, hasFloatingLabel: hasFloatingLabelKnob, @@ -253,7 +253,7 @@ class _SearchWithDropdownStoryState extends State { activeBorderColor ?? context.moonColors!.beerus, inactiveBorderColor: inactiveBorderColor, backgroundColor: backgroundColor, - hoverBorderColor: hoverBorderColor, + hoverColor: hoverColor, borderRadius: getBorderRadius(_Section.input), hintText: "Search components", controller: _searchController, diff --git a/example/lib/src/storybook/stories/composites/search_with_list.dart b/example/lib/src/storybook/stories/composites/search_with_list.dart index 5ab2731e..6e26aaed 100644 --- a/example/lib/src/storybook/stories/composites/search_with_list.dart +++ b/example/lib/src/storybook/stories/composites/search_with_list.dart @@ -60,22 +60,22 @@ class _SearchWithListStoryState extends State { @override Widget build(BuildContext context) { - final textInputSizeKnob = context.knobs.nullable.options( - label: "textInputSize", - description: "Size variants for MoonTextInput.", + final inputSizeKnob = context.knobs.nullable.options( + label: "inputSize", + description: "Size variants for MoonInput.", enabled: false, - initial: MoonTextInputSize.md, + initial: MoonInputSize.md, options: const [ - Option(label: "sm", value: MoonTextInputSize.sm), - Option(label: "md", value: MoonTextInputSize.md), - Option(label: "lg", value: MoonTextInputSize.lg), - Option(label: "xl", value: MoonTextInputSize.xl), + Option(label: "sm", value: MoonInputSize.sm), + Option(label: "md", value: MoonInputSize.md), + Option(label: "lg", value: MoonInputSize.lg), + Option(label: "xl", value: MoonInputSize.xl), ], ); final activeBorderColorKnob = context.knobs.nullable.options( label: "activeBorderColor", - description: "MoonColors variants for MoonTextInput active border.", + description: "MoonColors variants for MoonInput active border.", enabled: false, initial: 0, // piccolo @@ -86,7 +86,7 @@ class _SearchWithListStoryState extends State { final inactiveBorderColorKnob = context.knobs.nullable.options( label: "inactiveBorderColor", - description: "MoonColors variants for MoonTextInput inactive border.", + description: "MoonColors variants for MoonInput inactive border.", enabled: false, initial: 0, // piccolo @@ -96,20 +96,20 @@ class _SearchWithListStoryState extends State { final inactiveBorderColor = colorTable(context)[inactiveBorderColorKnob ?? 40]; - final hoverBorderColorKnob = context.knobs.nullable.options( - label: "hoverBorderColor", - description: "MoonColors variants for MoonTextInput border on hover.", + final hoverColorKnob = context.knobs.nullable.options( + label: "hoverColor", + description: "MoonColors variants for MoonInput on hover.", enabled: false, initial: 0, // piccolo options: colorOptions, ); - final hoverBorderColor = colorTable(context)[hoverBorderColorKnob ?? 40]; + final hoverColor = colorTable(context)[hoverColorKnob ?? 40]; final backgroundColorKnob = context.knobs.nullable.options( label: "backgroundColor", - description: "MoonColors variants for MoonTextInput background.", + description: "MoonColors variants for MoonInput background.", enabled: false, initial: 0, // piccolo @@ -120,7 +120,7 @@ class _SearchWithListStoryState extends State { final borderRadiusKnob = context.knobs.nullable.sliderInt( label: "borderRadius", - description: "Border radius for MoonTextInput", + description: "Border radius for MoonInput", enabled: false, initial: 8, max: 32, @@ -128,13 +128,13 @@ class _SearchWithListStoryState extends State { final enabledKnob = context.knobs.boolean( label: "enabled", - description: "Switch between MoonTextInput enabled and disabled states.", + description: "Switch between MoonInput enabled and disabled states.", initial: true, ); final hasFloatingLabelKnob = context.knobs.boolean( label: "hasFloatingLabel", - description: "Whether MoonTextInput has floating label.", + description: "Whether MoonInput has floating label.", ); return Padding( @@ -143,18 +143,18 @@ class _SearchWithListStoryState extends State { children: [ Column( children: [ - MoonTextInput( + MoonInput( enabled: enabledKnob, hasFloatingLabel: hasFloatingLabelKnob, activeBorderColor: activeBorderColor ?? context.moonColors!.beerus, inactiveBorderColor: inactiveBorderColor, backgroundColor: backgroundColor, - hoverBorderColor: hoverBorderColor, + hoverColor: hoverColor, borderRadius: borderRadiusKnob != null ? BorderRadius.circular(borderRadiusKnob.toDouble()) : null, - textInputSize: textInputSizeKnob, + inputSize: inputSizeKnob, hintText: "Search components", controller: _searchController, onTap: () => _performSearch(), diff --git a/example/lib/src/storybook/stories/primitives/dropdown.dart b/example/lib/src/storybook/stories/primitives/dropdown.dart index 6e7f8015..de753aa6 100644 --- a/example/lib/src/storybook/stories/primitives/dropdown.dart +++ b/example/lib/src/storybook/stories/primitives/dropdown.dart @@ -181,7 +181,7 @@ class _DropdownStoryState extends State { ), ), ), - child: MoonTextInput( + child: MoonInput( width: 270, readOnly: true, canRequestFocus: false, diff --git a/example/lib/src/storybook/stories/primitives/icons.dart b/example/lib/src/storybook/stories/primitives/icons.dart index 1ac90e17..16a66717 100644 --- a/example/lib/src/storybook/stories/primitives/icons.dart +++ b/example/lib/src/storybook/stories/primitives/icons.dart @@ -149,7 +149,7 @@ class _IconsStoryState extends State { backgroundColor: context.moonColors?.goku, title: Theme( data: Theme.of(context), - child: MoonTextInput( + child: MoonInput( controller: _searchController, hintText: "Search icons", leading: const Icon( diff --git a/example/lib/src/storybook/stories/primitives/text_input.dart b/example/lib/src/storybook/stories/primitives/input.dart similarity index 73% rename from example/lib/src/storybook/stories/primitives/text_input.dart rename to example/lib/src/storybook/stories/primitives/input.dart index 1c604fd4..8449c5ef 100644 --- a/example/lib/src/storybook/stories/primitives/text_input.dart +++ b/example/lib/src/storybook/stories/primitives/input.dart @@ -3,16 +3,16 @@ import 'package:flutter/material.dart'; import 'package:moon_design/moon_design.dart'; import 'package:storybook_flutter/storybook_flutter.dart'; -class TextInputStory extends StatefulWidget { - static const path = '/primitives/text_input'; +class InputStory extends StatefulWidget { + static const path = '/primitives/input'; - const TextInputStory({super.key}); + const InputStory({super.key}); @override - State createState() => _TextInputStoryState(); + State createState() => _InputStoryState(); } -class _TextInputStoryState extends State { +class _InputStoryState extends State { final TextEditingController _textController = TextEditingController(); final TextEditingController _dateController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); @@ -21,22 +21,22 @@ class _TextInputStoryState extends State { @override Widget build(BuildContext context) { - final textInputSizeKnob = context.knobs.nullable.options( - label: "textInputSize", - description: "Size variants for MoonTextInput.", + final inputSizeKnob = context.knobs.nullable.options( + label: "inputSize", + description: "Size variants for MoonInput.", enabled: false, - initial: MoonTextInputSize.md, + initial: MoonInputSize.md, options: const [ - Option(label: "sm", value: MoonTextInputSize.sm), - Option(label: "md", value: MoonTextInputSize.md), - Option(label: "lg", value: MoonTextInputSize.lg), - Option(label: "xl", value: MoonTextInputSize.xl), + Option(label: "sm", value: MoonInputSize.sm), + Option(label: "md", value: MoonInputSize.md), + Option(label: "lg", value: MoonInputSize.lg), + Option(label: "xl", value: MoonInputSize.xl), ], ); final textColorKnob = context.knobs.nullable.options( label: "textColor", - description: "MoonColors variants for MoonTextInput text.", + description: "MoonColors variants for MoonInput text.", enabled: false, initial: 0, // piccolo @@ -47,7 +47,7 @@ class _TextInputStoryState extends State { final hintTextColorKnob = context.knobs.nullable.options( label: "hintTextColor", - description: "MoonColors variants for MoonTextInput hint text.", + description: "MoonColors variants for MoonInput hint text.", enabled: false, initial: 0, // piccolo @@ -56,9 +56,20 @@ class _TextInputStoryState extends State { final hintTextColor = colorTable(context)[hintTextColorKnob ?? 40]; + final helperTextColorKnob = context.knobs.nullable.options( + label: "Helper text color", + description: "MoonColors variants for MoonInput helper text.", + enabled: false, + initial: 0, + // piccolo + options: colorOptions, + ); + + final helperTextColor = colorTable(context)[helperTextColorKnob ?? 40]; + final backgroundColorKnob = context.knobs.nullable.options( label: "backgroundColor", - description: "MoonColors variants for MoonTextInput background.", + description: "MoonColors variants for MoonInput background.", enabled: false, initial: 0, // piccolo @@ -69,7 +80,7 @@ class _TextInputStoryState extends State { final activeBorderColorKnob = context.knobs.nullable.options( label: "activeBorderColor", - description: "MoonColors variants for MoonTextInput active border.", + description: "MoonColors variants for MoonInput active border.", enabled: false, initial: 0, // piccolo @@ -80,7 +91,7 @@ class _TextInputStoryState extends State { final inactiveBorderColorKnob = context.knobs.nullable.options( label: "inactiveBorderColor", - description: "MoonColors variants for MoonTextInput inactive border.", + description: "MoonColors variants for MoonInput inactive border.", enabled: false, initial: 0, // piccolo @@ -90,20 +101,20 @@ class _TextInputStoryState extends State { final inactiveBorderColor = colorTable(context)[inactiveBorderColorKnob ?? 40]; - final hoverBorderColorKnob = context.knobs.nullable.options( - label: "hoverBorderColor", - description: "MoonColors variants for MoonTextInput border on hover.", + final hoverColorKnob = context.knobs.nullable.options( + label: "hoverColor", + description: "MoonColors variants for MoonInput on hover.", enabled: false, initial: 0, // piccolo options: colorOptions, ); - final hoverBorderColor = colorTable(context)[hoverBorderColorKnob ?? 40]; + final hoverColor = colorTable(context)[hoverColorKnob ?? 40]; final errorColorKnob = context.knobs.nullable.options( label: "errorColor", - description: "MoonColors variants for MoonTextInput in error state.", + description: "MoonColors variants for MoonInput in error state.", enabled: false, initial: 0, // piccolo @@ -114,7 +125,7 @@ class _TextInputStoryState extends State { final borderRadiusKnob = context.knobs.nullable.sliderInt( label: "borderRadius", - description: "Border radius for MoonTextInput.", + description: "Border radius for MoonInput.", enabled: false, initial: 8, max: 32, @@ -128,24 +139,36 @@ class _TextInputStoryState extends State { final hasFloatingLabelKnob = context.knobs.boolean( label: "hasFloatingLabel", - description: "Whether MoonTextInput has floating label.", + description: "Whether MoonInput has floating label.", ); final showLeadingKnob = context.knobs.boolean( label: "leading", - description: "Show widget in MoonTextInput leading slot.", + description: "Show widget in MoonInput leading slot.", initial: true, ); final showTrailingKnob = context.knobs.boolean( label: "trailing", - description: "Show widget in MoonTextInput trailing slot.", + description: "Show widget in MoonInput trailing slot.", + initial: true, + ); + + final showLabelKnob = context.knobs.boolean( + label: "label", + description: "Show label for MoonInput.", + initial: true, + ); + + final showHintKnob = context.knobs.boolean( + label: "hintText", + description: "Show hint text for MoonInput.", initial: true, ); final showHelperKnob = context.knobs.boolean( - label: "helper", - description: "Show widget in MoonTextInput helper slot.", + label: "helperText", + description: "Show helper text for MoonInput.", ); final BorderRadiusGeometry? borderRadius = borderRadiusKnob != null @@ -161,26 +184,30 @@ class _TextInputStoryState extends State { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - MoonFormTextInput( + MoonInput.form( controller: _textController, enabled: enabledKnob, - textInputSize: textInputSizeKnob, + inputSize: inputSizeKnob, hasFloatingLabel: hasFloatingLabelKnob, textColor: textColor, hintTextColor: hintTextColor, + helperErrorTextStyle: TextStyle(color: helperTextColor), backgroundColor: backgroundColor, activeBorderColor: activeBorderColor, inactiveBorderColor: inactiveBorderColor, - hoverBorderColor: hoverBorderColor, + hoverColor: hoverColor, errorColor: errorColor, borderRadius: borderRadius, - hintText: "Enter text (over 10 characters)", validator: (String? value) => value != null && value.length < 10 ? "The text should be longer than 10 characters." : null, onTapOutside: (PointerDownEvent _) => FocusManager.instance.primaryFocus?.unfocus(), + label: showLabelKnob ? const Text("Label") : null, + hintText: + showHintKnob ? "Enter text (over 10 characters)" : null, + helperText: showHelperKnob ? 'Supporting Text' : null, leading: showLeadingKnob ? const Icon( MoonIcons.generic_search_24_light, @@ -199,32 +226,34 @@ class _TextInputStoryState extends State { ), ) : null, - helper: - showHelperKnob ? const Text("Supporting text") : null, ), const SizedBox(height: 16), StatefulBuilder( - builder: (context, setState) { - return MoonFormTextInput( + builder: (BuildContext context, StateSetter setState) { + return MoonInput.form( controller: _passwordController, enabled: enabledKnob, keyboardType: TextInputType.visiblePassword, obscureText: _hidePassword, - textInputSize: textInputSizeKnob, + inputSize: inputSizeKnob, hasFloatingLabel: hasFloatingLabelKnob, textColor: textColor, hintTextColor: hintTextColor, + helperErrorTextStyle: TextStyle(color: helperTextColor), backgroundColor: backgroundColor, activeBorderColor: activeBorderColor, inactiveBorderColor: inactiveBorderColor, - hoverBorderColor: hoverBorderColor, + hoverColor: hoverColor, errorColor: errorColor, borderRadius: borderRadius, - hintText: "Enter password (123abc)", validator: (String? value) => value != "123abc" ? "Wrong password." : null, onTapOutside: (PointerDownEvent _) => FocusManager.instance.primaryFocus?.unfocus(), + label: showLabelKnob ? const Text("Label") : null, + hintText: + showHintKnob ? "Enter password (123abc)" : null, + helperText: showHelperKnob ? "Supporting text" : null, leading: showLeadingKnob ? const Icon( MoonIcons.security_password_24_light, @@ -255,27 +284,24 @@ class _TextInputStoryState extends State { ), ) : null, - helper: showHelperKnob - ? const Text("Supporting text") - : null, ); }, ), const SizedBox(height: 16), - MoonFormTextInput( + MoonInput.form( readOnly: true, controller: _dateController, enabled: enabledKnob, - textInputSize: textInputSizeKnob, + inputSize: inputSizeKnob, textColor: textColor, hintTextColor: hintTextColor, + helperErrorTextStyle: TextStyle(color: helperTextColor), backgroundColor: backgroundColor, activeBorderColor: activeBorderColor, inactiveBorderColor: inactiveBorderColor, - hoverBorderColor: hoverBorderColor, + hoverColor: hoverColor, errorColor: errorColor, borderRadius: borderRadius, - hintText: "Pick a date", validator: (String? value) => value != null && value.isEmpty ? "Pick a date." : null, onTap: () async { @@ -291,6 +317,8 @@ class _TextInputStoryState extends State { "${pickedDate.toLocal()}".split(" ")[0]; } }, + hintText: showHintKnob ? "Pick a date" : null, + helperText: showHelperKnob ? "Supporting text" : null, leading: showLeadingKnob ? const Icon( MoonIcons.time_calendar_24_light, @@ -309,8 +337,6 @@ class _TextInputStoryState extends State { ), ) : null, - helper: - showHelperKnob ? const Text("Supporting text") : null, ), const SizedBox(height: 32), MoonFilledButton( diff --git a/example/lib/src/storybook/stories/primitives/text_input_group.dart b/example/lib/src/storybook/stories/primitives/input_group.dart similarity index 65% rename from example/lib/src/storybook/stories/primitives/text_input_group.dart rename to example/lib/src/storybook/stories/primitives/input_group.dart index c8bdd7f8..d6cfaffd 100644 --- a/example/lib/src/storybook/stories/primitives/text_input_group.dart +++ b/example/lib/src/storybook/stories/primitives/input_group.dart @@ -3,16 +3,16 @@ import 'package:flutter/material.dart'; import 'package:moon_design/moon_design.dart'; import 'package:storybook_flutter/storybook_flutter.dart'; -class TextInputGroupStory extends StatefulWidget { - static const path = '/primitives/text_input_group'; +class InputGroupStory extends StatefulWidget { + static const path = '/primitives/input_group'; - const TextInputGroupStory({super.key}); + const InputGroupStory({super.key}); @override - State createState() => _TextInputGroupStoryState(); + State createState() => _InputGroupStoryState(); } -class _TextInputGroupStoryState extends State { +class _InputGroupStoryState extends State { final TextEditingController _textController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); @@ -22,7 +22,7 @@ class _TextInputGroupStoryState extends State { Widget build(BuildContext context) { final textColorKnob = context.knobs.nullable.options( label: "textColor", - description: "MoonColors variants for MoonTextInputGroup text.", + description: "MoonColors variants for MoonInputGroup text.", enabled: false, initial: 0, // piccolo @@ -33,7 +33,7 @@ class _TextInputGroupStoryState extends State { final hintTextColorKnob = context.knobs.nullable.options( label: "hintTextColor", - description: "MoonColors variants for MoonTextInputGroup hint text.", + description: "MoonColors variants for MoonInputGroup hint text.", enabled: false, initial: 0, // piccolo @@ -42,9 +42,31 @@ class _TextInputGroupStoryState extends State { final hintTextColor = colorTable(context)[hintTextColorKnob ?? 40]; + final labelColorKnob = context.knobs.nullable.options( + label: "Label text color", + description: "MoonColors variants for MoonInputGroup label text.", + enabled: false, + initial: 0, + // piccolo + options: colorOptions, + ); + + final labelColor = colorTable(context)[labelColorKnob ?? 40]; + + final helperTextColorKnob = context.knobs.nullable.options( + label: "Helper text color", + description: "MoonColors variants for MoonInputGroup helper text.", + enabled: false, + initial: 0, + // piccolo + options: colorOptions, + ); + + final helperTextColor = colorTable(context)[helperTextColorKnob ?? 40]; + final backgroundColorKnob = context.knobs.nullable.options( label: "backgroundColor", - description: "MoonColors variants for MoonTextInputGroup background.", + description: "MoonColors variants for MoonInputGroup background.", enabled: false, initial: 0, // piccolo @@ -55,7 +77,7 @@ class _TextInputGroupStoryState extends State { final borderColorKnob = context.knobs.nullable.options( label: "borderColor", - description: "MoonColors variants for MoonTextInputGroup border.", + description: "MoonColors variants for MoonInputGroup border.", enabled: false, initial: 0, // piccolo @@ -64,34 +86,33 @@ class _TextInputGroupStoryState extends State { final borderColor = colorTable(context)[borderColorKnob ?? 40]; - final activeChildrenBorderColorKnob = context.knobs.nullable.options( - label: "activeBorderColor", - description: - "MoonColors variants for MoonTextInputGroup children active border.", + final hoverColorKnob = context.knobs.nullable.options( + label: "hoverColor", + description: "MoonColors variants for MoonInputGroup children on hover.", enabled: false, initial: 0, // piccolo options: colorOptions, ); - final activeBorderColor = - colorTable(context)[activeChildrenBorderColorKnob ?? 40]; + final hoverColor = colorTable(context)[hoverColorKnob ?? 40]; - final errorBorderColorKnob = context.knobs.nullable.options( - label: "errorBorderColor", + final activeChildrenBorderColorKnob = context.knobs.nullable.options( + label: "activeBorderColor", description: - "MoonColors variants for MoonTextInputGroup error state border.", + "MoonColors variants for MoonInputGroup children active border.", enabled: false, initial: 0, // piccolo options: colorOptions, ); - final errorBorderColor = colorTable(context)[errorBorderColorKnob ?? 40]; + final activeBorderColor = + colorTable(context)[activeChildrenBorderColorKnob ?? 40]; final errorColorKnob = context.knobs.nullable.options( label: "errorColor", - description: "MoonColors variants for MoonTextInputGroup in error state.", + description: "MoonColors variants for MoonInputGroup in error state.", enabled: false, initial: 0, // piccolo @@ -102,24 +123,24 @@ class _TextInputGroupStoryState extends State { final orientationKnob = context.knobs.nullable.options( label: "orientation", - description: "MoonTextInputGroup orientation.", + description: "MoonInputGroup orientation.", enabled: false, - initial: MoonTextInputGroupOrientation.vertical, + initial: MoonInputGroupOrientation.vertical, options: [ const Option( label: "vertical", - value: MoonTextInputGroupOrientation.vertical, + value: MoonInputGroupOrientation.vertical, ), const Option( label: "horizontal", - value: MoonTextInputGroupOrientation.horizontal, + value: MoonInputGroupOrientation.horizontal, ), ], ); final borderRadiusKnob = context.knobs.nullable.sliderInt( label: "borderRadius", - description: "Border radius for MoonTextInputGroup.", + description: "Border radius for MoonInputGroup.", enabled: false, initial: 8, max: 32, @@ -131,9 +152,21 @@ class _TextInputGroupStoryState extends State { initial: true, ); - final showHelperKnob = context.knobs.boolean( - label: "helper", - description: "Show widget in MoonTextInputGroup helper slot.", + final showLabelKnob = context.knobs.boolean( + label: "label", + description: "Show label for MoonInputGroup.", + initial: true, + ); + + final showHintKnob = context.knobs.boolean( + label: "hintText", + description: "Show hint text for MoonInputGroup children.", + initial: true, + ); + + final showHelperTextKnob = context.knobs.boolean( + label: "helperText", + description: "Show helper text for MoonInputGroup.", ); final BorderRadiusGeometry? borderRadius = borderRadiusKnob != null @@ -149,29 +182,33 @@ class _TextInputGroupStoryState extends State { return Column( mainAxisSize: MainAxisSize.min, children: [ - MoonTextInputGroup( + MoonInputGroup( enabled: enabledKnob, backgroundColor: backgroundColor, borderColor: borderColor, + hoverColor: hoverColor, errorColor: errorColor, + hintColor: hintTextColor, + label: showLabelKnob + ? Text( + "Input Group", + style: TextStyle(color: labelColor ?? Colors.black), + ) + : null, + helperErrorTextStyle: TextStyle(color: helperTextColor), borderRadius: borderRadius, - orientation: orientationKnob ?? - MoonTextInputGroupOrientation.vertical, - helper: - showHelperKnob ? const Text("Supporting text") : null, + orientation: + orientationKnob ?? MoonInputGroupOrientation.vertical, + helperText: showHelperTextKnob ? "Supporting text" : null, children: [ - MoonFormTextInput( - textInputSize: MoonTextInputSize.xl, + MoonInput.form( + inputSize: MoonInputSize.xl, controller: _textController, - enabled: enabledKnob, - hasFloatingLabel: true, textColor: textColor, - hintTextColor: hintTextColor, activeBorderColor: activeBorderColor, - errorBorderColor: errorBorderColor, - errorColor: errorColor, - borderRadius: borderRadius, - hintText: "Enter text (over 10 characters)", + hintText: showHintKnob + ? "Enter text (over 10 characters)" + : null, validator: (String? value) => value?.length != null && value!.length < 10 ? "The text should be longer than 10 characters." @@ -193,19 +230,15 @@ class _TextInputGroupStoryState extends State { ), ), ), - MoonFormTextInput( - textInputSize: MoonTextInputSize.xl, + MoonInput.form( + inputSize: MoonInputSize.xl, controller: _passwordController, - enabled: enabledKnob, keyboardType: TextInputType.visiblePassword, obscureText: _hidePassword, - hasFloatingLabel: true, textColor: textColor, - hintTextColor: hintTextColor, activeBorderColor: activeBorderColor, - errorColor: errorColor, - borderRadius: borderRadius, - hintText: "Enter password (123abc)", + hintText: + showHintKnob ? "Enter password (123abc)" : null, validator: (String? value) => value != "123abc" ? "Wrong password." : null, onTapOutside: (PointerDownEvent _) => @@ -240,7 +273,8 @@ class _TextInputGroupStoryState extends State { const SizedBox(height: 32), MoonFilledButton( label: const Text("Submit"), - onTap: () => Form.of(context).validate(), + onTap: + enabledKnob ? () => Form.of(context).validate() : null, ), ], ); diff --git a/example/lib/src/storybook/stories/primitives/text_area.dart b/example/lib/src/storybook/stories/primitives/text_area.dart index dfbf2277..ac9c59f2 100644 --- a/example/lib/src/storybook/stories/primitives/text_area.dart +++ b/example/lib/src/storybook/stories/primitives/text_area.dart @@ -32,6 +32,17 @@ class TextAreaStory extends StatelessWidget { final hintTextColor = colorTable(context)[hintTextColorKnob ?? 40]; + final helperTextColorKnob = context.knobs.nullable.options( + label: "Helper text color", + description: "MoonColors variants for MoonTextArea helper text.", + enabled: false, + initial: 0, + // piccolo + options: colorOptions, + ); + + final helperTextColor = colorTable(context)[helperTextColorKnob ?? 40]; + final backgroundColorKnob = context.knobs.nullable.options( label: "backgroundColor", description: "MoonColors variants for MoonTextArea background.", @@ -96,9 +107,21 @@ class TextAreaStory extends StatelessWidget { description: "Whether MoonTextArea can expand to fit its contents.", ); - final showHelperKnob = context.knobs.boolean( - label: "helper", - description: "Show widget in MoonTextArea helper slot.", + final showLabelKnob = context.knobs.boolean( + label: "label", + description: "Show widget in MoonTextArea label slot.", + initial: true, + ); + + final showHintKnob = context.knobs.boolean( + label: "hintText", + description: "Show MoonTextArea hint text.", + initial: true, + ); + + final showHelperTextKnob = context.knobs.boolean( + label: "helperText", + description: "Show MoonTextArea helper text.", ); return Center( @@ -115,6 +138,7 @@ class TextAreaStory extends StatelessWidget { expands: expandsKnob, height: expandsKnob ? null : 200, textColor: textColor, + helperErrorTextStyle: TextStyle(color: helperTextColor), hintTextColor: hintTextColor, backgroundColor: backgroundColor, activeBorderColor: activeBorderColor, @@ -123,15 +147,15 @@ class TextAreaStory extends StatelessWidget { borderRadius: borderRadiusKnob != null ? BorderRadius.circular(borderRadiusKnob.toDouble()) : null, - hintText: "Enter your text here...", validator: (String? value) => value?.length != null && value!.length < 10 ? "The text should be longer than 10 characters." : null, onTapOutside: (PointerDownEvent _) => FocusManager.instance.primaryFocus?.unfocus(), - helper: - showHelperKnob ? const Text("Supporting text") : null, + hintText: showHintKnob ? "Enter your text here..." : null, + helperText: showHelperTextKnob ? "Supporting text" : null, + label: showLabelKnob ? const Text("Label") : null, ), const SizedBox(height: 32), MoonFilledButton( diff --git a/lib/moon_design.dart b/lib/moon_design.dart index ed1e7640..11d89d37 100644 --- a/lib/moon_design.dart +++ b/lib/moon_design.dart @@ -1,5 +1,5 @@ /// Moon Design for Flutter -library moon_design; +library; export 'package:hugeicons/hugeicons.dart'; @@ -15,6 +15,8 @@ export 'package:moon_design/src/theme/chip/chip_theme.dart'; export 'package:moon_design/src/theme/dot_indicator/dot_indicator_theme.dart'; export 'package:moon_design/src/theme/drawer/drawer_theme.dart'; export 'package:moon_design/src/theme/effects/effects_theme.dart'; +export 'package:moon_design/src/theme/input/input_theme.dart'; +export 'package:moon_design/src/theme/input_group/input_group_theme.dart'; export 'package:moon_design/src/theme/loaders/circular_loader/circular_loader_theme.dart'; export 'package:moon_design/src/theme/loaders/linear_loader/linear_loader_theme.dart'; export 'package:moon_design/src/theme/menu_item/menu_item_theme.dart'; @@ -27,8 +29,6 @@ export 'package:moon_design/src/theme/radio/radio_theme.dart'; export 'package:moon_design/src/theme/segmented_control/segmented_control_theme.dart'; export 'package:moon_design/src/theme/switch/switch_theme.dart'; export 'package:moon_design/src/theme/text_area/text_area_theme.dart'; -export 'package:moon_design/src/theme/text_input/text_input_theme.dart'; -export 'package:moon_design/src/theme/text_input_group/text_input_group_theme.dart'; export 'package:moon_design/src/theme/theme.dart'; export 'package:moon_design/src/theme/toast/toast_theme.dart'; export 'package:moon_design/src/theme/tokens/borders.dart'; @@ -66,12 +66,13 @@ export 'package:moon_design/src/widgets/common/base_control.dart'; export 'package:moon_design/src/widgets/common/base_segmented_tab_bar.dart'; export 'package:moon_design/src/widgets/common/effects/focus_effect.dart'; export 'package:moon_design/src/widgets/common/effects/pulse_effect.dart'; -export 'package:moon_design/src/widgets/common/error_message_widgets.dart'; export 'package:moon_design/src/widgets/common/progress_indicators/circular_progress_indicator.dart'; export 'package:moon_design/src/widgets/common/progress_indicators/linear_progress_indicator.dart'; export 'package:moon_design/src/widgets/dot_indicator/dot_indicator.dart'; export 'package:moon_design/src/widgets/drawer/drawer.dart'; export 'package:moon_design/src/widgets/dropdown/dropdown.dart'; +export 'package:moon_design/src/widgets/input/input.dart'; +export 'package:moon_design/src/widgets/input_group/input_group.dart'; export 'package:moon_design/src/widgets/loaders/circular_loader.dart'; export 'package:moon_design/src/widgets/loaders/linear_loader.dart'; export 'package:moon_design/src/widgets/menu_item/menu_item.dart'; @@ -94,9 +95,6 @@ export 'package:moon_design/src/widgets/tab_bar/tab_style.dart'; export 'package:moon_design/src/widgets/table/table.dart'; export 'package:moon_design/src/widgets/tag/tag.dart'; export 'package:moon_design/src/widgets/text_area/text_area.dart'; -export 'package:moon_design/src/widgets/text_input/form_text_input.dart'; -export 'package:moon_design/src/widgets/text_input/text_input.dart'; -export 'package:moon_design/src/widgets/text_input_group/text_input_group.dart'; export 'package:moon_design/src/widgets/toast/toast.dart'; export 'package:moon_design/src/widgets/tooltip/tooltip.dart'; diff --git a/lib/src/theme/text_input/text_input_colors.dart b/lib/src/theme/input/input_colors.dart similarity index 66% rename from lib/src/theme/text_input/text_input_colors.dart rename to lib/src/theme/input/input_colors.dart index 703a5e6d..b6ca5f05 100644 --- a/lib/src/theme/text_input/text_input_colors.dart +++ b/lib/src/theme/input/input_colors.dart @@ -4,68 +4,68 @@ import 'package:flutter/material.dart'; import 'package:moon_design/src/utils/color_premul_lerp.dart'; @immutable -class MoonTextInputColors extends ThemeExtension +class MoonInputColors extends ThemeExtension with DiagnosticableTreeMixin { - /// The background color of the MoonTextInput. + /// The background color of the MoonInput. final Color backgroundColor; - /// The border color of the active or focused MoonTextInput. + /// The border color of the active or focused MoonInput. final Color activeBorderColor; - /// The border color of the inactive MoonTextInput. + /// The border color of the inactive MoonInput. final Color inactiveBorderColor; - /// The color of the MoonTextInput in error state. + /// The color of the MoonInput in error state. final Color errorColor; - /// The border color of the MoonTextInput on hover. - final Color hoverBorderColor; + /// The hover effect color of the MoonInput. + final Color hoverColor; - /// The text color of the MoonTextInput. + /// The text color of the MoonInput. final Color textColor; - /// The text color of the MoonTextInput helper and errorBuilder widgets. + /// The text color of the MoonInput helper widget. final Color helperTextColor; - const MoonTextInputColors({ + const MoonInputColors({ required this.backgroundColor, required this.activeBorderColor, required this.inactiveBorderColor, required this.errorColor, - required this.hoverBorderColor, + required this.hoverColor, required this.textColor, required this.helperTextColor, }); @override - MoonTextInputColors copyWith({ + MoonInputColors copyWith({ Color? backgroundColor, Color? activeBorderColor, Color? inactiveBorderColor, Color? errorColor, - Color? hoverBorderColor, + Color? hoverColor, Color? textColor, Color? helperTextColor, }) { - return MoonTextInputColors( + return MoonInputColors( backgroundColor: backgroundColor ?? this.backgroundColor, activeBorderColor: activeBorderColor ?? this.activeBorderColor, inactiveBorderColor: inactiveBorderColor ?? this.inactiveBorderColor, errorColor: errorColor ?? this.errorColor, - hoverBorderColor: hoverBorderColor ?? this.hoverBorderColor, + hoverColor: hoverColor ?? this.hoverColor, textColor: textColor ?? this.textColor, helperTextColor: helperTextColor ?? this.helperTextColor, ); } @override - MoonTextInputColors lerp( - ThemeExtension? other, + MoonInputColors lerp( + ThemeExtension? other, double t, ) { - if (other is! MoonTextInputColors) return this; + if (other is! MoonInputColors) return this; - return MoonTextInputColors( + return MoonInputColors( backgroundColor: colorPremulLerp(backgroundColor, other.backgroundColor, t)!, activeBorderColor: @@ -73,8 +73,7 @@ class MoonTextInputColors extends ThemeExtension inactiveBorderColor: colorPremulLerp(inactiveBorderColor, other.inactiveBorderColor, t)!, errorColor: colorPremulLerp(errorColor, other.errorColor, t)!, - hoverBorderColor: - colorPremulLerp(hoverBorderColor, other.hoverBorderColor, t)!, + hoverColor: colorPremulLerp(hoverColor, other.hoverColor, t)!, textColor: colorPremulLerp(textColor, other.textColor, t)!, helperTextColor: colorPremulLerp(helperTextColor, other.helperTextColor, t)!, @@ -85,12 +84,12 @@ class MoonTextInputColors extends ThemeExtension void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty("type", "MoonTextInputColors")) + ..add(DiagnosticsProperty("type", "MoonInputColors")) ..add(ColorProperty("backgroundColor", backgroundColor)) ..add(ColorProperty("activeBorderColor", activeBorderColor)) ..add(ColorProperty("inactiveBorderColor", inactiveBorderColor)) ..add(ColorProperty("errorColor", errorColor)) - ..add(ColorProperty("hoverBorderColor", hoverBorderColor)) + ..add(ColorProperty("hoverColor", hoverColor)) ..add(ColorProperty("textColor", textColor)) ..add(ColorProperty("helperTextColor", helperTextColor)); } diff --git a/lib/src/theme/text_input/text_input_properties.dart b/lib/src/theme/input/input_properties.dart similarity index 70% rename from lib/src/theme/text_input/text_input_properties.dart rename to lib/src/theme/input/input_properties.dart index cb858455..dff17921 100644 --- a/lib/src/theme/text_input/text_input_properties.dart +++ b/lib/src/theme/input/input_properties.dart @@ -2,22 +2,21 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @immutable -class MoonTextInputProperties extends ThemeExtension +class MoonInputProperties extends ThemeExtension with DiagnosticableTreeMixin { - /// The duration of the MoonTextInput transition animation - /// (enable and disable). + /// The duration of the MoonInput transition animation (enable and disable). final Duration transitionDuration; - /// The curve of the MoonTextInput transition animation (enable and disable). + /// The curve of the MoonInput transition animation (enable and disable). final Curve transitionCurve; - /// The padding of the MoonTextInput helper and errorBuilder widgets. + /// The padding of the MoonInput helper and errorBuilder widgets. final EdgeInsetsGeometry helperPadding; - /// The text style of the MoonTextInput helper and errorBuilder widgets. + /// The text style of the MoonInput helper and errorBuilder widgets. final TextStyle helperTextStyle; - const MoonTextInputProperties({ + const MoonInputProperties({ required this.transitionDuration, required this.transitionCurve, required this.helperPadding, @@ -25,13 +24,13 @@ class MoonTextInputProperties extends ThemeExtension }); @override - MoonTextInputProperties copyWith({ + MoonInputProperties copyWith({ Duration? transitionDuration, Curve? transitionCurve, EdgeInsetsGeometry? helperPadding, TextStyle? helperTextStyle, }) { - return MoonTextInputProperties( + return MoonInputProperties( transitionDuration: transitionDuration ?? this.transitionDuration, transitionCurve: transitionCurve ?? this.transitionCurve, helperPadding: helperPadding ?? this.helperPadding, @@ -40,13 +39,13 @@ class MoonTextInputProperties extends ThemeExtension } @override - MoonTextInputProperties lerp( - ThemeExtension? other, + MoonInputProperties lerp( + ThemeExtension? other, double t, ) { - if (other is! MoonTextInputProperties) return this; + if (other is! MoonInputProperties) return this; - return MoonTextInputProperties( + return MoonInputProperties( transitionDuration: lerpDuration(transitionDuration, other.transitionDuration, t), transitionCurve: other.transitionCurve, @@ -62,7 +61,7 @@ class MoonTextInputProperties extends ThemeExtension super.debugFillProperties(properties); properties ..add( - DiagnosticsProperty("type", "MoonTextInputProperties"), + DiagnosticsProperty("type", "MoonInputProperties"), ) ..add( DiagnosticsProperty("transitionDuration", transitionDuration), diff --git a/lib/src/theme/text_input/text_input_size_properties.dart b/lib/src/theme/input/input_size_properties.dart similarity index 60% rename from lib/src/theme/text_input/text_input_size_properties.dart rename to lib/src/theme/input/input_size_properties.dart index 932c5dac..b4c2911e 100644 --- a/lib/src/theme/text_input/text_input_size_properties.dart +++ b/lib/src/theme/input/input_size_properties.dart @@ -4,29 +4,27 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @immutable -class MoonTextInputSizeProperties - extends ThemeExtension +class MoonInputSizeProperties extends ThemeExtension with DiagnosticableTreeMixin { - /// The border radius of the MoonTextInput. + /// The border radius of the MoonInput. final BorderRadiusGeometry borderRadius; - /// The height of the MoonTextInput. + /// The height of the MoonInput. final double height; - /// The gap between the leading, label and trailing widgets of the - /// MoonTextInput. + /// The gap between the leading, label and trailing widgets of the MoonInput. final double gap; - /// The size value of the MoonTextInput icon. + /// The size value of the MoonInput icon. final double iconSizeValue; - /// The padding of the MoonTextInput. + /// The padding of the MoonInput. final EdgeInsetsGeometry padding; - /// The text style of the MoonTextInput. + /// The text style of the MoonInput. final TextStyle textStyle; - const MoonTextInputSizeProperties({ + const MoonInputSizeProperties({ required this.borderRadius, required this.height, required this.gap, @@ -36,7 +34,7 @@ class MoonTextInputSizeProperties }); @override - MoonTextInputSizeProperties copyWith({ + MoonInputSizeProperties copyWith({ BorderRadiusGeometry? borderRadius, double? height, double? gap, @@ -44,7 +42,7 @@ class MoonTextInputSizeProperties EdgeInsetsGeometry? padding, TextStyle? textStyle, }) { - return MoonTextInputSizeProperties( + return MoonInputSizeProperties( borderRadius: borderRadius ?? this.borderRadius, height: height ?? this.height, gap: gap ?? this.gap, @@ -55,13 +53,13 @@ class MoonTextInputSizeProperties } @override - MoonTextInputSizeProperties lerp( - ThemeExtension? other, + MoonInputSizeProperties lerp( + ThemeExtension? other, double t, ) { - if (other is! MoonTextInputSizeProperties) return this; + if (other is! MoonInputSizeProperties) return this; - return MoonTextInputSizeProperties( + return MoonInputSizeProperties( borderRadius: BorderRadiusGeometry.lerp(borderRadius, other.borderRadius, t)!, height: lerpDouble(height, other.height, t)!, @@ -76,26 +74,14 @@ class MoonTextInputSizeProperties void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add( - DiagnosticsProperty("type", "MoonTextInputSizeProperties"), - ) + ..add(DiagnosticsProperty("type", "MoonInputSizeProperties")) + ..add(DoubleProperty("height", height)) + ..add(DoubleProperty("gap", gap)) + ..add(DoubleProperty("iconSizeValue", iconSizeValue)) + ..add(DiagnosticsProperty("padding", padding)) + ..add(DiagnosticsProperty("textStyle", textStyle)) ..add( DiagnosticsProperty("borderRadius", borderRadius), - ) - ..add( - DoubleProperty("height", height), - ) - ..add( - DoubleProperty("gap", gap), - ) - ..add( - DoubleProperty("iconSizeValue", iconSizeValue), - ) - ..add( - DiagnosticsProperty("padding", padding), - ) - ..add( - DiagnosticsProperty("textStyle", textStyle), ); } } diff --git a/lib/src/theme/text_input/text_input_sizes.dart b/lib/src/theme/input/input_sizes.dart similarity index 61% rename from lib/src/theme/text_input/text_input_sizes.dart rename to lib/src/theme/input/input_sizes.dart index 121bc125..c5a4bdbc 100644 --- a/lib/src/theme/text_input/text_input_sizes.dart +++ b/lib/src/theme/input/input_sizes.dart @@ -1,35 +1,35 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:moon_design/src/theme/text_input/text_input_size_properties.dart'; +import 'package:moon_design/src/theme/input/input_size_properties.dart'; import 'package:moon_design/src/theme/tokens/tokens.dart'; @immutable -class MoonTextInputSizes extends ThemeExtension +class MoonInputSizes extends ThemeExtension with DiagnosticableTreeMixin { /// The tokens of the Moon Design System. final MoonTokens tokens; - /// The properties of the small MoonTextInput. - final MoonTextInputSizeProperties sm; + /// The properties of the small MoonInput. + final MoonInputSizeProperties sm; - /// The properties of the medium MoonTextInput. - final MoonTextInputSizeProperties md; + /// The properties of the medium MoonInput. + final MoonInputSizeProperties md; - /// The properties of the large MoonTextInput. - final MoonTextInputSizeProperties lg; + /// The properties of the large MoonInput. + final MoonInputSizeProperties lg; - /// The properties of the extra large MoonTextInput. - final MoonTextInputSizeProperties xl; + /// The properties of the extra large MoonInput. + final MoonInputSizeProperties xl; - MoonTextInputSizes({ + MoonInputSizes({ required this.tokens, - MoonTextInputSizeProperties? sm, - MoonTextInputSizeProperties? md, - MoonTextInputSizeProperties? lg, - MoonTextInputSizeProperties? xl, + MoonInputSizeProperties? sm, + MoonInputSizeProperties? md, + MoonInputSizeProperties? lg, + MoonInputSizeProperties? xl, }) : sm = sm ?? - MoonTextInputSizeProperties( + MoonInputSizeProperties( borderRadius: tokens.borders.interactiveXs, height: tokens.sizes.sm, gap: tokens.sizes.x4s, @@ -41,7 +41,7 @@ class MoonTextInputSizes extends ThemeExtension textStyle: tokens.typography.body.textDefault, ), md = md ?? - MoonTextInputSizeProperties( + MoonInputSizeProperties( borderRadius: tokens.borders.interactiveSm, height: tokens.sizes.md, gap: tokens.sizes.x4s, @@ -53,7 +53,7 @@ class MoonTextInputSizes extends ThemeExtension textStyle: tokens.typography.body.textDefault, ), lg = lg ?? - MoonTextInputSizeProperties( + MoonInputSizeProperties( borderRadius: tokens.borders.interactiveSm, height: tokens.sizes.lg, gap: tokens.sizes.x4s, @@ -65,7 +65,7 @@ class MoonTextInputSizes extends ThemeExtension textStyle: tokens.typography.body.text16, ), xl = xl ?? - MoonTextInputSizeProperties( + MoonInputSizeProperties( borderRadius: tokens.borders.interactiveSm, height: tokens.sizes.xl, gap: tokens.sizes.x2s, @@ -78,14 +78,14 @@ class MoonTextInputSizes extends ThemeExtension ); @override - MoonTextInputSizes copyWith({ + MoonInputSizes copyWith({ MoonTokens? tokens, - MoonTextInputSizeProperties? sm, - MoonTextInputSizeProperties? md, - MoonTextInputSizeProperties? lg, - MoonTextInputSizeProperties? xl, + MoonInputSizeProperties? sm, + MoonInputSizeProperties? md, + MoonInputSizeProperties? lg, + MoonInputSizeProperties? xl, }) { - return MoonTextInputSizes( + return MoonInputSizes( tokens: tokens ?? this.tokens, sm: sm ?? this.sm, md: md ?? this.md, @@ -95,10 +95,10 @@ class MoonTextInputSizes extends ThemeExtension } @override - MoonTextInputSizes lerp(ThemeExtension? other, double t) { - if (other is! MoonTextInputSizes) return this; + MoonInputSizes lerp(ThemeExtension? other, double t) { + if (other is! MoonInputSizes) return this; - return MoonTextInputSizes( + return MoonInputSizes( tokens: tokens.lerp(other.tokens, t), sm: sm.lerp(other.sm, t), md: md.lerp(other.md, t), @@ -111,11 +111,11 @@ class MoonTextInputSizes extends ThemeExtension void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty("type", "MoonTextInputSizes")) + ..add(DiagnosticsProperty("type", "MoonInputSizes")) ..add(DiagnosticsProperty("tokens", tokens)) - ..add(DiagnosticsProperty("sm", sm)) - ..add(DiagnosticsProperty("md", md)) - ..add(DiagnosticsProperty("lg", lg)) - ..add(DiagnosticsProperty("xl", xl)); + ..add(DiagnosticsProperty("sm", sm)) + ..add(DiagnosticsProperty("md", md)) + ..add(DiagnosticsProperty("lg", lg)) + ..add(DiagnosticsProperty("xl", xl)); } } diff --git a/lib/src/theme/text_input/text_input_theme.dart b/lib/src/theme/input/input_theme.dart similarity index 52% rename from lib/src/theme/text_input/text_input_theme.dart rename to lib/src/theme/input/input_theme.dart index 2821d817..0ff97c27 100644 --- a/lib/src/theme/text_input/text_input_theme.dart +++ b/lib/src/theme/input/input_theme.dart @@ -1,43 +1,43 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:moon_design/src/theme/text_input/text_input_colors.dart'; -import 'package:moon_design/src/theme/text_input/text_input_properties.dart'; -import 'package:moon_design/src/theme/text_input/text_input_sizes.dart'; +import 'package:moon_design/src/theme/input/input_colors.dart'; +import 'package:moon_design/src/theme/input/input_properties.dart'; +import 'package:moon_design/src/theme/input/input_sizes.dart'; import 'package:moon_design/src/theme/tokens/tokens.dart'; @immutable -class MoonTextInputTheme extends ThemeExtension +class MoonInputTheme extends ThemeExtension with DiagnosticableTreeMixin { /// The tokens of the Moon Design System. final MoonTokens tokens; - /// The colors of the MoonTextInput. - final MoonTextInputColors colors; + /// The colors of the MoonInput. + final MoonInputColors colors; - /// The properties of the MoonTextInput. - final MoonTextInputProperties properties; + /// The properties of the MoonInput. + final MoonInputProperties properties; - /// The sizes of the MoonTextInput. - final MoonTextInputSizes sizes; + /// The sizes of the MoonInput. + final MoonInputSizes sizes; - MoonTextInputTheme({ + MoonInputTheme({ required this.tokens, - MoonTextInputColors? colors, - MoonTextInputProperties? properties, - MoonTextInputSizes? sizes, + MoonInputColors? colors, + MoonInputProperties? properties, + MoonInputSizes? sizes, }) : colors = colors ?? - MoonTextInputColors( + MoonInputColors( backgroundColor: tokens.colors.goku, activeBorderColor: tokens.colors.piccolo, inactiveBorderColor: tokens.colors.beerus, errorColor: tokens.colors.chichi, - hoverBorderColor: tokens.colors.beerus, + hoverColor: tokens.colors.beerus, textColor: tokens.colors.textPrimary, helperTextColor: tokens.colors.textSecondary, ), properties = properties ?? - MoonTextInputProperties( + MoonInputProperties( // The duration value extracted from: // https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/textfield/TextInputLayout.java transitionDuration: const Duration(milliseconds: 167), @@ -45,16 +45,16 @@ class MoonTextInputTheme extends ThemeExtension helperPadding: EdgeInsets.only(top: tokens.sizes.x4s), helperTextStyle: tokens.typography.body.text12, ), - sizes = sizes ?? MoonTextInputSizes(tokens: tokens); + sizes = sizes ?? MoonInputSizes(tokens: tokens); @override - MoonTextInputTheme copyWith({ + MoonInputTheme copyWith({ MoonTokens? tokens, - MoonTextInputColors? colors, - MoonTextInputProperties? properties, - MoonTextInputSizes? sizes, + MoonInputColors? colors, + MoonInputProperties? properties, + MoonInputSizes? sizes, }) { - return MoonTextInputTheme( + return MoonInputTheme( tokens: tokens ?? this.tokens, colors: colors ?? this.colors, properties: properties ?? this.properties, @@ -63,10 +63,10 @@ class MoonTextInputTheme extends ThemeExtension } @override - MoonTextInputTheme lerp(ThemeExtension? other, double t) { - if (other is! MoonTextInputTheme) return this; + MoonInputTheme lerp(ThemeExtension? other, double t) { + if (other is! MoonInputTheme) return this; - return MoonTextInputTheme( + return MoonInputTheme( tokens: tokens.lerp(other.tokens, t), colors: colors.lerp(other.colors, t), properties: properties.lerp(other.properties, t), @@ -78,20 +78,10 @@ class MoonTextInputTheme extends ThemeExtension void debugFillProperties(DiagnosticPropertiesBuilder diagnosticProperties) { super.debugFillProperties(diagnosticProperties); diagnosticProperties - ..add( - DiagnosticsProperty("type", "MoonTextInputTheme"), - ) - ..add( - DiagnosticsProperty("tokens", tokens), - ) - ..add( - DiagnosticsProperty("colors", colors), - ) - ..add( - DiagnosticsProperty("properties", properties), - ) - ..add( - DiagnosticsProperty("sizes", sizes), - ); + ..add(DiagnosticsProperty("type", "MoonInputTheme")) + ..add(DiagnosticsProperty("tokens", tokens)) + ..add(DiagnosticsProperty("colors", colors)) + ..add(DiagnosticsProperty("properties", properties)) + ..add(DiagnosticsProperty("sizes", sizes)); } } diff --git a/lib/src/theme/text_input_group/text_input_group_colors.dart b/lib/src/theme/input_group/input_group_colors.dart similarity index 59% rename from lib/src/theme/text_input_group/text_input_group_colors.dart rename to lib/src/theme/input_group/input_group_colors.dart index a2820cf5..b4a0f32c 100644 --- a/lib/src/theme/text_input_group/text_input_group_colors.dart +++ b/lib/src/theme/input_group/input_group_colors.dart @@ -4,64 +4,63 @@ import 'package:flutter/material.dart'; import 'package:moon_design/src/utils/color_premul_lerp.dart'; @immutable -class MoonTextInputGroupColors extends ThemeExtension +class MoonInputGroupColors extends ThemeExtension with DiagnosticableTreeMixin { - /// The background color of the MoonTextInputGroup. + /// The background color of the MoonInputGroup. final Color backgroundColor; - /// The color of the MoonTextInputGroup in error state. + /// The color of the MoonInputGroup in error state. final Color errorColor; - /// The text color of the MoonTextInputGroup helper and errorBuilder widgets. + /// The text color of the MoonInputGroup helper widget. final Color helperTextColor; - /// The border color of the MoonTextInputGroup. + /// The border color of the MoonInputGroup. final Color borderColor; - /// The border color of the MoonTextInputGroup on hover. - final Color hoverBorderColor; + /// The hover effect color of the MoonInputGroup children. + final Color hoverColor; - const MoonTextInputGroupColors({ + const MoonInputGroupColors({ required this.backgroundColor, required this.errorColor, required this.helperTextColor, required this.borderColor, - required this.hoverBorderColor, + required this.hoverColor, }); @override - MoonTextInputGroupColors copyWith({ + MoonInputGroupColors copyWith({ Color? backgroundColor, Color? errorColor, Color? helperTextColor, Color? borderColor, - Color? hoverBorderColor, + Color? hoverColor, }) { - return MoonTextInputGroupColors( + return MoonInputGroupColors( backgroundColor: backgroundColor ?? this.backgroundColor, errorColor: errorColor ?? this.errorColor, helperTextColor: helperTextColor ?? this.helperTextColor, borderColor: borderColor ?? this.borderColor, - hoverBorderColor: hoverBorderColor ?? this.hoverBorderColor, + hoverColor: hoverColor ?? this.hoverColor, ); } @override - MoonTextInputGroupColors lerp( - ThemeExtension? other, + MoonInputGroupColors lerp( + ThemeExtension? other, double t, ) { - if (other is! MoonTextInputGroupColors) return this; + if (other is! MoonInputGroupColors) return this; - return MoonTextInputGroupColors( + return MoonInputGroupColors( backgroundColor: colorPremulLerp(backgroundColor, other.backgroundColor, t)!, errorColor: colorPremulLerp(errorColor, other.errorColor, t)!, helperTextColor: colorPremulLerp(helperTextColor, other.helperTextColor, t)!, borderColor: colorPremulLerp(borderColor, other.borderColor, t)!, - hoverBorderColor: - colorPremulLerp(hoverBorderColor, other.hoverBorderColor, t)!, + hoverColor: colorPremulLerp(hoverColor, other.hoverColor, t)!, ); } @@ -69,11 +68,11 @@ class MoonTextInputGroupColors extends ThemeExtension void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty("type", "MoonTextInputGroupColors")) + ..add(DiagnosticsProperty("type", "MoonInputGroupColors")) ..add(ColorProperty("backgroundColor", backgroundColor)) ..add(ColorProperty("errorColor", errorColor)) ..add(ColorProperty("helperTextColor", helperTextColor)) ..add(ColorProperty("borderColor", borderColor)) - ..add(ColorProperty("hoverBorderColor", hoverBorderColor)); + ..add(ColorProperty("hoverColor", hoverColor)); } } diff --git a/lib/src/theme/text_input_group/text_input_group_properties.dart b/lib/src/theme/input_group/input_group_properties.dart similarity index 60% rename from lib/src/theme/text_input_group/text_input_group_properties.dart rename to lib/src/theme/input_group/input_group_properties.dart index a84f2762..e618aa29 100644 --- a/lib/src/theme/text_input_group/text_input_group_properties.dart +++ b/lib/src/theme/input_group/input_group_properties.dart @@ -2,33 +2,30 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @immutable -class MoonTextInputGroupProperties - extends ThemeExtension +class MoonInputGroupProperties extends ThemeExtension with DiagnosticableTreeMixin { - /// The border radius of the MoonTextInputGroup. + /// The border radius of the MoonInputGroup. final BorderRadiusGeometry borderRadius; - /// The duration of the MoonTextInputGroup transition animation - /// (enable and disable). + /// The duration of the MoonInputGroup transition animation (enable and disable). final Duration transitionDuration; - /// The curve of the MoonTextInputGroup transition animation - /// (enable and disable). + /// The curve of the MoonInputGroup transition animation (enable and disable). final Curve transitionCurve; - /// The padding of the MoonTextInputGroup helper and errorBuilder widgets. + /// The padding of the MoonInputGroup helper and errorBuilder widgets. final EdgeInsetsGeometry helperPadding; - /// The padding of the MoonTextInputGroup text. + /// The padding of the MoonInputGroup text. final EdgeInsetsGeometry textPadding; - /// The text style of the MoonTextInputGroup. + /// The text style of the MoonInputGroup. final TextStyle textStyle; - /// The text style of the MoonTextInputGroup helper and errorBuilder widgets. + /// The text style of the MoonInputGroup helper and errorBuilder widgets. final TextStyle helperTextStyle; - const MoonTextInputGroupProperties({ + const MoonInputGroupProperties({ required this.borderRadius, required this.transitionDuration, required this.transitionCurve, @@ -39,7 +36,7 @@ class MoonTextInputGroupProperties }); @override - MoonTextInputGroupProperties copyWith({ + MoonInputGroupProperties copyWith({ BorderRadiusGeometry? borderRadius, Duration? transitionDuration, Curve? transitionCurve, @@ -48,7 +45,7 @@ class MoonTextInputGroupProperties TextStyle? textStyle, TextStyle? helperTextStyle, }) { - return MoonTextInputGroupProperties( + return MoonInputGroupProperties( borderRadius: borderRadius ?? this.borderRadius, transitionDuration: transitionDuration ?? this.transitionDuration, transitionCurve: transitionCurve ?? this.transitionCurve, @@ -60,13 +57,13 @@ class MoonTextInputGroupProperties } @override - MoonTextInputGroupProperties lerp( - ThemeExtension? other, + MoonInputGroupProperties lerp( + ThemeExtension? other, double t, ) { - if (other is! MoonTextInputGroupProperties) return this; + if (other is! MoonInputGroupProperties) return this; - return MoonTextInputGroupProperties( + return MoonInputGroupProperties( borderRadius: BorderRadiusGeometry.lerp(borderRadius, other.borderRadius, t)!, transitionDuration: @@ -85,38 +82,19 @@ class MoonTextInputGroupProperties void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties + ..add(DiagnosticsProperty("type", "MoonInputGroupProperties")) + ..add(DiagnosticsProperty("transitionCurve", transitionCurve)) + ..add(DiagnosticsProperty("textPadding", textPadding)) + ..add(DiagnosticsProperty("textStyle", textStyle)) + ..add(DiagnosticsProperty("helperTextStyle", helperTextStyle)) ..add( - DiagnosticsProperty( - "type", - "MoonTextInputGroupProperties", - ), - ) - ..add( - DiagnosticsProperty( - "borderRadius", - borderRadius, - ), - ) - ..add( - DiagnosticsProperty( - "transitionDuration", - transitionDuration, - ), + DiagnosticsProperty("borderRadius", borderRadius), ) ..add( - DiagnosticsProperty( - "transitionCurve", - transitionCurve, - ), + DiagnosticsProperty("transitionDuration", transitionDuration), ) ..add( - DiagnosticsProperty( - "helperPadding", - helperPadding, - ), - ) - ..add(DiagnosticsProperty("textPadding", textPadding)) - ..add(DiagnosticsProperty("textStyle", textStyle)) - ..add(DiagnosticsProperty("helperTextStyle", helperTextStyle)); + DiagnosticsProperty("helperPadding", helperPadding), + ); } } diff --git a/lib/src/theme/text_input_group/text_input_group_theme.dart b/lib/src/theme/input_group/input_group_theme.dart similarity index 56% rename from lib/src/theme/text_input_group/text_input_group_theme.dart rename to lib/src/theme/input_group/input_group_theme.dart index ed46877c..e4b80d17 100644 --- a/lib/src/theme/text_input_group/text_input_group_theme.dart +++ b/lib/src/theme/input_group/input_group_theme.dart @@ -1,36 +1,36 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:moon_design/src/theme/text_input_group/text_input_group_colors.dart'; -import 'package:moon_design/src/theme/text_input_group/text_input_group_properties.dart'; +import 'package:moon_design/src/theme/input_group/input_group_colors.dart'; +import 'package:moon_design/src/theme/input_group/input_group_properties.dart'; import 'package:moon_design/src/theme/tokens/tokens.dart'; @immutable -class MoonTextInputGroupTheme extends ThemeExtension +class MoonInputGroupTheme extends ThemeExtension with DiagnosticableTreeMixin { /// The tokens of the Moon Design System. final MoonTokens tokens; - /// The colors of the MoonTextInputGroup. - final MoonTextInputGroupColors colors; + /// The colors of the MoonInputGroup. + final MoonInputGroupColors colors; - /// The properties of the MoonTextInputGroup. - final MoonTextInputGroupProperties properties; + /// The properties of the MoonInputGroup. + final MoonInputGroupProperties properties; - MoonTextInputGroupTheme({ + MoonInputGroupTheme({ required this.tokens, - MoonTextInputGroupColors? colors, - MoonTextInputGroupProperties? properties, + MoonInputGroupColors? colors, + MoonInputGroupProperties? properties, }) : colors = colors ?? - MoonTextInputGroupColors( + MoonInputGroupColors( backgroundColor: tokens.colors.goku, errorColor: tokens.colors.chichi, helperTextColor: tokens.colors.trunks, borderColor: tokens.colors.beerus, - hoverBorderColor: tokens.colors.beerus, + hoverColor: tokens.colors.beerus, ), properties = properties ?? - MoonTextInputGroupProperties( + MoonInputGroupProperties( borderRadius: tokens.borders.interactiveSm, transitionDuration: tokens.transitions.defaultTransitionDuration, transitionCurve: tokens.transitions.defaultTransitionCurve, @@ -41,12 +41,12 @@ class MoonTextInputGroupTheme extends ThemeExtension ); @override - MoonTextInputGroupTheme copyWith({ + MoonInputGroupTheme copyWith({ MoonTokens? tokens, - MoonTextInputGroupColors? colors, - MoonTextInputGroupProperties? properties, + MoonInputGroupColors? colors, + MoonInputGroupProperties? properties, }) { - return MoonTextInputGroupTheme( + return MoonInputGroupTheme( tokens: tokens ?? this.tokens, colors: colors ?? this.colors, properties: properties ?? this.properties, @@ -54,13 +54,13 @@ class MoonTextInputGroupTheme extends ThemeExtension } @override - MoonTextInputGroupTheme lerp( - ThemeExtension? other, + MoonInputGroupTheme lerp( + ThemeExtension? other, double t, ) { - if (other is! MoonTextInputGroupTheme) return this; + if (other is! MoonInputGroupTheme) return this; - return MoonTextInputGroupTheme( + return MoonInputGroupTheme( tokens: tokens.lerp(other.tokens, t), colors: colors.lerp(other.colors, t), properties: properties.lerp(other.properties, t), @@ -71,14 +71,11 @@ class MoonTextInputGroupTheme extends ThemeExtension void debugFillProperties(DiagnosticPropertiesBuilder diagnosticProperties) { super.debugFillProperties(diagnosticProperties); diagnosticProperties - ..add(DiagnosticsProperty("type", "MoonTextInputGroupTheme")) + ..add(DiagnosticsProperty("type", "MoonInputGroupTheme")) ..add(DiagnosticsProperty("tokens", tokens)) - ..add(DiagnosticsProperty("colors", colors)) + ..add(DiagnosticsProperty("colors", colors)) ..add( - DiagnosticsProperty( - "properties", - properties, - ), + DiagnosticsProperty("properties", properties), ); } } diff --git a/lib/src/theme/text_area/text_area_colors.dart b/lib/src/theme/text_area/text_area_colors.dart index 1f54cf5e..14ac784e 100644 --- a/lib/src/theme/text_area/text_area_colors.dart +++ b/lib/src/theme/text_area/text_area_colors.dart @@ -18,13 +18,13 @@ class MoonTextAreaColors extends ThemeExtension /// The color of the MoonTextArea in error state. final Color errorColor; - /// The border color of the MoonTextArea on hover. - final Color hoverBorderColor; + /// The hover effect color of the MoonTextArea. + final Color hoverColor; /// The text color of the MoonTextArea. final Color textColor; - /// The text color of the MoonTextArea helper and errorBuilder widgets. + /// The text color of the MoonTextArea helper widget. final Color helperTextColor; const MoonTextAreaColors({ @@ -32,7 +32,7 @@ class MoonTextAreaColors extends ThemeExtension required this.activeBorderColor, required this.inactiveBorderColor, required this.errorColor, - required this.hoverBorderColor, + required this.hoverColor, required this.textColor, required this.helperTextColor, }); @@ -43,7 +43,7 @@ class MoonTextAreaColors extends ThemeExtension Color? activeBorderColor, Color? inactiveBorderColor, Color? errorColor, - Color? hoverBorderColor, + Color? hoverColor, Color? textColor, Color? helperTextColor, }) { @@ -52,7 +52,7 @@ class MoonTextAreaColors extends ThemeExtension activeBorderColor: activeBorderColor ?? this.activeBorderColor, inactiveBorderColor: inactiveBorderColor ?? this.inactiveBorderColor, errorColor: errorColor ?? this.errorColor, - hoverBorderColor: hoverBorderColor ?? this.hoverBorderColor, + hoverColor: hoverColor ?? this.hoverColor, textColor: textColor ?? this.textColor, helperTextColor: helperTextColor ?? this.helperTextColor, ); @@ -70,8 +70,7 @@ class MoonTextAreaColors extends ThemeExtension inactiveBorderColor: colorPremulLerp(inactiveBorderColor, other.inactiveBorderColor, t)!, errorColor: colorPremulLerp(errorColor, other.errorColor, t)!, - hoverBorderColor: - colorPremulLerp(hoverBorderColor, other.hoverBorderColor, t)!, + hoverColor: colorPremulLerp(hoverColor, other.hoverColor, t)!, textColor: colorPremulLerp(textColor, other.textColor, t)!, helperTextColor: colorPremulLerp(helperTextColor, other.helperTextColor, t)!, @@ -87,7 +86,7 @@ class MoonTextAreaColors extends ThemeExtension ..add(ColorProperty("activeBorderColor", activeBorderColor)) ..add(ColorProperty("inactiveBorderColor", inactiveBorderColor)) ..add(ColorProperty("errorColor", errorColor)) - ..add(ColorProperty("hoverBorderColor", hoverBorderColor)) + ..add(ColorProperty("hoverColor", hoverColor)) ..add(ColorProperty("textColor", textColor)) ..add(ColorProperty("helperTextColor", helperTextColor)); } diff --git a/lib/src/theme/text_area/text_area_theme.dart b/lib/src/theme/text_area/text_area_theme.dart index 15eedd90..a6b1f2c8 100644 --- a/lib/src/theme/text_area/text_area_theme.dart +++ b/lib/src/theme/text_area/text_area_theme.dart @@ -27,7 +27,7 @@ class MoonTextAreaTheme extends ThemeExtension activeBorderColor: tokens.colors.piccolo, inactiveBorderColor: tokens.colors.beerus, errorColor: tokens.colors.chichi, - hoverBorderColor: tokens.colors.beerus, + hoverColor: tokens.colors.beerus, textColor: tokens.colors.textPrimary, helperTextColor: tokens.colors.trunks, ), diff --git a/lib/src/theme/theme.dart b/lib/src/theme/theme.dart index 03b7da82..f36c51e6 100644 --- a/lib/src/theme/theme.dart +++ b/lib/src/theme/theme.dart @@ -15,6 +15,8 @@ import 'package:moon_design/src/theme/dot_indicator/dot_indicator_theme.dart'; import 'package:moon_design/src/theme/drawer/drawer_theme.dart'; import 'package:moon_design/src/theme/dropdown/dropdown_theme.dart'; import 'package:moon_design/src/theme/effects/effects_theme.dart'; +import 'package:moon_design/src/theme/input/input_theme.dart'; +import 'package:moon_design/src/theme/input_group/input_group_theme.dart'; import 'package:moon_design/src/theme/loaders/circular_loader/circular_loader_theme.dart'; import 'package:moon_design/src/theme/loaders/linear_loader/linear_loader_theme.dart'; import 'package:moon_design/src/theme/menu_item/menu_item_theme.dart'; @@ -30,8 +32,6 @@ import 'package:moon_design/src/theme/tab_bar/tab_bar_theme.dart'; import 'package:moon_design/src/theme/table/table_theme.dart'; import 'package:moon_design/src/theme/tag/tag_theme.dart'; import 'package:moon_design/src/theme/text_area/text_area_theme.dart'; -import 'package:moon_design/src/theme/text_input/text_input_theme.dart'; -import 'package:moon_design/src/theme/text_input_group/text_input_group_theme.dart'; import 'package:moon_design/src/theme/toast/toast_theme.dart'; import 'package:moon_design/src/theme/tokens/borders.dart'; import 'package:moon_design/src/theme/tokens/opacities.dart'; @@ -41,6 +41,7 @@ import 'package:moon_design/src/theme/tokens/tokens.dart'; import 'package:moon_design/src/theme/tokens/transitions.dart'; import 'package:moon_design/src/theme/tokens/typography/typography.dart'; import 'package:moon_design/src/theme/tooltip/tooltip_theme.dart'; + import 'package:moon_tokens/moon_tokens.dart'; @immutable @@ -96,6 +97,12 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { /// The theming of the Moon Design System effects. final MoonEffectsTheme effects; + /// The theming of the Moon Design System MoonInput widget. + final MoonInputTheme inputTheme; + + /// The theming of the Moon Design System MoonInputGroup widget. + final MoonInputGroupTheme inputGroupTheme; + /// The theming of the Moon Design System MoonLinearLoader widget. final MoonLinearLoaderTheme linearLoaderTheme; @@ -135,12 +142,6 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { /// The theming of the Moon Design System MoonTextArea widget. final MoonTextAreaTheme textAreaTheme; - /// The theming of the Moon Design System MoonTextInput widget. - final MoonTextInputTheme textInputTheme; - - /// The theming of the Moon Design System MoonTextInputGroup widget. - final MoonTextInputGroupTheme textInputGroupTheme; - /// The theming of the Moon Design System MoonToast widget. final MoonToastTheme toastTheme; @@ -165,6 +166,8 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { MoonDrawerTheme? drawerTheme, MoonDropdownTheme? dropdownTheme, MoonEffectsTheme? effects, + MoonInputTheme? inputTheme, + MoonInputGroupTheme? inputGroupTheme, MoonLinearLoaderTheme? linearLoaderTheme, MoonLinearProgressTheme? linearProgressTheme, MoonMenuItemTheme? menuItemTheme, @@ -178,8 +181,6 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { MoonTableTheme? tableTheme, MoonTagTheme? tagTheme, MoonTextAreaTheme? textAreaTheme, - MoonTextInputTheme? textInputTheme, - MoonTextInputGroupTheme? textInputGroupTheme, MoonToastTheme? toastTheme, MoonTooltipTheme? tooltipTheme, }) : accordionTheme = accordionTheme ?? MoonAccordionTheme(tokens: tokens), @@ -203,6 +204,9 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { drawerTheme = drawerTheme ?? MoonDrawerTheme(tokens: tokens), dropdownTheme = dropdownTheme ?? MoonDropdownTheme(tokens: tokens), effects = effects ?? MoonEffectsTheme(tokens: tokens), + inputTheme = inputTheme ?? MoonInputTheme(tokens: tokens), + inputGroupTheme = + inputGroupTheme ?? MoonInputGroupTheme(tokens: tokens), linearLoaderTheme = linearLoaderTheme ?? MoonLinearLoaderTheme(tokens: tokens), linearProgressTheme = @@ -220,9 +224,6 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { tableTheme = tableTheme ?? MoonTableTheme(tokens: tokens), tagTheme = tagTheme ?? MoonTagTheme(tokens: tokens), textAreaTheme = textAreaTheme ?? MoonTextAreaTheme(tokens: tokens), - textInputTheme = textInputTheme ?? MoonTextInputTheme(tokens: tokens), - textInputGroupTheme = - textInputGroupTheme ?? MoonTextInputGroupTheme(tokens: tokens), toastTheme = toastTheme ?? MoonToastTheme(tokens: tokens), tooltipTheme = tooltipTheme ?? MoonTooltipTheme(tokens: tokens); @@ -245,6 +246,8 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { MoonDrawerTheme? drawerTheme, MoonDropdownTheme? dropdownTheme, MoonEffectsTheme? effects, + MoonInputTheme? inputTheme, + MoonInputGroupTheme? inputGroupTheme, MoonLinearLoaderTheme? linearLoaderTheme, MoonLinearProgressTheme? linearProgressTheme, MoonMenuItemTheme? menuItemTheme, @@ -258,8 +261,6 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { MoonTableTheme? tableTheme, MoonTagTheme? tagTheme, MoonTextAreaTheme? textAreaTheme, - MoonTextInputTheme? textInputTheme, - MoonTextInputGroupTheme? textInputGroupTheme, MoonToastTheme? toastTheme, MoonTooltipTheme? tooltipTheme, }) { @@ -282,6 +283,8 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { drawerTheme: drawerTheme ?? this.drawerTheme, dropdownTheme: dropdownTheme ?? this.dropdownTheme, effects: effects ?? this.effects, + inputTheme: inputTheme ?? this.inputTheme, + inputGroupTheme: inputGroupTheme ?? this.inputGroupTheme, linearLoaderTheme: linearLoaderTheme ?? this.linearLoaderTheme, linearProgressTheme: linearProgressTheme ?? this.linearProgressTheme, menuItemTheme: menuItemTheme ?? this.menuItemTheme, @@ -296,8 +299,6 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { tableTheme: tableTheme ?? this.tableTheme, tagTheme: tagTheme ?? this.tagTheme, textAreaTheme: textAreaTheme ?? this.textAreaTheme, - textInputTheme: textInputTheme ?? this.textInputTheme, - textInputGroupTheme: textInputGroupTheme ?? this.textInputGroupTheme, toastTheme: toastTheme ?? this.toastTheme, tooltipTheme: tooltipTheme ?? this.tooltipTheme, ); @@ -327,6 +328,8 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { drawerTheme: drawerTheme.lerp(other.drawerTheme, t), dropdownTheme: dropdownTheme.lerp(other.dropdownTheme, t), effects: effects.lerp(other.effects, t), + inputTheme: inputTheme.lerp(other.inputTheme, t), + inputGroupTheme: inputGroupTheme.lerp(other.inputGroupTheme, t), linearLoaderTheme: linearLoaderTheme.lerp(other.linearLoaderTheme, t), linearProgressTheme: linearProgressTheme.lerp(other.linearProgressTheme, t), @@ -342,9 +345,6 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { tableTheme: tableTheme.lerp(other.tableTheme, t), tagTheme: tagTheme.lerp(other.tagTheme, t), textAreaTheme: textAreaTheme.lerp(other.textAreaTheme, t), - textInputTheme: textInputTheme.lerp(other.textInputTheme, t), - textInputGroupTheme: - textInputGroupTheme.lerp(other.textInputGroupTheme, t), toastTheme: toastTheme.lerp(other.toastTheme, t), tooltipTheme: tooltipTheme.lerp(other.tooltipTheme, t), ); @@ -462,6 +462,18 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { effects, ), ) + ..add( + DiagnosticsProperty( + "MoonInputTheme", + inputTheme, + ), + ) + ..add( + DiagnosticsProperty( + "MoonInputGroupTheme", + inputGroupTheme, + ), + ) ..add( DiagnosticsProperty( "MoonLinearLoaderTheme", @@ -540,18 +552,6 @@ class MoonTheme extends ThemeExtension with DiagnosticableTreeMixin { textAreaTheme, ), ) - ..add( - DiagnosticsProperty( - "MoonTextInputTheme", - textInputTheme, - ), - ) - ..add( - DiagnosticsProperty( - "MoonTextInputGroupTheme", - textInputGroupTheme, - ), - ) ..add( DiagnosticsProperty( "MoonToastTheme", diff --git a/lib/src/utils/squircle/squircle_border.dart b/lib/src/utils/squircle/squircle_border.dart index b73737c6..bcd06260 100644 --- a/lib/src/utils/squircle/squircle_border.dart +++ b/lib/src/utils/squircle/squircle_border.dart @@ -371,8 +371,8 @@ class MoonSquicleBorderSide with Diagnosticable { /// border at all, the zero factor is special-cased to instead change the /// style to [BorderStyle.none]. /// - /// Values for 't' are usually obtained from an [Animation], such as - /// an [AnimationController]. + /// Values for 't' are usually obtained from an [Animation] of type [double], + /// such as an [AnimationController]. BorderSide scale(double t) { return BorderSide( color: color, diff --git a/lib/src/utils/squircle/squircle_radius.dart b/lib/src/utils/squircle/squircle_radius.dart index d0da164f..501bcdb3 100644 --- a/lib/src/utils/squircle/squircle_radius.dart +++ b/lib/src/utils/squircle/squircle_radius.dart @@ -129,8 +129,8 @@ class MoonSquircleRadius extends Radius { /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). /// - /// Values for `t` are usually obtained from an [Animation], such as - /// an [AnimationController]. + /// Values for 't' are usually obtained from an [Animation] of type [double], + /// such as an [AnimationController]. static MoonSquircleRadius? lerp( MoonSquircleRadius? a, MoonSquircleRadius? b, diff --git a/lib/src/widgets/common/border_container.dart b/lib/src/widgets/common/border_container.dart deleted file mode 100644 index afb483ae..00000000 --- a/lib/src/widgets/common/border_container.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:moon_core/moon_core.dart'; - -import 'package:moon_design/moon_design.dart'; - -class BorderContainer extends StatefulWidget { - final bool expands; - final Clip clipBehavior; - final Color? backgroundColor; - final Decoration? decoration; - final double? height; - final double? width; - final ShapeBorder border; - final Duration duration; - final Curve curve; - final Widget child; - - /// Creates a utility widget for animating the border of a container. - /// Primarily utilized in [MoonTextInput] and [MoonTextInputGroup]. - const BorderContainer({ - super.key, - this.expands = false, - this.clipBehavior = Clip.none, - this.backgroundColor, - this.decoration, - this.height, - this.width, - required this.border, - required this.duration, - required this.curve, - required this.child, - }); - - @override - _BorderContainerState createState() => _BorderContainerState(); -} - -class _BorderContainerState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - late Animation _borderAnimation; - late ShapeBorderTween _border; - - @override - void initState() { - super.initState(); - - _controller = AnimationController( - duration: widget.duration, - vsync: this, - ); - _borderAnimation = CurvedAnimation( - parent: _controller, - curve: widget.curve, - reverseCurve: widget.curve.flipped, - ); - _border = ShapeBorderTween( - begin: widget.border, - end: widget.border, - ); - } - - @override - void dispose() { - _controller.dispose(); - - super.dispose(); - } - - @override - void didUpdateWidget(BorderContainer oldWidget) { - super.didUpdateWidget(oldWidget); - - if (widget.border != oldWidget.border) { - _border = ShapeBorderTween( - begin: oldWidget.border, - end: widget.border, - ); - _controller - ..value = 0.0 - ..forward(); - } - } - - @override - Widget build(BuildContext context) { - return AnimatedBuilder( - animation: _borderAnimation, - builder: (context, child) { - return Container( - constraints: BoxConstraints( - minHeight: widget.height ?? 0, - minWidth: widget.width ?? 0, - maxHeight: widget.expands - ? double.infinity - : widget.height ?? double.infinity, - maxWidth: widget.expands - ? double.infinity - : widget.width ?? double.infinity, - ), - clipBehavior: widget.clipBehavior, - decoration: widget.decoration ?? - ShapeDecorationWithPremultipliedAlpha( - color: widget.backgroundColor, - shape: _border.evaluate(_borderAnimation)!, - ), - child: child, - ); - }, - child: widget.child, - ); - } -} diff --git a/lib/src/widgets/common/error_message_widgets.dart b/lib/src/widgets/common/error_message_widgets.dart index 5fc6a44c..6c6927a7 100644 --- a/lib/src/widgets/common/error_message_widgets.dart +++ b/lib/src/widgets/common/error_message_widgets.dart @@ -1,13 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:hugeicons/hugeicons.dart'; +import 'package:moon_core/moon_core.dart'; + import 'package:moon_design/src/theme/theme.dart'; -import 'package:moon_icons/moon_icons.dart'; class MoonErrorMessages extends StatelessWidget { final List errors; + final Color errorColor; - /// Creates a default error message widget, utilized in [MoonTextInputGroup]. - const MoonErrorMessages({required this.errors}); + /// Creates a default error message widget, utilized in [MoonInputGroup]. + const MoonErrorMessages({required this.errors, required this.errorColor}); List get _nonEmptyErrors => errors.where((String error) => error.isNotEmpty).toList(); @@ -20,76 +23,17 @@ class MoonErrorMessages extends StatelessWidget { final int derivedIndex = index ~/ 2; return index.isEven - ? MoonErrorMessage(errorText: _nonEmptyErrors[derivedIndex]) + ? MoonMessage( + text: _nonEmptyErrors[derivedIndex], + horizontalGap: context.moonSizes?.x5s ?? 4, + icon: Icon( + HugeIcons.strokeRoundedAlertCircle, + size: 16, + color: errorColor, + ), + ) : SizedBox(height: context.moonSizes?.x5s ?? 4); }), ); } } - -class MoonErrorMessage extends StatefulWidget { - final String errorText; - final Duration duration; - final Curve curve; - - /// Creates a default error message widget, utilized in [MoonTextInput] - /// and [MoonTextArea]. - const MoonErrorMessage({ - super.key, - required this.errorText, - this.duration = const Duration(milliseconds: 167), - this.curve = Curves.fastOutSlowIn, - }); - - @override - State createState() => _MoonErrorMessageState(); -} - -class _MoonErrorMessageState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - late Animation _opacityAnimation; - - @override - void initState() { - super.initState(); - - _controller = AnimationController( - duration: widget.duration, - vsync: this, - ); - _opacityAnimation = CurvedAnimation( - parent: _controller, - curve: widget.curve, - reverseCurve: widget.curve.flipped, - ); - - _controller - ..value = 0.0 - ..forward(); - } - - @override - void dispose() { - _controller.dispose(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return FadeTransition( - opacity: _opacityAnimation, - child: Row( - children: [ - Icon( - MoonIcons.generic_info_16_light, - size: context.moonSizes?.x2s ?? 16, - ), - SizedBox(width: context.moonSizes?.x5s ?? 4), - Text(widget.errorText), - ], - ), - ); - } -} diff --git a/lib/src/widgets/input/input.dart b/lib/src/widgets/input/input.dart new file mode 100644 index 00000000..bdb34d31 --- /dev/null +++ b/lib/src/widgets/input/input.dart @@ -0,0 +1,1152 @@ +import 'dart:ui' as ui hide TextStyle; + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hugeicons/hugeicons.dart'; + +import 'package:mix/mix.dart'; +import 'package:moon_core/moon_core.dart'; + +import 'package:moon_design/src/theme/input/input_size_properties.dart'; +import 'package:moon_design/src/theme/input/input_sizes.dart'; +import 'package:moon_design/src/theme/theme.dart'; +import 'package:moon_design/src/theme/tokens/borders.dart'; +import 'package:moon_design/src/theme/tokens/opacities.dart'; +import 'package:moon_design/src/theme/tokens/sizes.dart'; +import 'package:moon_design/src/theme/tokens/tokens.dart'; +import 'package:moon_design/src/theme/tokens/typography/typography.dart'; + +import 'package:moon_tokens/moon_tokens.dart'; + +export 'package:flutter/services.dart' + show + SmartDashesType, + SmartQuotesType, + TextCapitalization, + TextInputAction, + TextInputType; + +enum MoonInputSize { + sm, + md, + lg, + xl, +} + +class MoonInput extends StatelessWidget { + final bool _isFormInput; + + // Moon Design System properties. + /// Whether the input has floating label. Applied when [label] is not null. + final bool hasFloatingLabel; + + /// The border radius of the input. + final BorderRadiusGeometry? borderRadius; + + /// The background color of the input. + final Color? backgroundColor; + + /// The border color of the active or focused input. + final Color? activeBorderColor; + + /// The border color of the input in error state. + /// Takes precedence over [errorColor] if provided. + final Color? errorBorderColor; + + /// The border color of the inactive input. + final Color? inactiveBorderColor; + + /// The general color of the input in error state. + final Color? errorColor; + + /// The hover effect color of the input. + final Color? hoverColor; + + /// The text color of the input. + final Color? textColor; + + /// The text color of the input hint. + final Color? hintTextColor; + + /// The custom decoration of the input. + final Decoration? decoration; + + /// The gap between the [leading] widget, input area and [trailing] widget. + final double? gap; + + /// The height of the input (does not include the space taken by [MoonInput.errorBuilder]). + final double? height; + + /// The width of the input. + final double? width; + + /// The duration of the input transition animation (enable and disable). + final Duration? transitionDuration; + + /// The curve of the input transition animation (enable and disable). + final Curve? transitionCurve; + + /// The padding of the input text. + final EdgeInsetsGeometry? padding; + + /// The padding of the helper and error widget. + final EdgeInsetsGeometry? helperErrorPadding; + + /// The padding of the [label]. Applies only when [hasFloatingLabel] is false. + final EdgeInsetsGeometry? labelPadding; + + /// The size of the input. + final MoonInputSize? inputSize; + + /// The error text can be used to force input into an error state. + /// Useful for asynchronous errors. + /// + /// The validator errors take precedence over the provided [errorText]. + /// Mutually exclusive with [errorBuilder]. + final String? errorText; + + /// The helper text to display below the input area. Not displayed in error state. + /// To customize the helper, use [helper]. + /// Mutually exclusive with [helper]. + final String? helperText; + + /// The hint text to display in the input area. + /// Displayed when the input is empty and focused. + final String? hintText; + + /// The initial value of the input. If [controller] is provided, + /// this value is ignored and [controller]'s value is used. + final String? initialValue; + + /// The text style of the [helper] and error state text. + final TextStyle? helperErrorTextStyle; + + /// A builder to build and customise the text input [errorText] widget. + /// If errorBuilder is not provided, default [MoonMessage] is used to + /// display the [errorText]. + /// Mutually exclusive with [errorText]. + final MoonTextInputErrorBuilder? errorBuilder; + + /// The widget to display before the input area. + final Widget? leading; + + /// The widget to display after the input area. + final Widget? trailing; + + /// The widget to display below the input. Not displayed in error state. + /// Mutually exclusive with [helperText]. + final Widget? helper; + + /// The widget to display above the input if [hasFloatingLabel] is false or + /// inside the input if [hasFloatingLabel] is true. + /// If [hasFloatingLabel] is false, the label is always displayed regardless + /// of input state. + /// If [hasFloatingLabel] is true, the label is displayed when the input + /// is empty and not focused. When focused, the label will float to the top + /// of the input. + final Widget? label; + + // Flutter properties. + + /// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro} + /// + /// {@macro flutter.widgets.magnifier.intro} + /// + /// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details} + /// + /// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier] + /// on Android, and builds nothing on all other platforms. If it is desired to + /// suppress the magnifier, consider passing [TextMagnifierConfiguration.disabled]. + /// + /// {@tool dartpad} + /// This sample demonstrates how to customize the magnifier that this text field uses. + /// + /// ** See code in examples/api/lib/widgets/text_magnifier/text_magnifier.0.dart ** + /// {@end-tool} + final TextMagnifierConfiguration? magnifierConfiguration; + + /// Controls the input text. + /// + /// If null, this widget will create its own [TextEditingController]. + final TextEditingController? controller; + + /// Defines the keyboard focus for this widget. + /// + /// The [focusNode] is a long-lived object that is typically managed by a + /// [StatefulWidget] parent. See [FocusNode] for more information. + /// + /// To give the keyboard focus to this widget, provide a [focusNode] and then + /// use the current [FocusScope] to request the focus: + /// + /// ```dart + /// FocusScope.of(context).requestFocus(myFocusNode); + /// ``` + /// + /// This happens automatically when the widget is tapped. + /// + /// To be notified when the widget gains or loses the focus, add a listener + /// to the [focusNode]: + /// + /// ```dart + /// myFocusNode.addListener(() { print(myFocusNode.hasFocus); }); + /// ``` + /// + /// If null, this widget will create its own [FocusNode]. + /// + /// ## Keyboard + /// + /// Requesting the focus will typically cause the keyboard to be shown + /// if it is not showing already. + /// + /// On Android, the user can hide the keyboard - without changing the focus - + /// with the system back button. They can restore the keyboard's visibility + /// by tapping on a text field. The user might hide the keyboard and + /// switch to a physical keyboard, or they might just need to get it + /// out of the way for a moment, to expose something it's + /// obscuring. In this case requesting the focus again will not + /// cause the focus to change, and will not make the keyboard visible. + /// + /// This widget builds an [EditableText] and will ensure that the keyboard is + /// showing when it is tapped by calling [EditableTextState.requestKeyboard()]. + final FocusNode? focusNode; + + /// {@macro flutter.widgets.editableText.keyboardType} + final TextInputType? keyboardType; + + /// {@template flutter.widgets.TextField.inputAction} + /// The type of action button to use for the keyboard. + /// + /// Defaults to [TextInputAction.newline] if [keyboardType] is + /// [TextInputType.multiline] and [TextInputAction.done] otherwise. + /// {@endtemplate} + final TextInputAction? textInputAction; + + /// {@macro flutter.widgets.editableText.textCapitalization} + final TextCapitalization textCapitalization; + + /// The text style of the input text. + /// + /// This text style is also used as the base style for the [decoration]. + /// + /// If null, defaults to 'titleMedium' text style from the current [Theme]. + final TextStyle? style; + + /// {@macro flutter.widgets.editableText.strutStyle} + final StrutStyle? strutStyle; + + /// {@macro flutter.widgets.editableText.textAlign} + final TextAlign textAlign; + + /// {@macro flutter.material.InputDecorator.textAlignVertical} + /// If [hasFloatingLabel] is true and [label] is not null, this value is set + /// to [TextAlignVertical.bottom]. + final TextAlignVertical? textAlignVertical; + + /// {@macro flutter.widgets.editableText.textDirection} + final TextDirection? textDirection; + + /// {@macro flutter.widgets.editableText.autofocus} + final bool autofocus; + + /// {@macro flutter.widgets.editableText.obscuringCharacter} + final String obscuringCharacter; + + /// {@macro flutter.widgets.editableText.obscureText} + final bool obscureText; + + /// {@macro flutter.widgets.editableText.autocorrect} + final bool autocorrect; + + /// {@macro flutter.services.TextInputConfiguration.smartDashesType} + final SmartDashesType? smartDashesType; + + /// {@macro flutter.services.TextInputConfiguration.smartQuotesType} + final SmartQuotesType? smartQuotesType; + + /// {@macro flutter.services.TextInputConfiguration.enableSuggestions} + final bool enableSuggestions; + + /// {@macro flutter.widgets.editableText.maxLines} + /// * [expands], which determines whether the field should fill the height of + /// its parent. + final int? maxLines; + + /// {@macro flutter.widgets.editableText.minLines} + /// * [expands], which determines whether the field should fill the height of + /// its parent. + final int? minLines; + + /// {@macro flutter.widgets.editableText.expands} + final bool expands; + + /// {@macro flutter.widgets.editableText.readOnly} + final bool readOnly; + + /// {@macro flutter.widgets.editableText.showCursor} + final bool? showCursor; + + /// The maximum number of characters (unicode grapheme clusters) to allow in the text field. + /// + /// If set, a character counter will be displayed below the + /// field showing how many characters have been entered. If set to a number + /// greater than 0, it will also display the maximum number allowed. If set + /// to [MoonInput.noMaxLength] then only the current character count is displayed. + /// + /// After [maxLength] characters have been input, additional input + /// is ignored, unless [maxLengthEnforcement] is set to [MaxLengthEnforcement.none]. + /// + /// The text field enforces the length with a [LengthLimitingTextInputFormatter], + /// which is evaluated after the supplied [inputFormatters], if any. + /// + /// This value must be either null, [MoonInput.noMaxLength], or greater than 0. + /// If null (the default) then there is no limit to the number of characters + /// that can be entered. If set to [MoonInput.noMaxLength], then no limit will + /// be enforced, but the number of characters entered will still be displayed. + /// + /// Whitespace characters (e.g. newline, space, tab) are included in the character count. + /// + /// If [maxLengthEnforcement] is [MaxLengthEnforcement.none], then more than + /// [maxLength] characters may be entered, but the error counter and divider + /// will switch to the [decoration]'s [InputDecoration.errorStyle] when the limit is exceeded. + /// + /// {@macro flutter.services.lengthLimitingTextInputFormatter.maxLength} + final int? maxLength; + + /// Determines how the [maxLength] limit should be enforced. + /// + /// {@macro flutter.services.textFormatter.effectiveMaxLengthEnforcement} + /// + /// {@macro flutter.services.textFormatter.maxLengthEnforcement} + final MaxLengthEnforcement? maxLengthEnforcement; + + /// {@macro flutter.widgets.editableText.onChanged} + /// + /// See also: + /// + /// * [inputFormatters], which are called before [onChanged] + /// runs and can validate and change ("format") the input value. + /// * [onEditingComplete], [onSubmitted]: + /// which are more specialized input change notifications. + final ValueChanged? onChanged; + + /// {@macro flutter.widgets.editableText.onEditingComplete} + final VoidCallback? onEditingComplete; + + /// {@macro flutter.widgets.editableText.onSubmitted} + /// + /// See also: + /// + /// * [TextInputAction.next] and [TextInputAction.previous], which + /// automatically shift the focus to the next/previous focusable item when + /// the user is done editing. + final ValueChanged? onSubmitted; + + /// {@macro flutter.widgets.editableText.onAppPrivateCommand} + final AppPrivateCommandCallback? onAppPrivateCommand; + + /// {@macro flutter.widgets.editableText.inputFormatters} + final List? inputFormatters; + + /// If false the text field is "disabled": it ignores taps and its + /// [decoration] is rendered in grey. + /// + /// If non-null this property overrides the [decoration]'s + /// [InputDecoration.enabled] property. + final bool enabled; + + /// {@macro flutter.widgets.editableText.cursorWidth} + final double cursorWidth; + + /// {@macro flutter.widgets.editableText.cursorHeight} + final double? cursorHeight; + + /// {@macro flutter.widgets.editableText.cursorRadius} + final Radius? cursorRadius; + + /// {@macro flutter.widgets.editableText.cursorOpacityAnimates} + final bool? cursorOpacityAnimates; + + /// The color of the cursor. + /// + /// The cursor indicates the current location of text insertion point in + /// the field. + final Color? cursorColor; + + /// The color of the cursor when the [MoonInput] is showing an error. + final Color? cursorErrorColor; + + /// Controls how tall the selection highlight boxes are computed to be. + /// + /// See [ui.BoxHeightStyle] for details on available styles. + final ui.BoxHeightStyle selectionHeightStyle; + + /// Controls how wide the selection highlight boxes are computed to be. + /// + /// See [ui.BoxWidthStyle] for details on available styles. + final ui.BoxWidthStyle selectionWidthStyle; + + /// The appearance of the keyboard. + /// + /// This setting is only honored on iOS devices. + /// + /// If unset, defaults to [ThemeData.brightness]. + final Brightness? keyboardAppearance; + + /// {@macro flutter.widgets.editableText.scrollPadding} + final EdgeInsets scrollPadding; + + /// {@macro flutter.widgets.editableText.enableInteractiveSelection} + final bool? enableInteractiveSelection; + + /// {@macro flutter.widgets.editableText.selectionControls} + final TextSelectionControls? selectionControls; + + /// {@macro flutter.widgets.scrollable.dragStartBehavior} + final DragStartBehavior dragStartBehavior; + + /// {@template flutter.material.textfield.onTap} + /// Called for the first tap in a series of taps. + /// + /// The text field builds a [GestureDetector] to handle input events like tap, + /// to trigger focus requests, to move the caret, adjust the selection, etc. + /// Handling some of those events by wrapping the text field with a competing + /// GestureDetector is problematic. + /// + /// To unconditionally handle taps, without interfering with the text field's + /// internal gesture detector, provide this callback. + /// + /// If the text field is created with [enabled] false, taps will not be + /// recognized. + /// + /// To be notified when the text field gains or loses the focus, provide a + /// [focusNode] and add a listener to that. + /// + /// To listen to arbitrary pointer events without competing with the + /// text field's internal gesture detector, use a [Listener]. + /// {@endtemplate} + /// + /// If [onTapAlwaysCalled] is enabled, this will also be called for consecutive + /// taps. + final GestureTapCallback? onTap; + + /// Whether [onTap] should be called for every tap. + /// + /// Defaults to false, so [onTap] is only called for each distinct tap. When + /// enabled, [onTap] is called for every tap including consecutive taps. + final bool onTapAlwaysCalled; + + /// {@macro flutter.widgets.editableText.onTapOutside} + /// + /// {@tool dartpad} + /// This example shows how to use a 'TextFieldTapRegion' to wrap a set of + /// "spinner" buttons that increment and decrement a value in the [MoonInput] + /// without causing the text field to lose keyboard focus. + /// + /// This example includes a generic 'SpinnerField' class, parameterized by + /// type T, that you can copy into your own project and customize. + /// + /// ** See code in examples/api/lib/widgets/tap_region/text_field_tap_region.0.dart ** + /// {@end-tool} + /// + /// See also: + /// + /// * [TapRegion] for how the region group is determined. + final TapRegionCallback? onTapOutside; + + /// The cursor for a mouse pointer when it enters or is hovering over the + /// widget. + final MouseCursor? mouseCursor; + + /// {@macro flutter.widgets.editableText.scrollPhysics} + final ScrollPhysics? scrollPhysics; + + /// {@macro flutter.widgets.editableText.scrollController} + final ScrollController? scrollController; + + /// {@macro flutter.widgets.editableText.autofillHints} + /// {@macro flutter.services.AutofillConfiguration.autofillHints} + final Iterable? autofillHints; + + /// {@macro flutter.material.Material.clipBehavior} + /// + /// Defaults to [Clip.hardEdge]. + final Clip clipBehavior; + + /// {@template flutter.material.textfield.restorationId} + /// Restoration ID to save and restore the state of the text field. + /// + /// If non-null, the text field will persist and restore its current scroll + /// offset and - if no [controller] has been provided - the content of the + /// text field. If a [controller] has been provided, it is the responsibility + /// of the owner of that controller to persist and restore it, e.g. by using + /// a [RestorableTextEditingController]. + /// + /// The state of this widget is persisted in a [RestorationBucket] claimed + /// from the surrounding [RestorationScope] using the provided restoration ID. + /// + /// See also: + /// + /// * [RestorationManager], which explains how state restoration works in + /// Flutter. + /// {@endtemplate} + final String? restorationId; + + /// {@macro flutter.widgets.editableText.scribbleEnabled} + final bool scribbleEnabled; + + /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning} + final bool enableIMEPersonalizedLearning; + + /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} + final ContentInsertionConfiguration? contentInsertionConfiguration; + + /// {@macro flutter.widgets.EditableText.contextMenuBuilder} + /// + /// If not provided, will build a default menu based on the platform. + /// + /// See also: + /// + /// * [AdaptiveTextSelectionToolbar], which is built by default. + final EditableTextContextMenuBuilder? contextMenuBuilder; + + /// Determines whether this text field can request the primary focus. + /// + /// Defaults to true. If false, the text field will not request focus + /// when tapped, or when its context menu is displayed. If false it will not + /// be possible to move the focus to the text field with tab key. + final bool canRequestFocus; + + /// {@macro flutter.widgets.undoHistory.controller} + final UndoHistoryController? undoController; + + /// {@macro flutter.widgets.EditableText.spellCheckConfiguration} + /// + /// If [SpellCheckConfiguration.misspelledTextStyle] is not specified in this + /// configuration, then [materialMisspelledTextStyle] is used by default. + final SpellCheckConfiguration? spellCheckConfiguration; + + /// Used to enable/disable this form field auto validation and update its + /// error text. + /// + /// {@template flutter.widgets.FormField.autovalidateMode} + /// If [AutovalidateMode.onUserInteraction], this FormField will only + /// auto-validate after its content changes. If [AutovalidateMode.always], it + /// will auto-validate even without user interaction. If + /// [AutovalidateMode.disabled], auto-validation will be disabled. + /// + /// Defaults to [AutovalidateMode.disabled]. + /// {@endtemplate} + final AutovalidateMode? autovalidateMode; + + final MoonFormTextInputValidationStatusCallback? validationStatusCallback; + + /// Signature for being notified when a form field changes value. + /// + /// Used by [FormField.onSaved]. + final FormFieldSetter? onSaved; + + /// Signature for validating a form field. + /// + /// Returns an error string to display if the input is invalid, or null + /// otherwise. + /// + /// Used by [FormField.validator]. + final FormFieldValidator? validator; + + /// Creates a Moon Design input. + /// + /// The [maxLines] property can be set to null to remove the restriction on + /// the number of lines. By default, it is one, meaning this is a single-line + /// text field. [maxLines] must not be zero. + /// + /// The [maxLength] property is set to null by default, which means the + /// number of characters allowed in the text field is not restricted. If + /// [maxLength] is set a character counter will be displayed below the + /// field showing how many characters have been entered. If the value is + /// set to a positive integer it will also display the maximum allowed + /// number of characters to be entered. If the value is set to + /// [MoonInput.noMaxLength] then only the current length is displayed. + /// + /// After [maxLength] characters have been input, additional input + /// is ignored, unless [maxLengthEnforcement] is set to + /// [MaxLengthEnforcement.none]. + /// The text field enforces the length with a [LengthLimitingTextInputFormatter], + /// which is evaluated after the supplied [inputFormatters], if any. + /// The [maxLength] value must be either null or greater than zero. + /// + /// If [maxLengthEnforcement] is set to [MaxLengthEnforcement.none], then more + /// than [maxLength] characters may be entered, and the error counter and + /// divider will switch to the [decoration].errorStyle when the limit is + /// exceeded. + /// + /// The text cursor is not shown if [showCursor] is false or if [showCursor] + /// is null (the default) and [readOnly] is true. + /// + /// The [selectionHeightStyle] and [selectionWidthStyle] properties allow + /// changing the shape of the selection highlighting. These properties default + /// to [ui.BoxHeightStyle.tight] and [ui.BoxWidthStyle.tight] respectively and + /// must not be null. + /// + /// The [textAlign], [autofocus], [obscureText], [readOnly], [autocorrect], + /// [scrollPadding], [maxLines], [maxLength], [selectionHeightStyle], + /// [selectionWidthStyle], [enableSuggestions], and + /// [enableIMEPersonalizedLearning] arguments must not be null. + /// + /// See also: + /// + /// * [maxLength], which discusses the precise meaning of "number of + /// characters" and how it may differ from the intuitive meaning. + const MoonInput({ + // Moon Design System properties. + this.activeBorderColor, + this.backgroundColor, + this.borderRadius, + this.decoration, + this.errorBorderColor, + this.errorBuilder, + this.errorColor, + this.errorText, + this.gap, + this.hasFloatingLabel = false, + this.height, + this.helper, + this.helperErrorPadding, + this.helperText, + this.helperErrorTextStyle, + this.hoverColor, + this.inactiveBorderColor, + this.initialValue, + this.inputSize, + this.label, + this.labelPadding, + this.leading, + this.padding, + this.hintText, + this.hintTextColor, + this.textColor, + this.trailing, + this.transitionCurve, + this.transitionDuration, + this.width, + + // Flutter properties. + super.key, + this.autocorrect = true, + this.autofillHints = const [], + this.autofocus = false, + this.canRequestFocus = true, + this.clipBehavior = Clip.hardEdge, + this.contentInsertionConfiguration, + this.contextMenuBuilder, + this.controller, + this.cursorColor, + this.cursorErrorColor, + this.cursorHeight, + this.cursorOpacityAnimates, + this.cursorRadius, + this.cursorWidth = 2.0, + this.dragStartBehavior = DragStartBehavior.start, + this.enableIMEPersonalizedLearning = true, + this.enableInteractiveSelection, + this.enableSuggestions = true, + this.enabled = true, + this.expands = false, + this.focusNode, + this.inputFormatters, + this.keyboardAppearance, + this.keyboardType, + this.magnifierConfiguration, + this.maxLength, + this.maxLengthEnforcement, + this.maxLines = 1, + this.minLines, + this.mouseCursor, + this.obscureText = false, + this.obscuringCharacter = '•', + this.onAppPrivateCommand, + this.onChanged, + this.onEditingComplete, + this.onSubmitted, + this.onTap, + this.onTapAlwaysCalled = false, + this.onTapOutside, + this.readOnly = false, + this.restorationId, + this.scribbleEnabled = true, + this.scrollController, + this.scrollPadding = const EdgeInsets.all(20.0), + this.scrollPhysics, + this.selectionControls, + this.selectionHeightStyle = ui.BoxHeightStyle.tight, + this.selectionWidthStyle = ui.BoxWidthStyle.tight, + this.showCursor, + this.smartDashesType, + this.smartQuotesType, + this.spellCheckConfiguration, + this.strutStyle, + this.style, + this.textAlign = TextAlign.start, + this.textAlignVertical, + this.textCapitalization = TextCapitalization.none, + this.textDirection, + this.textInputAction, + this.undoController, + }) : assert( + helperText == null || helper == null, + 'Either helperText or helper can be provided, but not both.', + ), + validator = null, + autovalidateMode = null, + validationStatusCallback = null, + onSaved = null, + _isFormInput = false; + + const MoonInput.form({ + // Moon Design System properties. + this.activeBorderColor, + this.backgroundColor, + this.borderRadius, + this.decoration, + this.errorBorderColor, + this.errorBuilder, + this.errorColor, + this.errorText, + this.gap, + this.hasFloatingLabel = false, + this.height, + this.helper, + this.helperErrorPadding, + this.helperText, + this.helperErrorTextStyle, + this.hoverColor, + this.inactiveBorderColor, + this.initialValue, + this.inputSize, + this.label, + this.labelPadding, + this.leading, + this.padding, + this.hintText, + this.hintTextColor, + this.textColor, + this.trailing, + this.transitionCurve, + this.transitionDuration, + this.width, + + // Flutter properties. + super.key, + this.autocorrect = true, + this.autofillHints = const [], + this.autofocus = false, + this.autovalidateMode, + this.canRequestFocus = true, + this.clipBehavior = Clip.hardEdge, + this.contentInsertionConfiguration, + this.contextMenuBuilder, + this.controller, + this.cursorColor, + this.cursorErrorColor, + this.cursorHeight, + this.cursorOpacityAnimates, + this.cursorRadius, + this.cursorWidth = 2.0, + this.dragStartBehavior = DragStartBehavior.start, + this.enableIMEPersonalizedLearning = true, + this.enableInteractiveSelection, + this.enableSuggestions = true, + this.enabled = true, + this.expands = false, + this.focusNode, + this.inputFormatters, + this.keyboardAppearance, + this.keyboardType, + this.magnifierConfiguration, + this.maxLength, + this.maxLengthEnforcement, + this.maxLines = 1, + this.minLines, + this.mouseCursor, + this.obscureText = false, + this.obscuringCharacter = '•', + this.onAppPrivateCommand, + this.onChanged, + this.onEditingComplete, + this.onSaved, + this.onSubmitted, + this.onTap, + this.onTapAlwaysCalled = false, + this.onTapOutside, + this.readOnly = false, + this.restorationId, + this.scribbleEnabled = true, + this.scrollController, + this.scrollPadding = const EdgeInsets.all(20.0), + this.scrollPhysics, + this.selectionControls, + this.selectionHeightStyle = ui.BoxHeightStyle.tight, + this.selectionWidthStyle = ui.BoxWidthStyle.tight, + this.showCursor, + this.smartDashesType, + this.smartQuotesType, + this.spellCheckConfiguration, + this.strutStyle, + this.style, + this.textAlign = TextAlign.start, + this.textAlignVertical, + this.textCapitalization = TextCapitalization.none, + this.textDirection, + this.textInputAction, + this.undoController, + this.validationStatusCallback, + this.validator, + }) : assert( + helperText == null || helper == null, + 'Either helperText or helper can be provided, but not both.', + ), + _isFormInput = true; + + MoonInputSizeProperties _getMoonInputSize(MoonInputSize? moonTextInputSize) => + switch (moonTextInputSize) { + MoonInputSize.sm => MoonInputSizes(tokens: MoonTokens.light).sm, + MoonInputSize.md => MoonInputSizes(tokens: MoonTokens.light).md, + MoonInputSize.lg => MoonInputSizes(tokens: MoonTokens.light).lg, + MoonInputSize.xl => MoonInputSizes(tokens: MoonTokens.light).xl, + _ => MoonInputSizes(tokens: MoonTokens.light).md, + }; + + MoonBorder _getBorder(Color borderColor, {double? width}) { + final MoonInputSizeProperties effectiveMoonInputSize = + _getMoonInputSize(inputSize); + + final BorderRadiusGeometry effectiveBorderRadius = + borderRadius ?? effectiveMoonInputSize.borderRadius; + + return MoonBorder( + borderRadius: effectiveBorderRadius, + side: BorderSide( + color: borderColor, + width: width ?? MoonBorders.borders.defaultBorderWidth, + ), + ); + } + + MoonTextInputErrorBuilder? _buildErrorWidget() { + final Color effectiveErrorColor = errorColor ?? MoonColors.light.chichi; + + return errorBuilder ?? + (BuildContext context, String? error) => MoonMessage( + text: error ?? "", + horizontalGap: MoonSizes.sizes.x5s, + icon: Icon( + HugeIcons.strokeRoundedAlertCircle, + size: 16, + color: effectiveErrorColor, + ), + ); + } + + Widget? _buildHelperWidget() { + final Color effectiveHelperTextColor = + helperErrorTextStyle?.color ?? MoonColors.light.trunks; + + return helperText != null + ? MoonMessage( + text: helperText!, + horizontalGap: MoonSizes.sizes.x5s, + icon: Icon( + HugeIcons.strokeRoundedInformationCircle, + size: 16, + color: effectiveHelperTextColor, + ), + ) + : helper; + } + + Widget? _buildHintWidget(TextStyle hintTextStyle) => hintText != null + ? Text( + hintText!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + textAlign: textAlign, + style: hintTextStyle, + ) + : null; + + Widget? _buildLabelWidget(TextStyle labelTextStyle) => label != null + ? SpecBuilder( + style: Style( + $with.defaultTextStyle.style.as(labelTextStyle), + $with.iconTheme.data.color(labelTextStyle.color!), + ), + builder: (BuildContext context) => label!, + ) + : null; + + @override + Widget build(BuildContext context) { + final MoonInputSizeProperties effectiveMoonInputSize = + _getMoonInputSize(inputSize); + + final Color effectiveActiveBorderColor = + activeBorderColor ?? MoonColors.light.piccolo; + + final Color effectiveInactiveBorderColor = + inactiveBorderColor ?? MoonColors.light.beerus; + + final Color effectiveBackgroundColor = + backgroundColor ?? MoonColors.light.goku; + + final Color effectiveErrorColor = errorColor ?? MoonColors.light.chichi; + + final Color effectiveHoverColor = + hoverColor ?? context.moonColors?.heles ?? MoonColors.light.heles; + + final Color effectiveCursorErrorColor = + cursorErrorColor ?? effectiveErrorColor; + + final Color effectiveTextColor = textColor ?? MoonColors.light.textPrimary; + + final Color effectiveHintTextColor = + hintTextColor ?? MoonColors.light.trunks; + + final Color effectiveHelperTextColor = + helperErrorTextStyle?.color ?? MoonColors.light.trunks; + + final double effectiveGap = gap ?? effectiveMoonInputSize.gap; + + final double effectiveHeight = height ?? effectiveMoonInputSize.height; + + final double effectiveDisabledOpacityValue = + context.moonOpacities?.disabled ?? MoonOpacities.opacities.disabled; + + final double resolvedMaxHeight = + expands ? double.infinity : effectiveHeight; + + final double resolvedMaxWidth = + expands ? double.infinity : width ?? double.infinity; + + final Duration effectiveTransitionDuration = + transitionDuration ?? const Duration(milliseconds: 167); + + final Curve effectiveTransitionCurve = + transitionCurve ?? Curves.fastOutSlowIn; + + final EdgeInsetsGeometry effectivePadding = + padding ?? effectiveMoonInputSize.padding; + + final EdgeInsetsGeometry effectiveHelperErrorPadding = + helperErrorPadding ?? EdgeInsets.only(top: MoonSizes.sizes.x4s); + + final EdgeInsetsGeometry effectiveLabelPadding = + labelPadding ?? EdgeInsets.only(bottom: MoonSizes.sizes.x4s); + + final TextStyle effectiveTextStyle = effectiveMoonInputSize.textStyle + .copyWith(color: effectiveTextColor) + .merge(style); + + final TextStyle effectiveHelperTextStyle = MoonTypography + .typography.body.text12 + .copyWith(color: effectiveHelperTextColor) + .merge(helperErrorTextStyle); + + final TextStyle effectiveErrorTextStyle = + effectiveHelperTextStyle.copyWith(color: effectiveErrorColor); + + final TextStyle effectiveHintTextStyle = + effectiveTextStyle.copyWith(color: effectiveHintTextColor); + + final TextStyle resolvedLabelTextStyle = + hasFloatingLabel ? effectiveHintTextStyle : effectiveTextStyle; + + final Widget? hint = _buildHintWidget(effectiveHintTextStyle); + + final Widget? label = _buildLabelWidget(resolvedLabelTextStyle); + + final Widget? helper = _buildHelperWidget(); + + final Style inputStyle = Style( + $box.chain + ..padding.directional.as(effectivePadding) + ..clipBehavior(decoration != null ? clipBehavior : Clip.none) + ..constraints.minHeight(effectiveHeight) + ..constraints.minWidth(width ?? 0) + ..constraints.maxHeight(resolvedMaxHeight) + ..constraints.maxWidth(resolvedMaxWidth), + $flex.chain + ..gap(effectiveGap) + ..crossAxisAlignment( + expands ? CrossAxisAlignment.center : CrossAxisAlignment.stretch, + ), + decoration != null + ? decorationToAttribute(decoration!) + : $with.animatedShapeDecoration( + bgColor: effectiveBackgroundColor, + border: _getBorder(effectiveInactiveBorderColor), + duration: effectiveTransitionDuration, + curve: effectiveTransitionCurve, + ), + $with.animatedOpacity( + opacity: enabled ? 1.0 : effectiveDisabledOpacityValue, + duration: effectiveTransitionDuration, + curve: effectiveTransitionCurve, + ), + $on.hover( + decoration == null + ? $with.animatedShapeDecoration( + hoverColor: effectiveHoverColor, + border: _getBorder(effectiveInactiveBorderColor), + ) + : null, + ), + $on.focus( + decoration == null + ? $with.animatedShapeDecoration( + hoverColor: Colors.transparent, + border: _getBorder( + effectiveActiveBorderColor, + width: MoonBorders.borders.activeBorderWidth, + ), + ) + : null, + ), + $on.error( + decoration == null + ? $with.animatedShapeDecoration( + border: _getBorder( + errorBorderColor ?? effectiveErrorColor, + width: MoonBorders.borders.activeBorderWidth, + ), + ) + : null, + ), + ); + + final Style helperErrorStyle = Style( + $box.padding.as(effectiveHelperErrorPadding), + $with.align(alignment: Alignment.centerLeft), + $with.iconTheme.data.color(effectiveHelperTextColor), + $with.defaultTextStyle.style.as(effectiveHelperTextStyle), + $on.error( + $with.iconTheme.data.color(effectiveErrorColor), + $with.defaultTextStyle.style.as(effectiveErrorTextStyle), + ), + $with.animatedOpacity( + opacity: enabled ? 1.0 : effectiveDisabledOpacityValue, + duration: effectiveTransitionDuration, + curve: effectiveTransitionCurve, + ), + ); + + final MoonTextInputConfiguration config = MoonTextInputConfiguration( + autocorrect: autocorrect, + autofillHints: autofillHints, + autofocus: autofocus, + canRequestFocus: canRequestFocus, + clipBehavior: clipBehavior, + contentInsertionConfiguration: contentInsertionConfiguration, + contextMenuBuilder: contextMenuBuilder, + controller: controller, + cursorColor: cursorColor, + cursorErrorColor: effectiveCursorErrorColor, + cursorHeight: cursorHeight, + cursorOpacityAnimates: cursorOpacityAnimates, + cursorRadius: cursorRadius, + cursorWidth: cursorWidth, + dragStartBehavior: dragStartBehavior, + enableIMEPersonalizedLearning: enableIMEPersonalizedLearning, + enableInteractiveSelection: enableInteractiveSelection, + enableSuggestions: enableSuggestions, + enabled: enabled, + errorBuilder: _buildErrorWidget(), + expands: expands, + focusNode: focusNode, + hasFloatingLabel: label != null && hasFloatingLabel, + helper: helper, + helperErrorStyle: helperErrorStyle, + hint: hint, + initialValue: initialValue, + inputFormatters: inputFormatters, + inputStyle: inputStyle, + keyboardAppearance: keyboardAppearance, + keyboardType: keyboardType, + label: hasFloatingLabel ? label : null, + labelTextAlignVertical: hasFloatingLabel && label != null + ? TextAlignVertical.center + : textAlignVertical, + leading: leading, + magnifierConfiguration: magnifierConfiguration, + maxLength: maxLength, + maxLengthEnforcement: maxLengthEnforcement, + maxLines: maxLines, + minLines: minLines, + mouseCursor: mouseCursor, + obscureText: obscureText, + obscuringCharacter: obscuringCharacter, + onAppPrivateCommand: onAppPrivateCommand, + onChanged: onChanged, + onEditingComplete: onEditingComplete, + onSubmitted: onSubmitted, + onTap: onTap, + onTapAlwaysCalled: onTapAlwaysCalled, + onTapOutside: onTapOutside, + readOnly: readOnly, + restorationId: restorationId, + scribbleEnabled: scribbleEnabled, + scrollController: scrollController, + scrollPadding: scrollPadding, + scrollPhysics: scrollPhysics, + selectionControls: selectionControls, + selectionHeightStyle: selectionHeightStyle, + selectionWidthStyle: selectionWidthStyle, + showCursor: showCursor, + smartDashesType: smartDashesType, + smartQuotesType: smartQuotesType, + spellCheckConfiguration: spellCheckConfiguration, + strutStyle: strutStyle, + style: effectiveTextStyle, + textAlign: textAlign, + textAlignVertical: hasFloatingLabel && label != null + ? TextAlignVertical.bottom + : textAlignVertical, + textCapitalization: textCapitalization, + textDirection: textDirection, + textInputAction: textInputAction, + trailing: trailing, + transitionCurve: transitionCurve, + transitionDuration: transitionDuration, + undoController: undoController, + ); + + final Widget child = _isFormInput + ? MoonRawFormTextInput( + restorationId: restorationId, + autovalidateMode: autovalidateMode, + onSaved: onSaved, + validator: validator, + validationStatusCallback: validationStatusCallback, + textInputConfiguration: config, + ) + : MoonRawTextInput(textInputConfiguration: config); + + return (label != null && !hasFloatingLabel) + ? Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AnimatedOpacity( + opacity: enabled ? 1.0 : effectiveDisabledOpacityValue, + duration: effectiveTransitionDuration, + curve: effectiveTransitionCurve, + child: Padding( + padding: effectiveLabelPadding, + child: label, + ), + ), + child, + ], + ) + : child; + } +} diff --git a/lib/src/widgets/input_group/input_group.dart b/lib/src/widgets/input_group/input_group.dart new file mode 100644 index 00000000..e93f547e --- /dev/null +++ b/lib/src/widgets/input_group/input_group.dart @@ -0,0 +1,591 @@ +import 'package:flutter/material.dart'; + +import 'package:mix/mix.dart'; +import 'package:moon_core/moon_core.dart'; + +import 'package:moon_design/moon_design.dart'; +import 'package:moon_design/src/theme/tokens/transitions.dart'; +import 'package:moon_design/src/widgets/common/error_message_widgets.dart'; + +enum MoonInputGroupOrientation { + vertical, + horizontal, +} + +typedef MoonInputGroupErrorBuilder = Widget Function( + BuildContext context, + List errorText, +); + +class MoonInputGroup extends StatefulWidget { + /// Whether the input group is enabled. When false, taps are ignored and the + /// opacity is reduced. + final bool enabled; + + /// The border radius of the input group and its children. + /// If not provided, both the input group and its children have a default + /// border radius of 8. + /// If custom [decoration] is used, border radius needs to be defined for the + /// input group children to have the same rounded corners. + final BorderRadiusGeometry? borderRadius; + + /// {@macro flutter.material.Material.clipBehavior} + /// + /// Defaults to [Clip.hardEdge]. + final Clip? clipBehavior; + + /// The background color of the input group. + /// If [MoonInput.backgroundColor] is set, it takes precedence over this. + final Color? backgroundColor; + + /// The default border color of the input group. + final Color? borderColor; + + /// The color of the input group in error state. + /// If [MoonInput.errorColor] is set, it takes precedence over this. + final Color? errorColor; + + /// The text color of the input group's children hint. + /// If [MoonInput.hintTextColor] is set, it takes precedence over this. + final Color? hintColor; + + /// The hover effect color of the input group's children. + /// If [MoonInput.hoverColor] is set, it takes precedence over this. + final Color? hoverColor; + + /// The height of the input group. Only applied when [orientation] is + /// [MoonInputGroupOrientation.horizontal]. + /// If null, default height of 56 is used. + final double? height; + + /// The duration of the input group transition animation (enable and disable). + final Duration? transitionDuration; + + /// The curve of the input group transition animation (enable and disable). + final Curve? transitionCurve; + + /// The padding of the [helper] and [errorBuilder] widgets. + final EdgeInsetsGeometry? helperPadding; + + /// The padding of the [label]. + final EdgeInsetsGeometry? labelPadding; + + /// The custom decoration of the input group. + final Decoration? decoration; + + /// The list of [MoonInput]'s to display as the children of the input group. + final List children; + + /// The orientation of the input group. + final MoonInputGroupOrientation orientation; + + /// A builder to build the input group error widget. + final MoonInputGroupErrorBuilder? errorBuilder; + + /// The semantic label for the input group widget. + final String? semanticLabel; + + /// The helper text to display below the input group area. Not displayed in error state. + /// To customize the helper, use [helper]. + /// Mutually exclusive with [helper]. + final String? helperText; + + /// The text style of the [helper] or error state text of the input group. + final TextStyle? helperErrorTextStyle; + + /// The widget to display below the input group. Not displayed in error state. + final Widget? helper; + + /// The widget to display above the input group. + /// If provided, the label is always displayed regardless of the input group state. + /// If [MoonInput.label] is specified, it is displayed only when + /// [MoonInput.hasFloatingLabel] is true. + final Widget? label; + + /// The divider widget to display between the input group children. + /// If not provided, default divider is used. + final Widget? divider; + + /// Creates a Moon Design input group. + const MoonInputGroup({ + super.key, + required this.children, + this.backgroundColor, + this.borderColor, + this.borderRadius, + this.clipBehavior, + this.decoration, + this.divider, + this.enabled = true, + this.errorBuilder, + this.errorColor, + this.height, + this.helper, + this.helperPadding, + this.helperText, + this.helperErrorTextStyle, + this.hoverColor, + this.label, + this.labelPadding, + this.orientation = MoonInputGroupOrientation.vertical, + this.hintColor, + this.semanticLabel, + this.transitionCurve, + this.transitionDuration, + }) : assert( + helperText == null || helper == null, + 'Either helperText or helper can be provided, but not both.', + ); + + @override + State createState() => _MoonInputGroupState(); +} + +class _MoonInputGroupState extends State { + late final List _validatorErrors = + List.filled(widget.children.length, null); + + late List _previousValidatorErrors; + + bool get _isVertical => + widget.orientation == MoonInputGroupOrientation.vertical; + + bool get _groupHasValidationError => + _validatorErrors.nonNulls.toList().isNotEmpty; + + bool get _groupHasErrorText => + widget.children.any((MoonInput child) => child.errorText != null); + + bool get _groupHasError => _groupHasValidationError || _groupHasErrorText; + + bool get _groupHasAllValidationErrors => + _validatorErrors.nonNulls.length == widget.children.length; + + bool get _groupHasAllErrorTexts => + widget.children.every((MoonInput child) => child.errorText != null); + + bool get _groupIsInErrorState => + _groupHasAllValidationErrors || _groupHasAllErrorTexts; + + bool get _hasAllInError => + (!_groupHasValidationError && _groupHasAllErrorTexts) || + _groupHasAllValidationErrors; + + bool get _shouldShowError => _groupHasError || _groupIsInErrorState; + + bool get _shouldShowHelper => + widget.helper != null || widget.helperText != null; + + void _handleValidationError(int index, String? errorText) { + _previousValidatorErrors = _validatorErrors; + + if (_previousValidatorErrors[index] == errorText) return; + + _validatorErrors[index] = errorText; + + WidgetsBinding.instance.addPostFrameCallback((Duration _) { + // Rebuild the widget to show the error. + if (mounted) setState(() {}); + }); + } + + MoonBorder _getBorder({required Color borderColor, double borderWidth = 1}) { + final BorderRadiusGeometry effectiveBorderRadius = + widget.borderRadius ?? BorderRadius.circular(8); + + return MoonBorder( + borderRadius: effectiveBorderRadius, + side: BorderSide(color: borderColor, width: borderWidth), + ); + } + + BorderRadius _getChildResolvedBorderRadius( + BorderRadiusGeometry borderRadius, + bool isFirstItem, + bool isLastItem, + ) { + final TextDirection textDirection = Directionality.of(context); + + final bool isLTR = textDirection == TextDirection.ltr; + + final resolvedBorderRadius = borderRadius.resolve(textDirection); + + final double effectiveBorderWidth = + widget.decoration == null ? MoonBorders.borders.defaultBorderWidth : 0; + + Radius getRadius(Radius radius) => Radius.circular( + (radius.x - effectiveBorderWidth).clamp(0, double.infinity), + ); + + return BorderRadius.only( + topLeft: getRadius( + _isVertical || isLTR + ? (isFirstItem ? resolvedBorderRadius.topLeft : Radius.zero) + : (isLastItem ? resolvedBorderRadius.topLeft : Radius.zero), + ), + topRight: getRadius( + _isVertical || isLTR + ? ((isFirstItem && _isVertical) || (isLastItem && !_isVertical) + ? resolvedBorderRadius.topRight + : Radius.zero) + : ((isFirstItem && !_isVertical) || (isLastItem && _isVertical) + ? resolvedBorderRadius.topRight + : Radius.zero), + ), + bottomLeft: getRadius( + _isVertical || isLTR + ? ((isFirstItem && !_isVertical) || (isLastItem && _isVertical) + ? resolvedBorderRadius.bottomLeft + : Radius.zero) + : ((isLastItem && !_isVertical) || (isFirstItem && _isVertical) + ? resolvedBorderRadius.bottomLeft + : Radius.zero), + ), + bottomRight: getRadius( + _isVertical || isLTR + ? (isLastItem ? resolvedBorderRadius.bottomRight : Radius.zero) + : (isFirstItem ? resolvedBorderRadius.bottomRight : Radius.zero), + ), + ); + } + + @override + Widget build(BuildContext context) { + final BorderRadiusGeometry effectiveBorderRadius = + widget.borderRadius ?? BorderRadiusDirectional.circular(8); + + final Color effectiveBackgroundColor = + widget.backgroundColor ?? MoonColors.light.goku; + + final Color effectiveBorderColor = + widget.borderColor ?? MoonColors.light.beerus; + + final Color effectiveErrorColor = + widget.errorColor ?? MoonColors.light.chichi; + + final Color effectiveHelperTextColor = + widget.helperErrorTextStyle?.color ?? MoonColors.light.trunks; + + final Color resolvedHelperErrorTextColor = + _shouldShowError ? effectiveErrorColor : effectiveHelperTextColor; + + final Color effectiveTextColor = MoonColors.light.bulma; + + final double effectiveOpacity = + widget.enabled ? 1 : MoonOpacities.opacities.disabled; + + final Duration effectiveTransitionDuration = widget.transitionDuration ?? + MoonTransitions.transitions.defaultTransitionDuration; + + final Curve effectiveTransitionCurve = widget.transitionCurve ?? + MoonTransitions.transitions.defaultTransitionCurve; + + final EdgeInsetsGeometry effectiveHelperErrorPadding = + widget.helperPadding ?? EdgeInsets.only(top: MoonSizes.sizes.x4s); + + final EdgeInsetsGeometry effectiveLabelPadding = + widget.labelPadding ?? EdgeInsets.only(bottom: MoonSizes.sizes.x4s); + + final TextStyle effectiveHelperTextStyle = MoonTypography + .typography.body.text12 + .merge(widget.helperErrorTextStyle); + + final TextStyle effectiveLabelTextStyle = MoonTypography + .typography.body.text14 + .copyWith(color: effectiveTextColor); + + final TextStyle resolvedHelperErrorTextStyle = + effectiveHelperTextStyle.copyWith(color: resolvedHelperErrorTextColor); + + List childrenWithDivider() => List.generate( + widget.children.length * 2 - 1, + (int index) { + final int derivedIndex = index ~/ 2; + + final MoonInput config = widget.children[derivedIndex]; + + final bool selfShowError = ((config.errorText != null && + _validatorErrors[derivedIndex] == null) && + !_groupHasValidationError && + !_groupHasAllErrorTexts) || + (_validatorErrors[derivedIndex] != null && + !_groupHasAllValidationErrors); + + final bool isLastItem = derivedIndex == widget.children.length - 1; + final bool isFirstItem = derivedIndex == 0; + + final BorderRadius resolvedBorderRadius = + _getChildResolvedBorderRadius( + effectiveBorderRadius, + isFirstItem, + isLastItem, + ); + + Widget child = MoonInput.form( + activeBorderColor: config.activeBorderColor, + autocorrect: config.autocorrect, + autofillHints: config.autofillHints, + autofocus: config.autofocus, + autovalidateMode: config.autovalidateMode, + backgroundColor: config.backgroundColor ?? Colors.transparent, + borderRadius: config.borderRadius ?? resolvedBorderRadius, + canRequestFocus: config.canRequestFocus, + clipBehavior: config.clipBehavior, + contentInsertionConfiguration: + config.contentInsertionConfiguration, + contextMenuBuilder: config.contextMenuBuilder, + controller: config.controller, + cursorColor: config.cursorColor, + cursorErrorColor: config.cursorErrorColor, + cursorHeight: config.cursorHeight, + cursorOpacityAnimates: config.cursorOpacityAnimates, + cursorRadius: config.cursorRadius, + cursorWidth: config.cursorWidth, + decoration: config.decoration, + dragStartBehavior: config.dragStartBehavior, + enableIMEPersonalizedLearning: + config.enableIMEPersonalizedLearning, + enableInteractiveSelection: config.enableInteractiveSelection, + enableSuggestions: config.enableSuggestions, + enabled: widget.enabled && config.enabled, + errorBorderColor: selfShowError + ? config.errorBorderColor ?? effectiveErrorColor + : Colors.transparent, + errorBuilder: (BuildContext _, String? __) => + const SizedBox.shrink(), + errorColor: config.errorColor ?? widget.errorColor, + errorText: config.errorText, + expands: config.expands, + focusNode: config.focusNode, + gap: config.gap, + hasFloatingLabel: config.hasFloatingLabel, + height: _isVertical ? config.height : widget.height, + helper: config.helper, + helperErrorPadding: config.helperErrorPadding ?? EdgeInsets.zero, + helperErrorTextStyle: + config.helperErrorTextStyle ?? widget.helperErrorTextStyle, + helperText: config.helperText, + hintText: config.hintText, + hintTextColor: config.hintTextColor ?? widget.hintColor, + hoverColor: config.hoverColor ?? widget.hoverColor, + inactiveBorderColor: Colors.transparent, + initialValue: config.initialValue, + textInputAction: config.textInputAction, + inputFormatters: config.inputFormatters, + inputSize: config.inputSize, + keyboardAppearance: config.keyboardAppearance, + keyboardType: config.keyboardType, + label: config.hasFloatingLabel ? config.label : null, + labelPadding: config.labelPadding, + leading: config.leading, + magnifierConfiguration: config.magnifierConfiguration, + maxLength: config.maxLength, + maxLengthEnforcement: config.maxLengthEnforcement, + maxLines: config.maxLines, + minLines: config.minLines, + mouseCursor: config.mouseCursor, + obscureText: config.obscureText, + obscuringCharacter: config.obscuringCharacter, + onAppPrivateCommand: config.onAppPrivateCommand, + onChanged: config.onChanged, + onEditingComplete: config.onEditingComplete, + onSaved: config.onSaved, + onSubmitted: config.onSubmitted, + onTap: config.onTap, + onTapAlwaysCalled: config.onTapAlwaysCalled, + onTapOutside: config.onTapOutside, + padding: config.padding, + readOnly: config.readOnly, + restorationId: config.restorationId, + scribbleEnabled: config.scribbleEnabled, + scrollController: config.scrollController, + scrollPadding: config.scrollPadding, + scrollPhysics: config.scrollPhysics, + selectionControls: config.selectionControls, + selectionHeightStyle: config.selectionHeightStyle, + selectionWidthStyle: config.selectionWidthStyle, + showCursor: config.showCursor, + smartDashesType: config.smartDashesType, + smartQuotesType: config.smartQuotesType, + spellCheckConfiguration: config.spellCheckConfiguration, + strutStyle: config.strutStyle, + style: config.style, + textAlign: config.textAlign, + textAlignVertical: config.textAlignVertical, + textCapitalization: config.textCapitalization, + textColor: config.textColor, + textDirection: config.textDirection, + trailing: config.trailing, + transitionCurve: + config.transitionCurve ?? effectiveTransitionCurve, + transitionDuration: + config.transitionDuration ?? effectiveTransitionDuration, + undoController: config.undoController, + validationStatusCallback: (String? errorText) => + _handleValidationError(derivedIndex, errorText), + validator: config.validator, + width: config.width, + ); + + child = config.width != null + ? SizedBox(width: config.width, child: child) + : Flexible(child: child); + + return index.isEven + ? child + : widget.divider ?? + Box( + style: Style( + $box.chain + ..height(_isVertical ? 1 : double.infinity) + ..width(_isVertical ? double.infinity : 1), + $with.animatedShapeDecoration( + border: _getBorder( + borderColor: _hasAllInError + ? effectiveErrorColor + : effectiveBorderColor, + ), + duration: effectiveTransitionDuration, + curve: effectiveTransitionCurve, + ), + ), + child: const SizedBox.shrink(), + ); + }, + growable: false, + ); + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (widget.label != null) + SpecBuilder( + style: Style( + $with.defaultTextStyle.style.as(effectiveLabelTextStyle), + $with.iconTheme.data.color(effectiveTextColor), + $with.animatedOpacity( + opacity: effectiveOpacity, + duration: effectiveTransitionDuration, + curve: effectiveTransitionCurve, + ), + ), + builder: (BuildContext context) { + return Padding( + padding: effectiveLabelPadding, + child: widget.label, + ); + }, + ), + MoonBaseInteractiveWidget( + enabled: widget.enabled, + semanticLabel: widget.semanticLabel, + focusNode: FocusNode(skipTraversal: true), + onTap: widget.enabled ? () {} : null, + style: Style( + widget.decoration != null ? null : $box.padding.all(1), + widget.decoration != null && widget.clipBehavior != null + ? $box.clipBehavior(widget.clipBehavior!) + : null, + widget.decoration != null + ? decorationToAttribute(widget.decoration!) + : $with.animatedShapeDecoration( + bgColor: effectiveBackgroundColor, + border: _getBorder( + borderColor: _hasAllInError + ? effectiveErrorColor + : effectiveBorderColor, + ), + duration: effectiveTransitionDuration, + curve: effectiveTransitionCurve, + ), + $with.animatedOpacity( + opacity: effectiveOpacity, + duration: effectiveTransitionDuration, + curve: effectiveTransitionCurve, + ), + ), + child: _InputGroupOrientation( + orientation: widget.orientation, + height: widget.height, + children: childrenWithDivider(), + ), + ), + if (_shouldShowHelper || _shouldShowError) + SpecBuilder( + style: Style( + $with.defaultTextStyle.style.as(resolvedHelperErrorTextStyle), + $with.iconTheme.data.color(resolvedHelperErrorTextColor), + $with.animatedOpacity( + opacity: effectiveOpacity, + duration: effectiveTransitionDuration, + curve: effectiveTransitionCurve, + ), + ), + builder: (BuildContext context) { + final List effectiveErrorMessages = + _validatorErrors.nonNulls.toList().isNotEmpty + ? _validatorErrors.nonNulls.toList() + : widget.children + .map((MoonInput child) => child.errorText) + .nonNulls + .toList(); + + return Padding( + padding: effectiveHelperErrorPadding, + child: _shouldShowError + ? (widget.errorBuilder + ?.call(context, effectiveErrorMessages) ?? + MoonErrorMessages( + errors: effectiveErrorMessages, + errorColor: effectiveErrorColor, + )) + : widget.helperText != null + ? MoonMessage( + text: widget.helperText!, + horizontalGap: MoonSizes.sizes.x5s, + icon: Icon( + HugeIcons.strokeRoundedInformationCircle, + size: 16, + color: effectiveHelperTextColor, + ), + ) + : widget.helper, + ); + }, + ), + ], + ); + } +} + +class _InputGroupOrientation extends StatelessWidget { + final double? height; + final MoonInputGroupOrientation orientation; + final List children; + + const _InputGroupOrientation({ + this.height, + required this.orientation, + required this.children, + }); + + @override + Widget build(BuildContext context) { + return switch (orientation) { + MoonInputGroupOrientation.vertical => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: children, + ), + MoonInputGroupOrientation.horizontal => SizedBox( + height: height ?? 56, + width: 300, + child: Row( + mainAxisSize: MainAxisSize.min, + children: children, + ), + ), + }; + } +} diff --git a/lib/src/widgets/text_area/text_area.dart b/lib/src/widgets/text_area/text_area.dart index 81b8bedb..4990f916 100644 --- a/lib/src/widgets/text_area/text_area.dart +++ b/lib/src/widgets/text_area/text_area.dart @@ -1,11 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:moon_design/src/theme/theme.dart'; -import 'package:moon_design/src/theme/tokens/sizes.dart'; -import 'package:moon_design/src/theme/tokens/transitions.dart'; import 'package:moon_design/src/theme/tokens/typography/typography.dart'; -import 'package:moon_design/src/widgets/text_input/form_text_input.dart'; +import 'package:moon_design/src/widgets/input/input.dart'; + import 'package:moon_tokens/moon_tokens.dart'; typedef MoonTextAreaErrorBuilder = Widget Function( @@ -75,8 +73,8 @@ class MoonTextArea extends StatelessWidget { /// The color of the text area in error state. final Color? errorColor; - /// The border color of the text area on hover. - final Color? hoverBorderColor; + /// The hover effect color of the text area. + final Color? hoverColor; /// The text color of the text area. final Color? textColor; @@ -94,11 +92,14 @@ class MoonTextArea extends StatelessWidget { /// The curve of the text area transition animation (enable and disable). final Curve? transitionCurve; + /// The padding of the text area [label]. + final EdgeInsetsGeometry? labelPadding; + /// {@macro flutter.widgets.editableText.scrollPadding} final EdgeInsets scrollPadding; /// The padding of the [helper] and [errorBuilder] widgets. - final EdgeInsetsGeometry? helperPadding; + final EdgeInsetsGeometry? helperErrorPadding; /// The padding of the text content. final EdgeInsetsGeometry? textPadding; @@ -143,6 +144,10 @@ class MoonTextArea extends StatelessWidget { /// The hint text to display in the text area. final String? hintText; + /// The helper text to display below the text area. Not displayed in error state. + /// To customize the helper, use [helper]. Mutually exclusive with [helper]. + final String? helperText; + /// The initial value of the text area. final String? initialValue; @@ -156,9 +161,6 @@ class MoonTextArea extends StatelessWidget { /// {@endtemplate} final String? restorationId; - /// The semantic label for the text area. - final String? semanticLabel; - /// {@macro flutter.widgets.editableText.strutStyle} final StrutStyle? strutStyle; @@ -185,8 +187,8 @@ class MoonTextArea extends StatelessWidget { /// This text style is also used as the base style for the [decoration]. final TextStyle? textStyle; - /// The text style of the [helper] widget or error state text. - final TextStyle? helperTextStyle; + /// The text style of the [helper] and error state text. + final TextStyle? helperErrorTextStyle; /// The text validator for the text area widget. final FormFieldValidator? validator; @@ -228,189 +230,153 @@ class MoonTextArea extends StatelessWidget { /// The widget to display below the text area. Not displayed in error state. final Widget? helper; + /// The widget that describes the text area field. Displayed above the text area. + final Widget? label; + /// Creates a Moon Design text area. const MoonTextArea({ super.key, - this.autovalidateMode = AutovalidateMode.disabled, + this.activeBorderColor, this.autocorrect = true, + this.autofillHints, this.autofocus = false, - this.enabled = true, + this.autovalidateMode = AutovalidateMode.disabled, + this.backgroundColor, + this.borderRadius, + this.clipBehavior, + this.controller, + this.decoration, this.enableIMEPersonalizedLearning = true, this.enableInteractiveSelection, this.enableSuggestions = true, - this.expands = false, - this.readOnly = false, - this.scribbleEnabled = true, - this.showCursor, - this.borderRadius, - this.keyboardAppearance, - this.clipBehavior, - this.backgroundColor, - this.activeBorderColor, - this.inactiveBorderColor, + this.enabled = true, + this.errorBuilder, this.errorColor, - this.hoverBorderColor, - this.textColor, - this.hintTextColor, - this.height, - this.transitionDuration, - this.transitionCurve, - this.scrollPadding = const EdgeInsets.all(24.0), - this.helperPadding, - this.textPadding, + this.expands = false, this.focusNode, - this.validator, - this.maxLength, - this.minLines, - this.autofillHints, + this.height, + this.helper, + this.helperText, + this.helperErrorPadding, + this.helperErrorTextStyle, + this.hintText, + this.hintTextColor, + this.hoverColor, + this.inactiveBorderColor, + this.initialValue, this.inputFormatters, + this.keyboardAppearance, + this.label, + this.labelPadding, + this.maxLength, this.maxLengthEnforcement, + this.minLines, + this.onChanged, + this.onEditingComplete, + this.onSaved, + this.onSubmitted, + this.onTap, + this.onTapOutside, + this.readOnly = false, + this.restorationId, + this.scribbleEnabled = true, this.scrollController, + this.scrollPadding = const EdgeInsets.all(24.0), this.scrollPhysics, - this.decoration, - this.hintText, - this.initialValue, - this.restorationId, - this.semanticLabel, + this.showCursor, this.strutStyle, this.textAlign = TextAlign.start, this.textCapitalization = TextCapitalization.none, + this.textColor, this.textDirection, - this.controller, this.textInputAction, + this.textPadding, this.textStyle, - this.helperTextStyle, - this.onTap, - this.onTapOutside, - this.onChanged, - this.onEditingComplete, - this.onSaved, - this.onSubmitted, - this.errorBuilder, - this.helper, - }); + this.transitionCurve, + this.transitionDuration, + this.validator, + }) : assert( + helperText == null || helper == null, + 'Either helperText or helper can be provided, but not both.', + ); @override Widget build(BuildContext context) { - final BorderRadiusGeometry effectiveBorderRadius = borderRadius ?? - context.moonTheme?.textAreaTheme.properties.borderRadius ?? - BorderRadius.circular(8); - - final Color effectiveBackgroundColor = backgroundColor ?? - context.moonTheme?.textAreaTheme.colors.backgroundColor ?? - MoonColors.light.goku; - - final Color effectiveActiveBorderColor = activeBorderColor ?? - context.moonTheme?.textAreaTheme.colors.activeBorderColor ?? - MoonColors.light.piccolo; - - final Color effectiveInactiveBorderColor = inactiveBorderColor ?? - context.moonTheme?.textAreaTheme.colors.inactiveBorderColor ?? - MoonColors.light.beerus; - - final Color effectiveErrorColor = errorColor ?? - context.moonTheme?.textAreaTheme.colors.errorColor ?? - MoonColors.light.chichi; - - final Color effectiveHoverBorderColor = hoverBorderColor ?? - context.moonTheme?.textAreaTheme.colors.hoverBorderColor ?? - MoonColors.light.beerus; - - final Color effectiveTextColor = textColor ?? - context.moonTheme?.textAreaTheme.colors.textColor ?? - MoonColors.light.textPrimary; - - final Color effectiveHelperTextColor = hintTextColor ?? - context.moonTheme?.textAreaTheme.colors.helperTextColor ?? - MoonColors.light.trunks; - - final EdgeInsetsGeometry effectiveHelperPadding = helperPadding ?? - context.moonTheme?.textAreaTheme.properties.helperPadding ?? - EdgeInsets.only( - left: MoonSizes.sizes.x3s, - top: MoonSizes.sizes.x4s, - right: MoonSizes.sizes.x3s, - ); - - final EdgeInsetsGeometry effectiveTextPadding = textPadding ?? - context.moonTheme?.textAreaTheme.properties.textPadding ?? - const EdgeInsets.all(16); + final EdgeInsetsGeometry effectiveTextPadding = + textPadding ?? const EdgeInsets.all(12); - final TextStyle effectiveTextStyle = textStyle ?? - context.moonTheme?.textAreaTheme.properties.textStyle ?? - MoonTypography.typography.body.text16; + final TextStyle effectiveTextStyle = + textStyle ?? MoonTypography.typography.body.text16; - final TextStyle effectiveHelperTextStyle = helperTextStyle ?? - context.moonTheme?.textAreaTheme.properties.helperTextStyle ?? - MoonTypography.typography.body.text12; + final Color effectiveTextColor = textColor ?? MoonColors.light.textPrimary; - final Duration effectiveTransitionDuration = transitionDuration ?? - context.moonTheme?.textAreaTheme.properties.transitionDuration ?? - MoonTransitions.transitions.defaultTransitionDuration; + final TextStyle resolvedTextStyle = + effectiveTextStyle.copyWith(color: effectiveTextColor); - final Curve effectiveTransitionCurve = transitionCurve ?? - context.moonTheme?.textAreaTheme.properties.transitionCurve ?? - MoonTransitions.transitions.defaultTransitionCurve; - - return MoonFormTextInput( - activeBorderColor: effectiveActiveBorderColor, + return MoonInput.form( + activeBorderColor: activeBorderColor, autocorrect: autocorrect, autofillHints: autofillHints, autofocus: autofocus, autovalidateMode: autovalidateMode, - backgroundColor: effectiveBackgroundColor, - borderRadius: effectiveBorderRadius, + backgroundColor: backgroundColor, + borderRadius: borderRadius, + clipBehavior: clipBehavior ?? Clip.hardEdge, controller: controller, cursorColor: effectiveTextColor, - cursorErrorColor: effectiveErrorColor, - enabled: enabled, + cursorErrorColor: errorColor, + decoration: decoration, enableIMEPersonalizedLearning: enableIMEPersonalizedLearning, enableInteractiveSelection: enableInteractiveSelection, enableSuggestions: enableSuggestions, - errorColor: effectiveErrorColor, + enabled: enabled, errorBuilder: errorBuilder, + errorColor: errorColor, expands: expands, focusNode: focusNode, height: height, helper: helper, - helperPadding: effectiveHelperPadding, - helperTextStyle: effectiveHelperTextStyle, + helperText: helperText, + helperErrorPadding: helperErrorPadding, + helperErrorTextStyle: helperErrorTextStyle, hintText: hintText, - hintTextColor: effectiveHelperTextColor, - hoverBorderColor: effectiveHoverBorderColor, - inactiveBorderColor: effectiveInactiveBorderColor, + hintTextColor: hintTextColor, + hoverColor: hoverColor, + inactiveBorderColor: inactiveBorderColor, initialValue: initialValue, inputFormatters: inputFormatters, keyboardAppearance: keyboardAppearance, keyboardType: TextInputType.multiline, + label: label, + labelPadding: labelPadding, maxLength: maxLength, maxLengthEnforcement: maxLengthEnforcement, maxLines: null, minLines: minLines, onChanged: onChanged, onEditingComplete: onEditingComplete, - onSubmitted: onSubmitted, onSaved: onSaved, + onSubmitted: onSubmitted, onTap: onTap, onTapOutside: onTapOutside, padding: effectiveTextPadding, readOnly: readOnly, restorationId: restorationId, + scribbleEnabled: scribbleEnabled, scrollController: scrollController, scrollPadding: scrollPadding, scrollPhysics: scrollPhysics, - decoration: decoration, showCursor: showCursor, strutStyle: strutStyle, - style: effectiveTextStyle.copyWith(color: effectiveTextColor), + style: resolvedTextStyle, textAlign: textAlign, textAlignVertical: TextAlignVertical.top, textCapitalization: textCapitalization, - textColor: effectiveTextColor, + textColor: textColor, textDirection: textDirection, textInputAction: textInputAction, - transitionCurve: effectiveTransitionCurve, - transitionDuration: effectiveTransitionDuration, + transitionCurve: transitionCurve, + transitionDuration: transitionDuration, validator: validator, ); } diff --git a/lib/src/widgets/text_input/form_text_input.dart b/lib/src/widgets/text_input/form_text_input.dart deleted file mode 100644 index 8a3bc12d..00000000 --- a/lib/src/widgets/text_input/form_text_input.dart +++ /dev/null @@ -1,687 +0,0 @@ -import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle; - -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import 'package:moon_design/src/widgets/text_input/text_input.dart'; - -export 'package:flutter/services.dart' show SmartDashesType, SmartQuotesType; - -typedef MoonFormTextInputValidationStatusCallback = void Function( - String? validationErrorText, -); - -class MoonFormTextInput extends FormField { - final MoonFormTextInputConfiguration configuration; - - /// Creates a Moon Design [MoonFormTextInput] with a [MoonTextInput]. - /// - /// If a [controller] is specified, [initialValue] must be null. - /// If [controller] is null, a [TextEditingController] is automatically - /// created with its `text` initialized to [initialValue] or an empty string. - /// - /// See [MoonTextInput] documentation for details on various parameters. - /// - /// Validator errors take precedence over the provided [errorText]. - MoonFormTextInput({ - super.key, - // Moon Design system properties. - bool hasFloatingLabel = false, - BorderRadiusGeometry? borderRadius, - Color? backgroundColor, - Color? activeBorderColor, - Color? inactiveBorderColor, - Color? errorColor, - Color? errorBorderColor, - Color? hoverBorderColor, - Color? textColor, - Color? hintTextColor, - Decoration? decoration, - double? gap, - double? height, - double? width, - Duration? transitionDuration, - Curve? transitionCurve, - EdgeInsetsGeometry? padding, - EdgeInsetsGeometry? helperPadding, - MoonFormTextInputValidationStatusCallback? validationStatusCallback, - MoonTextInputSize? textInputSize, - String? errorText, - String? hintText, - String? initialValue, - TextStyle? helperTextStyle, - MoonTextInputErrorBuilder? errorBuilder, - Widget? leading, - Widget? trailing, - Widget? helper, - - // Flutter properties. - this.controller, - FocusNode? focusNode, - TextInputType? keyboardType, - TextCapitalization textCapitalization = TextCapitalization.none, - TextInputAction? textInputAction, - TextStyle? style, - StrutStyle? strutStyle, - TextDirection? textDirection, - TextAlign textAlign = TextAlign.start, - TextAlignVertical? textAlignVertical, - bool autofocus = false, - bool readOnly = false, - bool? showCursor, - String obscuringCharacter = '•', - bool obscureText = false, - bool autocorrect = true, - SmartDashesType? smartDashesType, - SmartQuotesType? smartQuotesType, - bool enableSuggestions = true, - MaxLengthEnforcement? maxLengthEnforcement, - int? maxLines = 1, - int? minLines, - bool expands = false, - int? maxLength, - ValueChanged? onChanged, - GestureTapCallback? onTap, - bool onTapAlwaysCalled = false, - TapRegionCallback? onTapOutside, - VoidCallback? onEditingComplete, - ValueChanged? onSubmitted, - super.onSaved, - super.validator, - List? inputFormatters, - bool? enabled, - double cursorWidth = 2.0, - double? cursorHeight, - Radius? cursorRadius, - Color? cursorColor, - Color? cursorErrorColor, - Brightness? keyboardAppearance, - EdgeInsets scrollPadding = const EdgeInsets.all(20.0), - bool? enableInteractiveSelection, - TextSelectionControls? selectionControls, - ScrollPhysics? scrollPhysics, - Iterable? autofillHints, - AutovalidateMode? autovalidateMode, - ScrollController? scrollController, - super.restorationId, - bool enableIMEPersonalizedLearning = true, - MouseCursor? mouseCursor, - EditableTextContextMenuBuilder? contextMenuBuilder = - defaultContextMenuBuilder, - SpellCheckConfiguration? spellCheckConfiguration, - TextMagnifierConfiguration? magnifierConfiguration, - - // Flutter missing properties. - AppPrivateCommandCallback? onAppPrivateCommand, - bool canRequestFocus = true, - bool scribbleEnabled = true, - bool? cursorOpacityAnimates, - Clip clipBehavior = Clip.hardEdge, - ContentInsertionConfiguration? contentInsertionConfiguration, - DragStartBehavior dragStartBehavior = DragStartBehavior.start, - ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.tight, - ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight, - UndoHistoryController? undoController, - }) : assert(obscuringCharacter.length == 1), - assert(maxLines == null || maxLines > 0), - assert(minLines == null || minLines > 0), - assert( - (maxLines == null) || (minLines == null) || (maxLines >= minLines), - "MinLines can not be greater than maxLines", - ), - assert( - !expands || (maxLines == null && minLines == null), - "MinLines and maxLines must be null when 'expands' is true.", - ), - assert( - !obscureText || maxLines == 1, - "Obscured fields cannot be multiline.", - ), - assert( - maxLength == null || - maxLength == MoonTextInput.noMaxLength || - maxLength > 0, - ), - configuration = MoonFormTextInputConfiguration( - activeBorderColor: activeBorderColor, - autocorrect: autocorrect, - autofillHints: autofillHints, - autofocus: autofocus, - autovalidateMode: autovalidateMode ?? AutovalidateMode.disabled, - backgroundColor: backgroundColor, - borderRadius: borderRadius, - canRequestFocus: canRequestFocus, - clipBehavior: clipBehavior, - contentInsertionConfiguration: contentInsertionConfiguration, - contextMenuBuilder: contextMenuBuilder, - controller: controller, - cursorColor: cursorColor, - cursorErrorColor: cursorErrorColor, - cursorHeight: cursorHeight, - cursorOpacityAnimates: cursorOpacityAnimates, - cursorRadius: cursorRadius, - cursorWidth: cursorWidth, - decoration: decoration, - dragStartBehavior: dragStartBehavior, - enabled: enabled ?? true, - enableIMEPersonalizedLearning: enableIMEPersonalizedLearning, - enableInteractiveSelection: - enableInteractiveSelection ?? (!obscureText || !readOnly), - enableSuggestions: enableSuggestions, - errorBorderColor: errorBorderColor, - errorBuilder: errorBuilder, - errorColor: errorColor, - errorText: errorText, - expands: expands, - focusNode: focusNode, - gap: gap, - hasFloatingLabel: hasFloatingLabel, - height: height, - helper: helper, - helperPadding: helperPadding, - helperTextStyle: helperTextStyle, - hintText: hintText, - hintTextColor: hintTextColor, - hoverBorderColor: hoverBorderColor, - inactiveBorderColor: inactiveBorderColor, - initialValue: initialValue, - inputFormatters: inputFormatters, - keyboardAppearance: keyboardAppearance, - keyboardType: keyboardType, - leading: leading, - magnifierConfiguration: magnifierConfiguration, - maxLength: maxLength, - maxLengthEnforcement: maxLengthEnforcement, - maxLines: maxLines, - minLines: minLines, - mouseCursor: mouseCursor, - obscureText: obscureText, - obscuringCharacter: obscuringCharacter, - onAppPrivateCommand: onAppPrivateCommand, - onChanged: onChanged, - onEditingComplete: onEditingComplete, - validationStatusCallback: validationStatusCallback, - onSaved: onSaved, - onSubmitted: onSubmitted, - onTap: onTap, - onTapAlwaysCalled: onTapAlwaysCalled, - onTapOutside: onTapOutside, - padding: padding, - readOnly: readOnly, - restorationId: restorationId, - scribbleEnabled: scribbleEnabled, - scrollController: scrollController, - scrollPadding: scrollPadding, - scrollPhysics: scrollPhysics, - selectionControls: selectionControls, - selectionHeightStyle: selectionHeightStyle, - selectionWidthStyle: selectionWidthStyle, - showCursor: showCursor, - smartDashesType: smartDashesType ?? - (obscureText - ? SmartDashesType.disabled - : SmartDashesType.enabled), - smartQuotesType: smartQuotesType ?? - (obscureText - ? SmartQuotesType.disabled - : SmartQuotesType.enabled), - spellCheckConfiguration: spellCheckConfiguration, - strutStyle: strutStyle, - style: style, - textAlign: textAlign, - textAlignVertical: textAlignVertical, - textCapitalization: textCapitalization, - textColor: textColor, - textDirection: textDirection, - textInputAction: textInputAction, - textInputSize: textInputSize, - trailing: trailing, - transitionCurve: transitionCurve, - transitionDuration: transitionDuration, - undoController: undoController, - validator: validator, - width: width, - ), - super( - initialValue: - controller != null ? controller.text : (initialValue ?? ""), - enabled: enabled ?? true, - autovalidateMode: autovalidateMode ?? AutovalidateMode.disabled, - builder: (FormFieldState field) { - final _MoonFormTextInputState state = - field as _MoonFormTextInputState; - - validationStatusCallback?.call(field.errorText); - - void onChangedHandler(String value) { - field.didChange(value); - if (onChanged != null) { - onChanged(value); - } - } - - return UnmanagedRestorationScope( - bucket: field.bucket, - child: MoonTextInput( - activeBorderColor: activeBorderColor, - autocorrect: autocorrect, - autofillHints: autofillHints, - autofocus: autofocus, - backgroundColor: backgroundColor, - borderRadius: borderRadius, - canRequestFocus: canRequestFocus, - clipBehavior: clipBehavior, - contentInsertionConfiguration: contentInsertionConfiguration, - contextMenuBuilder: contextMenuBuilder, - controller: state._effectiveController, - cursorColor: cursorColor, - cursorErrorColor: cursorErrorColor, - cursorHeight: cursorHeight, - cursorOpacityAnimates: cursorOpacityAnimates, - cursorRadius: cursorRadius, - cursorWidth: cursorWidth, - decoration: decoration, - dragStartBehavior: dragStartBehavior, - enabled: enabled ?? true, - enableIMEPersonalizedLearning: enableIMEPersonalizedLearning, - enableInteractiveSelection: - enableInteractiveSelection ?? (!obscureText || !readOnly), - enableSuggestions: enableSuggestions, - errorBorderColor: errorBorderColor, - errorBuilder: errorBuilder, - errorColor: errorColor, - errorText: field.errorText ?? errorText, - expands: expands, - focusNode: focusNode, - gap: gap, - hasFloatingLabel: hasFloatingLabel, - height: height, - helper: helper, - helperPadding: helperPadding, - helperTextStyle: helperTextStyle, - hintText: hintText, - hintTextColor: hintTextColor, - hoverBorderColor: hoverBorderColor, - inactiveBorderColor: inactiveBorderColor, - initialValue: initialValue, - inputFormatters: inputFormatters, - keyboardAppearance: keyboardAppearance, - keyboardType: keyboardType, - leading: leading, - magnifierConfiguration: magnifierConfiguration, - maxLength: maxLength, - maxLengthEnforcement: maxLengthEnforcement, - maxLines: maxLines, - minLines: minLines, - mouseCursor: mouseCursor, - obscureText: obscureText, - obscuringCharacter: obscuringCharacter, - onAppPrivateCommand: onAppPrivateCommand, - onChanged: onChangedHandler, - onEditingComplete: onEditingComplete, - onSubmitted: onSubmitted, - onTap: onTap, - onTapAlwaysCalled: onTapAlwaysCalled, - onTapOutside: onTapOutside, - padding: padding, - readOnly: readOnly, - restorationId: restorationId, - scribbleEnabled: scribbleEnabled, - scrollController: scrollController, - scrollPadding: scrollPadding, - scrollPhysics: scrollPhysics, - selectionControls: selectionControls, - selectionHeightStyle: selectionHeightStyle, - selectionWidthStyle: selectionWidthStyle, - showCursor: showCursor, - smartDashesType: smartDashesType ?? - (obscureText - ? SmartDashesType.disabled - : SmartDashesType.enabled), - smartQuotesType: smartQuotesType ?? - (obscureText - ? SmartQuotesType.disabled - : SmartQuotesType.enabled), - spellCheckConfiguration: spellCheckConfiguration, - strutStyle: strutStyle, - style: style, - textAlign: textAlign, - textAlignVertical: textAlignVertical, - textCapitalization: textCapitalization, - textColor: textColor, - textDirection: textDirection, - textInputAction: textInputAction, - textInputSize: textInputSize, - trailing: trailing, - transitionCurve: transitionCurve, - transitionDuration: transitionDuration, - undoController: undoController, - width: width, - ), - ); - }, - ); - - /// Controls the input text. - /// - /// If null, this widget will create its own [TextEditingController] and initialize its [TextEditingController.text] - /// with [initialValue]. - final TextEditingController? controller; - - static Widget defaultContextMenuBuilder( - BuildContext context, - EditableTextState editableTextState, - ) { - return AdaptiveTextSelectionToolbar.editableText( - editableTextState: editableTextState, - ); - } - - @override - FormFieldState createState() => _MoonFormTextInputState(); -} - -class _MoonFormTextInputState extends FormFieldState { - RestorableTextEditingController? _controller; - - TextEditingController get _effectiveController => - _moonFormTextInput.controller ?? _controller!.value; - - MoonFormTextInput get _moonFormTextInput => super.widget as MoonFormTextInput; - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - super.restoreState(oldBucket, initialRestore); - if (_controller != null) { - _registerController(); - } - // Update the internal [FormFieldState] value to synchronize with the text editing controller value. - setValue(_effectiveController.text); - } - - void _registerController() { - assert(_controller != null); - registerForRestoration(_controller!, 'controller'); - } - - void _createLocalController([TextEditingValue? value]) { - assert(_controller == null); - _controller = value == null - ? RestorableTextEditingController() - : RestorableTextEditingController.fromValue(value); - if (!restorePending) { - _registerController(); - } - } - - @override - void initState() { - super.initState(); - if (_moonFormTextInput.controller == null) { - _createLocalController( - widget.initialValue != null - ? TextEditingValue(text: widget.initialValue!) - : null, - ); - } else { - _moonFormTextInput.controller!.addListener(_handleControllerChanged); - } - } - - @override - void didUpdateWidget(MoonFormTextInput oldWidget) { - super.didUpdateWidget(oldWidget); - if (_moonFormTextInput.controller != oldWidget.controller) { - oldWidget.controller?.removeListener(_handleControllerChanged); - _moonFormTextInput.controller?.addListener(_handleControllerChanged); - - if (oldWidget.controller != null && - _moonFormTextInput.controller == null) { - _createLocalController(oldWidget.controller!.value); - } - - if (_moonFormTextInput.controller != null) { - setValue(_moonFormTextInput.controller!.text); - if (oldWidget.controller == null) { - unregisterFromRestoration(_controller!); - _controller!.dispose(); - _controller = null; - } - } - } - } - - @override - void dispose() { - _moonFormTextInput.controller?.removeListener(_handleControllerChanged); - _controller?.dispose(); - super.dispose(); - } - - @override - void didChange(String? value) { - super.didChange(value); - - if (_effectiveController.text != value) { - _effectiveController.text = value ?? ""; - } - } - - @override - void reset() { - // SetState is handled in the superclass, no additional call needed here. - _effectiveController.text = widget.initialValue ?? ''; - super.reset(); - } - - void _handleControllerChanged() { - // Suppress changes originating from within this class. - // When a controller is provided, this change listener is registered. In such cases, we may receive notifications - // for changes within this class (e.g., the reset() method), but the FormField value will already be set. - if (_effectiveController.text != value) { - didChange(_effectiveController.text); - } - } -} - -class MoonFormTextInputConfiguration { - // Moon Design System properties. - final bool hasFloatingLabel; - final BorderRadiusGeometry? borderRadius; - final Color? backgroundColor; - final Color? activeBorderColor; - final Color? errorBorderColor; - final Color? inactiveBorderColor; - final Color? errorColor; - final Color? hoverBorderColor; - final Color? textColor; - final Color? hintTextColor; - final Decoration? decoration; - final double? gap; - final double? height; - final double? width; - final Duration? transitionDuration; - final Curve? transitionCurve; - final EdgeInsetsGeometry? padding; - final EdgeInsetsGeometry? helperPadding; - final MoonFormTextInputValidationStatusCallback? validationStatusCallback; - final MoonTextInputSize? textInputSize; - final String? errorText; - final String? hintText; - final String? initialValue; - final TextStyle? helperTextStyle; - final MoonTextInputErrorBuilder? errorBuilder; - final Widget? leading; - final Widget? trailing; - final Widget? helper; - - // Flutter properties. - final TextMagnifierConfiguration? magnifierConfiguration; - final TextEditingController? controller; - final FocusNode? focusNode; - final TextInputType keyboardType; - final TextInputAction? textInputAction; - final TextCapitalization textCapitalization; - final TextStyle? style; - final StrutStyle? strutStyle; - final TextAlign textAlign; - final TextAlignVertical? textAlignVertical; - final TextDirection? textDirection; - final bool autofocus; - final String obscuringCharacter; - final bool obscureText; - final bool autocorrect; - final SmartDashesType smartDashesType; - final SmartQuotesType smartQuotesType; - final bool enableSuggestions; - final int? maxLines; - final int? minLines; - final bool expands; - final bool readOnly; - final bool? showCursor; - final int? maxLength; - final MaxLengthEnforcement? maxLengthEnforcement; - final ValueChanged? onChanged; - final VoidCallback? onEditingComplete; - final ValueChanged? onSubmitted; - final AppPrivateCommandCallback? onAppPrivateCommand; - final List? inputFormatters; - final bool enabled; - final double cursorWidth; - final double? cursorHeight; - final Radius? cursorRadius; - final bool? cursorOpacityAnimates; - final Color? cursorColor; - final Color? cursorErrorColor; - final ui.BoxHeightStyle selectionHeightStyle; - final ui.BoxWidthStyle selectionWidthStyle; - final Brightness? keyboardAppearance; - final EdgeInsets scrollPadding; - final bool enableInteractiveSelection; - final TextSelectionControls? selectionControls; - final DragStartBehavior dragStartBehavior; - final GestureTapCallback? onTap; - final bool onTapAlwaysCalled; - final TapRegionCallback? onTapOutside; - final MouseCursor? mouseCursor; - final ScrollPhysics? scrollPhysics; - final ScrollController? scrollController; - final Iterable? autofillHints; - final Clip clipBehavior; - final String? restorationId; - final bool scribbleEnabled; - final bool enableIMEPersonalizedLearning; - final ContentInsertionConfiguration? contentInsertionConfiguration; - final EditableTextContextMenuBuilder? contextMenuBuilder; - final bool canRequestFocus; - final UndoHistoryController? undoController; - final SpellCheckConfiguration? spellCheckConfiguration; - final FormFieldSetter? onSaved; - final FormFieldValidator? validator; - final AutovalidateMode autovalidateMode; - - const MoonFormTextInputConfiguration({ - // Moon Design System properties. - this.hasFloatingLabel = false, - this.borderRadius, - this.backgroundColor, - this.activeBorderColor, - this.errorBorderColor, - this.inactiveBorderColor, - this.errorColor, - this.hoverBorderColor, - this.textColor, - this.hintTextColor, - this.decoration, - this.gap, - this.height, - this.width, - this.transitionDuration, - this.transitionCurve, - this.padding, - this.helperPadding, - this.validationStatusCallback, - this.textInputSize, - this.errorText, - this.hintText, - this.initialValue, - this.helperTextStyle, - this.errorBuilder, - this.leading, - this.trailing, - this.helper, - - // Flutter properties. - this.controller, - this.focusNode, - this.undoController, - TextInputType? keyboardType, - this.textInputAction, - this.textCapitalization = TextCapitalization.none, - this.style, - this.strutStyle, - this.textAlign = TextAlign.start, - this.textAlignVertical, - this.textDirection, - this.readOnly = false, - this.showCursor, - this.autofocus = false, - this.obscuringCharacter = '•', - this.obscureText = false, - this.autocorrect = true, - SmartDashesType? smartDashesType, - SmartQuotesType? smartQuotesType, - this.enableSuggestions = true, - this.maxLines = 1, - this.minLines, - this.expands = false, - this.maxLength, - this.maxLengthEnforcement, - this.onChanged, - this.onEditingComplete, - this.onSubmitted, - this.onAppPrivateCommand, - this.inputFormatters, - this.enabled = true, - this.cursorWidth = 2.0, - this.cursorHeight, - this.cursorRadius, - this.cursorOpacityAnimates, - this.cursorColor, - this.cursorErrorColor, - this.selectionHeightStyle = ui.BoxHeightStyle.tight, - this.selectionWidthStyle = ui.BoxWidthStyle.tight, - this.keyboardAppearance, - this.scrollPadding = const EdgeInsets.all(20.0), - this.dragStartBehavior = DragStartBehavior.start, - bool? enableInteractiveSelection, - this.selectionControls, - this.onTap, - this.onTapAlwaysCalled = false, - this.onTapOutside, - this.mouseCursor, - this.scrollController, - this.scrollPhysics, - this.autofillHints = const [], - this.contentInsertionConfiguration, - this.clipBehavior = Clip.hardEdge, - this.restorationId, - this.scribbleEnabled = true, - this.enableIMEPersonalizedLearning = true, - this.contextMenuBuilder = MoonFormTextInput.defaultContextMenuBuilder, - this.canRequestFocus = true, - this.spellCheckConfiguration, - this.magnifierConfiguration, - this.onSaved, - this.validator, - this.autovalidateMode = AutovalidateMode.disabled, - }) : smartDashesType = smartDashesType ?? - (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled), - smartQuotesType = smartQuotesType ?? - (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled), - keyboardType = keyboardType ?? - (maxLines == 1 ? TextInputType.text : TextInputType.multiline), - enableInteractiveSelection = - enableInteractiveSelection ?? (!readOnly || !obscureText); -} diff --git a/lib/src/widgets/text_input/text_input.dart b/lib/src/widgets/text_input/text_input.dart deleted file mode 100644 index 2f44992c..00000000 --- a/lib/src/widgets/text_input/text_input.dart +++ /dev/null @@ -1,1809 +0,0 @@ -import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/services.dart'; - -import 'package:moon_design/src/theme/text_input/text_input_size_properties.dart'; -import 'package:moon_design/src/theme/text_input/text_input_sizes.dart'; -import 'package:moon_design/src/theme/theme.dart'; -import 'package:moon_design/src/theme/tokens/borders.dart'; -import 'package:moon_design/src/theme/tokens/opacities.dart'; -import 'package:moon_design/src/theme/tokens/sizes.dart'; -import 'package:moon_design/src/theme/tokens/tokens.dart'; -import 'package:moon_design/src/theme/tokens/typography/typography.dart'; -import 'package:moon_design/src/utils/extensions.dart'; -import 'package:moon_design/src/utils/squircle/squircle_border.dart'; -import 'package:moon_design/src/widgets/common/border_container.dart'; -import 'package:moon_design/src/widgets/common/error_message_widgets.dart'; -import 'package:moon_tokens/moon_tokens.dart'; - -export 'package:flutter/services.dart' - show - SmartDashesType, - SmartQuotesType, - TextCapitalization, - TextInputAction, - TextInputType; - -enum MoonTextInputSize { - sm, - md, - lg, - xl, -} - -typedef MoonTextInputErrorBuilder = Widget Function( - BuildContext context, - String? errorText, -); - -class MoonTextInput extends StatefulWidget { - /// If [maxLength] is set to this value, only the "current input length" part - /// of the character counter is displayed. - static const int noMaxLength = -1; - - static Widget _defaultContextMenuBuilder( - BuildContext context, - EditableTextState editableTextState, - ) { - return AdaptiveTextSelectionToolbar.editableText( - editableTextState: editableTextState, - ); - } - - // Moon Design System properties. - /// Whether the text input has floating label. - final bool hasFloatingLabel; - - /// The border radius of the text input. - final BorderRadiusGeometry? borderRadius; - - /// The background color of the text input. - final Color? backgroundColor; - - /// The border color of the active or focused text input. - final Color? activeBorderColor; - - /// The border color of the text input in error state. - final Color? errorBorderColor; - - /// The border color of the inactive text input. - final Color? inactiveBorderColor; - - /// The color of the text input in error state. - final Color? errorColor; - - /// The border color of the text input on hover. - final Color? hoverBorderColor; - - /// The text color of the text input. - final Color? textColor; - - /// The text color of the text input hint. - final Color? hintTextColor; - - /// The custom decoration of the text input. - final Decoration? decoration; - - /// The gap between the [leading] widget, text input and [trailing] widget. - final double? gap; - - /// The height of the text input (does not include the space taken by [MoonTextInput.errorBuilder]). - final double? height; - - /// The width of the text input. - final double? width; - - /// The duration of the text input transition animation (enable and disable). - final Duration? transitionDuration; - - /// The curve of the text input transition animation (enable and disable). - final Curve? transitionCurve; - - /// The padding of the text input. - final EdgeInsetsGeometry? padding; - - /// The padding of the [helper] and [errorBuilder] widget. - final EdgeInsetsGeometry? helperPadding; - - /// The size of the text input. - final MoonTextInputSize? textInputSize; - - /// The error text can be used to force text input into an error state (useful for asynchronous errors). - /// - /// The validator errors take precedence over the provided [errorText]. - final String? errorText; - - /// The hint text to display in the text area. - final String? hintText; - - /// The initial value of the text input. If [controller] is provided, - /// this value is ignored and [controller]'s value is used. - final String? initialValue; - - /// The text style of the [helper] widget or error state text. - final TextStyle? helperTextStyle; - - /// A builder to build the text input error widget. - final MoonTextInputErrorBuilder? errorBuilder; - - /// The widget to display before the text input. - final Widget? leading; - - /// The widget to display after the text input. - final Widget? trailing; - - /// The widget to display below the text input. Not displayed in error state. - final Widget? helper; - - // Flutter properties. - - /// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro} - /// - /// {@macro flutter.widgets.magnifier.intro} - /// - /// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details} - /// - /// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier] - /// on Android, and builds nothing on all other platforms. If it is desired to - /// suppress the magnifier, consider passing [TextMagnifierConfiguration.disabled]. - /// - /// {@tool dartpad} - /// This sample demonstrates how to customize the magnifier that this text field uses. - /// - /// ** See code in examples/api/lib/widgets/text_magnifier/text_magnifier.0.dart ** - /// {@end-tool} - final TextMagnifierConfiguration? magnifierConfiguration; - - /// Controls the input text. - /// - /// If null, this widget will create its own [TextEditingController]. - final TextEditingController? controller; - - /// Defines the keyboard focus for this widget. - /// - /// The [focusNode] is a long-lived object that is typically managed by a - /// [StatefulWidget] parent. See [FocusNode] for more information. - /// - /// To give the keyboard focus to this widget, provide a [focusNode] and then - /// use the current [FocusScope] to request the focus: - /// - /// ```dart - /// FocusScope.of(context).requestFocus(myFocusNode); - /// ``` - /// - /// This happens automatically when the widget is tapped. - /// - /// To be notified when the widget gains or loses the focus, add a listener - /// to the [focusNode]: - /// - /// ```dart - /// myFocusNode.addListener(() { print(myFocusNode.hasFocus); }); - /// ``` - /// - /// If null, this widget will create its own [FocusNode]. - /// - /// ## Keyboard - /// - /// Requesting the focus will typically cause the keyboard to be shown - /// if it is not showing already. - /// - /// On Android, the user can hide the keyboard - without changing the focus - - /// with the system back button. They can restore the keyboard's visibility - /// by tapping on a text field. The user might hide the keyboard and - /// switch to a physical keyboard, or they might just need to get it - /// out of the way for a moment, to expose something it's - /// obscuring. In this case requesting the focus again will not - /// cause the focus to change, and will not make the keyboard visible. - /// - /// This widget builds an [EditableText] and will ensure that the keyboard is - /// showing when it is tapped by calling [EditableTextState.requestKeyboard()]. - final FocusNode? focusNode; - - /// {@macro flutter.widgets.editableText.keyboardType} - final TextInputType keyboardType; - - /// {@template flutter.widgets.TextField.textInputAction} - /// The type of action button to use for the keyboard. - /// - /// Defaults to [TextInputAction.newline] if [keyboardType] is - /// [TextInputType.multiline] and [TextInputAction.done] otherwise. - /// {@endtemplate} - final TextInputAction? textInputAction; - - /// {@macro flutter.widgets.editableText.textCapitalization} - final TextCapitalization textCapitalization; - - /// The text style of the input text. - /// - /// This text style is also used as the base style for the [decoration]. - /// - /// If null, defaults to 'titleMedium' text style from the current [Theme]. - final TextStyle? style; - - /// {@macro flutter.widgets.editableText.strutStyle} - final StrutStyle? strutStyle; - - /// {@macro flutter.widgets.editableText.textAlign} - final TextAlign textAlign; - - /// {@macro flutter.material.InputDecorator.textAlignVertical} - final TextAlignVertical? textAlignVertical; - - /// {@macro flutter.widgets.editableText.textDirection} - final TextDirection? textDirection; - - /// {@macro flutter.widgets.editableText.autofocus} - final bool autofocus; - - /// {@macro flutter.widgets.editableText.obscuringCharacter} - final String obscuringCharacter; - - /// {@macro flutter.widgets.editableText.obscureText} - final bool obscureText; - - /// {@macro flutter.widgets.editableText.autocorrect} - final bool autocorrect; - - /// {@macro flutter.services.TextInputConfiguration.smartDashesType} - final SmartDashesType smartDashesType; - - /// {@macro flutter.services.TextInputConfiguration.smartQuotesType} - final SmartQuotesType smartQuotesType; - - /// {@macro flutter.services.TextInputConfiguration.enableSuggestions} - final bool enableSuggestions; - - /// {@macro flutter.widgets.editableText.maxLines} - /// * [expands], which determines whether the field should fill the height of - /// its parent. - final int? maxLines; - - /// {@macro flutter.widgets.editableText.minLines} - /// * [expands], which determines whether the field should fill the height of - /// its parent. - final int? minLines; - - /// {@macro flutter.widgets.editableText.expands} - final bool expands; - - /// {@macro flutter.widgets.editableText.readOnly} - final bool readOnly; - - /// Configuration of toolbar options. - /// - /// If not set, 'select all' and 'paste' default to enabled. - /// 'Copy' and 'cut' will be disabled if [obscureText] is true. - /// If [readOnly] is true, 'paste' and 'cut' will be disabled regardless. - - /// {@macro flutter.widgets.editableText.showCursor} - final bool? showCursor; - - /// The maximum number of characters (unicode grapheme clusters) to allow in the text field. - /// - /// If set, a character counter will be displayed below the - /// field showing how many characters have been entered. If set to a number - /// greater than 0, it will also display the maximum number allowed. If set - /// to [MoonTextInput.noMaxLength] then only the current character count is displayed. - /// - /// After [maxLength] characters have been input, additional input - /// is ignored, unless [maxLengthEnforcement] is set to [MaxLengthEnforcement.none]. - /// - /// The text field enforces the length with a [LengthLimitingTextInputFormatter], - /// which is evaluated after the supplied [inputFormatters], if any. - /// - /// This value must be either null, [MoonTextInput.noMaxLength], or greater than 0. - /// If null (the default) then there is no limit to the number of characters - /// that can be entered. If set to [MoonTextInput.noMaxLength], then no limit will - /// be enforced, but the number of characters entered will still be displayed. - /// - /// Whitespace characters (e.g. newline, space, tab) are included in the character count. - /// - /// If [maxLengthEnforcement] is [MaxLengthEnforcement.none], then more than - /// [maxLength] characters may be entered, but the error counter and divider - /// will switch to the [decoration]'s [InputDecoration.errorStyle] when the limit is exceeded. - /// - /// {@macro flutter.services.lengthLimitingTextInputFormatter.maxLength} - final int? maxLength; - - /// Determines how the [maxLength] limit should be enforced. - /// - /// {@macro flutter.services.textFormatter.effectiveMaxLengthEnforcement} - /// - /// {@macro flutter.services.textFormatter.maxLengthEnforcement} - final MaxLengthEnforcement? maxLengthEnforcement; - - /// {@macro flutter.widgets.editableText.onChanged} - /// - /// See also: - /// - /// * [inputFormatters], which are called before [onChanged] - /// runs and can validate and change ("format") the input value. - /// * [onEditingComplete], [onSubmitted]: - /// which are more specialized input change notifications. - final ValueChanged? onChanged; - - /// {@macro flutter.widgets.editableText.onEditingComplete} - final VoidCallback? onEditingComplete; - - /// {@macro flutter.widgets.editableText.onSubmitted} - /// - /// See also: - /// - /// * [TextInputAction.next] and [TextInputAction.previous], which - /// automatically shift the focus to the next/previous focusable item when - /// the user is done editing. - final ValueChanged? onSubmitted; - - /// {@macro flutter.widgets.editableText.onAppPrivateCommand} - final AppPrivateCommandCallback? onAppPrivateCommand; - - /// {@macro flutter.widgets.editableText.inputFormatters} - final List? inputFormatters; - - /// If false the text field is "disabled": it ignores taps and its - /// [decoration] is rendered in grey. - /// - /// If non-null this property overrides the [decoration]'s - /// [InputDecoration.enabled] property. - final bool enabled; - - /// {@macro flutter.widgets.editableText.cursorWidth} - final double cursorWidth; - - /// {@macro flutter.widgets.editableText.cursorHeight} - final double? cursorHeight; - - /// {@macro flutter.widgets.editableText.cursorRadius} - final Radius? cursorRadius; - - /// {@macro flutter.widgets.editableText.cursorOpacityAnimates} - final bool? cursorOpacityAnimates; - - /// The color of the cursor. - /// - /// The cursor indicates the current location of text insertion point in - /// the field. - final Color? cursorColor; - - /// The color of the cursor when the [MoonTextInput] is showing an error. - final Color? cursorErrorColor; - - /// Controls how tall the selection highlight boxes are computed to be. - /// - /// See [ui.BoxHeightStyle] for details on available styles. - final ui.BoxHeightStyle selectionHeightStyle; - - /// Controls how wide the selection highlight boxes are computed to be. - /// - /// See [ui.BoxWidthStyle] for details on available styles. - final ui.BoxWidthStyle selectionWidthStyle; - - /// The appearance of the keyboard. - /// - /// This setting is only honored on iOS devices. - /// - /// If unset, defaults to [ThemeData.brightness]. - final Brightness? keyboardAppearance; - - /// {@macro flutter.widgets.editableText.scrollPadding} - final EdgeInsets scrollPadding; - - /// {@macro flutter.widgets.editableText.enableInteractiveSelection} - final bool enableInteractiveSelection; - - /// {@macro flutter.widgets.editableText.selectionControls} - final TextSelectionControls? selectionControls; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.material.textfield.onTap} - /// Called for the first tap in a series of taps. - /// - /// The text field builds a [GestureDetector] to handle input events like tap, - /// to trigger focus requests, to move the caret, adjust the selection, etc. - /// Handling some of those events by wrapping the text field with a competing - /// GestureDetector is problematic. - /// - /// To unconditionally handle taps, without interfering with the text field's - /// internal gesture detector, provide this callback. - /// - /// If the text field is created with [enabled] false, taps will not be - /// recognized. - /// - /// To be notified when the text field gains or loses the focus, provide a - /// [focusNode] and add a listener to that. - /// - /// To listen to arbitrary pointer events without competing with the - /// text field's internal gesture detector, use a [Listener]. - /// {@endtemplate} - /// - /// If [onTapAlwaysCalled] is enabled, this will also be called for consecutive - /// taps. - final GestureTapCallback? onTap; - - /// Whether [onTap] should be called for every tap. - /// - /// Defaults to false, so [onTap] is only called for each distinct tap. When - /// enabled, [onTap] is called for every tap including consecutive taps. - final bool onTapAlwaysCalled; - - /// {@macro flutter.widgets.editableText.onTapOutside} - /// - /// {@tool dartpad} - /// This example shows how to use a 'TextFieldTapRegion' to wrap a set of - /// "spinner" buttons that increment and decrement a value in the [MoonTextInput] - /// without causing the text field to lose keyboard focus. - /// - /// This example includes a generic 'SpinnerField' class that you can copy - /// into your own project and customize. - /// - /// ** See code in examples/api/lib/widgets/tap_region/text_field_tap_region.0.dart ** - /// {@end-tool} - /// - /// See also: - /// - /// * [TapRegion] for how the region group is determined. - final TapRegionCallback? onTapOutside; - - /// The cursor for a mouse pointer when it enters or is hovering over the - /// widget. - final MouseCursor? mouseCursor; - - /// {@macro flutter.widgets.editableText.scrollPhysics} - final ScrollPhysics? scrollPhysics; - - /// {@macro flutter.widgets.editableText.scrollController} - final ScrollController? scrollController; - - /// {@macro flutter.widgets.editableText.autofillHints} - /// {@macro flutter.services.AutofillConfiguration.autofillHints} - final Iterable? autofillHints; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - /// {@template flutter.material.textfield.restorationId} - /// Restoration ID to save and restore the state of the text field. - /// - /// If non-null, the text field will persist and restore its current scroll - /// offset and - if no [controller] has been provided - the content of the - /// text field. If a [controller] has been provided, it is the responsibility - /// of the owner of that controller to persist and restore it, e.g. by using - /// a [RestorableTextEditingController]. - /// - /// The state of this widget is persisted in a [RestorationBucket] claimed - /// from the surrounding [RestorationScope] using the provided restoration ID. - /// - /// See also: - /// - /// * [RestorationManager], which explains how state restoration works in - /// Flutter. - /// {@endtemplate} - final String? restorationId; - - /// {@macro flutter.widgets.editableText.scribbleEnabled} - final bool scribbleEnabled; - - /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning} - final bool enableIMEPersonalizedLearning; - - /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} - final ContentInsertionConfiguration? contentInsertionConfiguration; - - /// {@macro flutter.widgets.EditableText.contextMenuBuilder} - /// - /// If not provided, will build a default menu based on the platform. - /// - /// See also: - /// - /// * [AdaptiveTextSelectionToolbar], which is built by default. - final EditableTextContextMenuBuilder? contextMenuBuilder; - - /// Determines whether this text field can request the primary focus. - /// - /// Defaults to true. If false, the text field will not request focus - /// when tapped, or when its context menu is displayed. If false it will not - /// be possible to move the focus to the text field with tab key. - final bool canRequestFocus; - - /// {@macro flutter.widgets.undoHistory.controller} - final UndoHistoryController? undoController; - - /// {@macro flutter.widgets.EditableText.spellCheckConfiguration} - /// - /// If [SpellCheckConfiguration.misspelledTextStyle] is not specified in this - /// configuration, then [materialMisspelledTextStyle] is used by default. - final SpellCheckConfiguration? spellCheckConfiguration; - - /// Creates a Moon Design text input. - /// - /// The [maxLines] property can be set to null to remove the restriction on - /// the number of lines. By default, it is one, meaning this is a single-line - /// text field. [maxLines] must not be zero. - /// - /// The [maxLength] property is set to null by default, which means the - /// number of characters allowed in the text field is not restricted. If - /// [maxLength] is set a character counter will be displayed below the - /// field showing how many characters have been entered. If the value is - /// set to a positive integer it will also display the maximum allowed - /// number of characters to be entered. If the value is set to - /// [MoonTextInput.noMaxLength] then only the current length is displayed. - /// - /// After [maxLength] characters have been input, additional input - /// is ignored, unless [maxLengthEnforcement] is set to - /// [MaxLengthEnforcement.none]. - /// The text field enforces the length with a [LengthLimitingTextInputFormatter], - /// which is evaluated after the supplied [inputFormatters], if any. - /// The [maxLength] value must be either null or greater than zero. - /// - /// If [maxLengthEnforcement] is set to [MaxLengthEnforcement.none], then more - /// than [maxLength] characters may be entered, and the error counter and - /// divider will switch to the [decoration].errorStyle when the limit is - /// exceeded. - /// - /// The text cursor is not shown if [showCursor] is false or if [showCursor] - /// is null (the default) and [readOnly] is true. - /// - /// The [selectionHeightStyle] and [selectionWidthStyle] properties allow - /// changing the shape of the selection highlighting. These properties default - /// to [ui.BoxHeightStyle.tight] and [ui.BoxWidthStyle.tight] respectively and - /// must not be null. - /// - /// The [textAlign], [autofocus], [obscureText], [readOnly], [autocorrect], - /// [scrollPadding], [maxLines], [maxLength], [selectionHeightStyle], - /// [selectionWidthStyle], [enableSuggestions], and - /// [enableIMEPersonalizedLearning] arguments must not be null. - /// - /// See also: - /// - /// * [maxLength], which discusses the precise meaning of "number of - /// characters" and how it may differ from the intuitive meaning. - const MoonTextInput({ - // Moon Design System properties. - this.hasFloatingLabel = false, - this.borderRadius, - this.backgroundColor, - this.activeBorderColor, - this.errorBorderColor, - this.inactiveBorderColor, - this.errorColor, - this.hoverBorderColor, - this.textColor, - this.hintTextColor, - this.decoration, - this.gap, - this.height, - this.width, - this.transitionDuration, - this.transitionCurve, - this.padding, - this.helperPadding, - this.textInputSize, - this.errorText, - this.hintText, - this.initialValue, - this.helperTextStyle, - this.errorBuilder, - this.leading, - this.trailing, - this.helper, - - // Flutter properties. - super.key, - this.controller, - this.focusNode, - this.undoController, - TextInputType? keyboardType, - this.textInputAction, - this.textCapitalization = TextCapitalization.none, - this.style, - this.strutStyle, - this.textAlign = TextAlign.start, - this.textAlignVertical, - this.textDirection, - this.readOnly = false, - this.showCursor, - this.autofocus = false, - this.obscuringCharacter = '•', - this.obscureText = false, - this.autocorrect = true, - SmartDashesType? smartDashesType, - SmartQuotesType? smartQuotesType, - this.enableSuggestions = true, - this.maxLines = 1, - this.minLines, - this.expands = false, - this.maxLength, - this.maxLengthEnforcement, - this.onChanged, - this.onEditingComplete, - this.onSubmitted, - this.onAppPrivateCommand, - this.inputFormatters, - this.enabled = true, - this.cursorWidth = 2.0, - this.cursorHeight, - this.cursorRadius, - this.cursorOpacityAnimates, - this.cursorColor, - this.cursorErrorColor, - this.selectionHeightStyle = ui.BoxHeightStyle.tight, - this.selectionWidthStyle = ui.BoxWidthStyle.tight, - this.keyboardAppearance, - this.scrollPadding = const EdgeInsets.all(20.0), - this.dragStartBehavior = DragStartBehavior.start, - bool? enableInteractiveSelection, - this.selectionControls, - this.onTap, - this.onTapAlwaysCalled = false, - this.onTapOutside, - this.mouseCursor, - this.scrollController, - this.scrollPhysics, - this.autofillHints = const [], - this.contentInsertionConfiguration, - this.clipBehavior = Clip.hardEdge, - this.restorationId, - this.scribbleEnabled = true, - this.enableIMEPersonalizedLearning = true, - this.contextMenuBuilder = _defaultContextMenuBuilder, - this.canRequestFocus = true, - this.spellCheckConfiguration, - this.magnifierConfiguration, - }) : assert(obscuringCharacter.length == 1), - assert(maxLines == null || maxLines > 0), - assert(minLines == null || minLines > 0), - assert( - (maxLines == null) || (minLines == null) || (maxLines >= minLines), - "MinLines can't be greater than maxLines.", - ), - assert( - !expands || !hasFloatingLabel, - 'Text input cannot both expand and have a floating label.', - ), - assert( - !expands || (maxLines == null && minLines == null), - 'MinLines and maxLines must be null when expands is true.', - ), - assert( - !obscureText || maxLines == 1, - 'Obscured fields cannot be multiline.', - ), - assert( - maxLength == null || - maxLength == MoonTextInput.noMaxLength || - maxLength > 0, - ), - // Assert the following to prevent unexpected changes in the user's set value. - assert( - !identical(textInputAction, TextInputAction.newline) || - maxLines == 1 || - !identical(keyboardType, TextInputType.text), - 'Use keyboardType TextInputType.multiline when using TextInputAction.newline on a multiline MoonTextInput.', - ), - smartDashesType = smartDashesType ?? - (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled), - smartQuotesType = smartQuotesType ?? - (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled), - keyboardType = keyboardType ?? - (maxLines == 1 ? TextInputType.text : TextInputType.multiline), - enableInteractiveSelection = - enableInteractiveSelection ?? (!readOnly || !obscureText); - - /// {@macro flutter.widgets.editableText.selectionEnabled} - bool get selectionEnabled => enableInteractiveSelection; - - @override - State createState() => _MoonTextInputState(); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add( - DiagnosticsProperty( - 'controller', - controller, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty( - 'focusNode', - focusNode, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty( - 'undoController', - undoController, - defaultValue: null, - ), - ); - properties - .add(DiagnosticsProperty('enabled', enabled, defaultValue: null)); - properties.add( - DiagnosticsProperty( - 'keyboardType', - keyboardType, - defaultValue: TextInputType.text, - ), - ); - properties.add( - DiagnosticsProperty('style', style, defaultValue: null), - ); - properties.add( - DiagnosticsProperty('autofocus', autofocus, defaultValue: false), - ); - properties.add( - DiagnosticsProperty( - 'obscuringCharacter', - obscuringCharacter, - defaultValue: '•', - ), - ); - properties.add( - DiagnosticsProperty( - 'obscureText', - obscureText, - defaultValue: false, - ), - ); - properties.add( - DiagnosticsProperty( - 'autocorrect', - autocorrect, - defaultValue: true, - ), - ); - properties.add( - EnumProperty( - 'smartDashesType', - smartDashesType, - defaultValue: - obscureText ? SmartDashesType.disabled : SmartDashesType.enabled, - ), - ); - properties.add( - EnumProperty( - 'smartQuotesType', - smartQuotesType, - defaultValue: - obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled, - ), - ); - properties.add( - DiagnosticsProperty( - 'enableSuggestions', - enableSuggestions, - defaultValue: true, - ), - ); - properties.add(IntProperty('maxLines', maxLines, defaultValue: 1)); - properties.add(IntProperty('minLines', minLines, defaultValue: null)); - properties.add( - DiagnosticsProperty('expands', expands, defaultValue: false), - ); - properties.add(IntProperty('maxLength', maxLength, defaultValue: null)); - properties.add( - EnumProperty( - 'maxLengthEnforcement', - maxLengthEnforcement, - defaultValue: null, - ), - ); - properties.add( - EnumProperty( - 'textInputAction', - textInputAction, - defaultValue: null, - ), - ); - properties.add( - EnumProperty( - 'textCapitalization', - textCapitalization, - defaultValue: TextCapitalization.none, - ), - ); - properties.add( - EnumProperty( - 'textAlign', - textAlign, - defaultValue: TextAlign.start, - ), - ); - properties.add( - DiagnosticsProperty( - 'textAlignVertical', - textAlignVertical, - defaultValue: null, - ), - ); - properties.add( - EnumProperty( - 'textDirection', - textDirection, - defaultValue: null, - ), - ); - properties - .add(DoubleProperty('cursorWidth', cursorWidth, defaultValue: 2.0)); - properties - .add(DoubleProperty('cursorHeight', cursorHeight, defaultValue: null)); - properties.add( - DiagnosticsProperty( - 'cursorRadius', - cursorRadius, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty( - 'cursorOpacityAnimates', - cursorOpacityAnimates, - defaultValue: null, - ), - ); - properties - .add(ColorProperty('cursorColor', cursorColor, defaultValue: null)); - properties.add( - ColorProperty( - 'cursorErrorColor', - cursorErrorColor, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty( - 'keyboardAppearance', - keyboardAppearance, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty( - 'scrollPadding', - scrollPadding, - defaultValue: const EdgeInsets.all(20.0), - ), - ); - properties.add( - FlagProperty( - 'selectionEnabled', - value: selectionEnabled, - defaultValue: true, - ifFalse: 'selection disabled', - ), - ); - properties.add( - DiagnosticsProperty( - 'selectionControls', - selectionControls, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty( - 'scrollController', - scrollController, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty( - 'scrollPhysics', - scrollPhysics, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty( - 'clipBehavior', - clipBehavior, - defaultValue: Clip.hardEdge, - ), - ); - properties.add( - DiagnosticsProperty( - 'scribbleEnabled', - scribbleEnabled, - defaultValue: true, - ), - ); - properties.add( - DiagnosticsProperty( - 'enableIMEPersonalizedLearning', - enableIMEPersonalizedLearning, - defaultValue: true, - ), - ); - properties.add( - DiagnosticsProperty( - 'spellCheckConfiguration', - spellCheckConfiguration, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty>( - 'contentCommitMimeTypes', - contentInsertionConfiguration?.allowedMimeTypes ?? const [], - defaultValue: contentInsertionConfiguration == null - ? const [] - : kDefaultContentInsertionMimeTypes, - ), - ); - } -} - -class _MoonTextInputState extends State - with RestorationMixin - implements TextSelectionGestureDetectorBuilderDelegate, AutofillClient { - @override - final GlobalKey editableTextKey = - GlobalKey(); - - @override - late bool forcePressEnabled; - - late _MoonTextInputSelectionGestureDetectorBuilder - _selectionGestureDetectorBuilder; - - RestorableTextEditingController? _controller; - FocusNode? _focusNode; - bool _isHovering = false; - bool _showSelectionHandles = false; - - bool get _isEnabled => widget.enabled; - - bool get _hasError => _hasIntrinsicError || widget.errorText != null; - - bool get _hasFocus => _effectiveFocusNode.hasFocus; - - int get _currentLength => _effectiveController.value.text.characters.length; - - EditableTextState? get _editableText => editableTextKey.currentState; - - FocusNode get _effectiveFocusNode => - widget.focusNode ?? (_focusNode ??= FocusNode()); - - TextEditingController get _effectiveController => - widget.controller ?? _controller!.value; - - bool get _canRequestFocus { - final NavigationMode mode = - MediaQuery.maybeNavigationModeOf(context) ?? NavigationMode.traditional; - switch (mode) { - case NavigationMode.traditional: - return widget.canRequestFocus && _isEnabled; - case NavigationMode.directional: - return true; - } - } - - bool get _hasIntrinsicError => - widget.maxLength != null && - widget.maxLength! > 0 && - (widget.controller == null - ? !restorePending && - _effectiveController.value.text.characters.length > - widget.maxLength! - : _effectiveController.value.text.characters.length > - widget.maxLength!); - - MaxLengthEnforcement get _effectiveMaxLengthEnforcement => - widget.maxLengthEnforcement ?? - LengthLimitingTextInputFormatter.getDefaultMaxLengthEnforcement( - Theme.of(context).platform, - ); - - Set get _materialState { - return { - if (!_isEnabled) WidgetState.disabled, - if (_isHovering) WidgetState.hovered, - if (_hasFocus) WidgetState.focused, - if (_hasError) WidgetState.error, - }; - } - - @override - String? get restorationId => widget.restorationId; - - @override - String get autofillId => _editableText!.autofillId; - - @override - bool get selectionEnabled => widget.selectionEnabled; - - @override - TextInputConfiguration get textInputConfiguration { - final List? autofillHints = - widget.autofillHints?.toList(growable: false); - final AutofillConfiguration autofillConfiguration = autofillHints != null - ? AutofillConfiguration( - uniqueIdentifier: autofillId, - autofillHints: autofillHints, - currentEditingValue: _effectiveController.value, - ) - : AutofillConfiguration.disabled; - - return _editableText!.textInputConfiguration - .copyWith(autofillConfiguration: autofillConfiguration); - } - - void _createLocalController([TextEditingValue? value]) { - assert(_controller == null); - - _controller = value == null - ? RestorableTextEditingController() - : RestorableTextEditingController.fromValue(value); - - if (!restorePending) _registerController(); - } - - void _handleFocusChanged() { - setState(() { - // Rebuilds the widget on focus change to toggle text selection highlight. - }); - } - - void _handleSelectionChanged( - TextSelection selection, - SelectionChangedCause? cause, - ) { - final bool willShowSelectionHandles = _shouldShowSelectionHandles(cause); - - if (willShowSelectionHandles != _showSelectionHandles) { - setState(() => _showSelectionHandles = willShowSelectionHandles); - } - - switch (Theme.of(context).platform) { - case TargetPlatform.iOS: - case TargetPlatform.macOS: - case TargetPlatform.linux: - case TargetPlatform.windows: - case TargetPlatform.fuchsia: - case TargetPlatform.android: - if (cause == SelectionChangedCause.longPress) { - _editableText?.bringIntoView(selection.extent); - } - } - - switch (Theme.of(context).platform) { - case TargetPlatform.iOS: - case TargetPlatform.fuchsia: - case TargetPlatform.android: - break; - case TargetPlatform.macOS: - case TargetPlatform.linux: - case TargetPlatform.windows: - if (cause == SelectionChangedCause.drag) _editableText?.hideToolbar(); - } - } - - void _handleHover(bool hovering) { - if (_isEnabled && hovering != _isHovering) { - setState(() => _isHovering = hovering); - } - } - - /// Toggle the toolbar when a selection handle is tapped. - void _handleSelectionHandleTapped() { - if (_effectiveController.selection.isCollapsed) { - _editableText!.toggleToolbar(); - } - } - - void _registerController() { - assert(_controller != null); - - registerForRestoration(_controller!, 'controller'); - } - - void _requestKeyboard() => _editableText?.requestKeyboard(); - - bool _shouldShowSelectionHandles(SelectionChangedCause? cause) { - // Handles are not shown when the text field is activated by events that don't trigger the selection overlay. - if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar) { - return false; - } - if (cause == SelectionChangedCause.keyboard) return false; - if (widget.readOnly && _effectiveController.selection.isCollapsed) { - return false; - } - if (!_isEnabled) return false; - if (cause == SelectionChangedCause.longPress || - cause == SelectionChangedCause.scribble) return true; - if (_effectiveController.text.isNotEmpty) return true; - - return false; - } - - MoonTextInputSizeProperties _getMoonTextInputSize( - BuildContext context, - MoonTextInputSize? moonTextInputSize, - ) { - switch (moonTextInputSize) { - case MoonTextInputSize.sm: - return context.moonTheme?.textInputTheme.sizes.sm ?? - MoonTextInputSizes(tokens: MoonTokens.light).sm; - case MoonTextInputSize.md: - return context.moonTheme?.textInputTheme.sizes.md ?? - MoonTextInputSizes(tokens: MoonTokens.light).md; - case MoonTextInputSize.lg: - return context.moonTheme?.textInputTheme.sizes.lg ?? - MoonTextInputSizes(tokens: MoonTokens.light).lg; - case MoonTextInputSize.xl: - return context.moonTheme?.textInputTheme.sizes.xl ?? - MoonTextInputSizes(tokens: MoonTokens.light).xl; - default: - return context.moonTheme?.textInputTheme.sizes.md ?? - MoonTextInputSizes(tokens: MoonTokens.light).md; - } - } - - @override - void autofill(TextEditingValue newEditingValue) => - _editableText!.autofill(newEditingValue); - - @override - void initState() { - super.initState(); - - _selectionGestureDetectorBuilder = - _MoonTextInputSelectionGestureDetectorBuilder(state: this); - - if (widget.controller == null) _createLocalController(); - - _effectiveFocusNode.canRequestFocus = widget.canRequestFocus && _isEnabled; - _effectiveFocusNode.addListener(_handleFocusChanged); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - - _effectiveFocusNode.canRequestFocus = _canRequestFocus; - } - - @override - void didUpdateWidget(MoonTextInput oldWidget) { - super.didUpdateWidget(oldWidget); - - if (widget.controller == null && oldWidget.controller != null) { - _createLocalController(oldWidget.controller!.value); - } else if (widget.controller != null && oldWidget.controller == null) { - unregisterFromRestoration(_controller!); - _controller!.dispose(); - _controller = null; - } - - if (widget.focusNode != oldWidget.focusNode) { - (oldWidget.focusNode ?? _focusNode)?.removeListener(_handleFocusChanged); - (widget.focusNode ?? _focusNode)?.addListener(_handleFocusChanged); - } - - _effectiveFocusNode.canRequestFocus = _canRequestFocus; - - if (_hasFocus && widget.readOnly != oldWidget.readOnly && _isEnabled) { - if (_effectiveController.selection.isCollapsed) { - _showSelectionHandles = !widget.readOnly; - } - } - } - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - if (_controller != null) _registerController(); - } - - @override - void dispose() { - _effectiveFocusNode.removeListener(_handleFocusChanged); - _focusNode?.dispose(); - _controller?.dispose(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - assert(debugCheckHasMaterial(context)); - assert(debugCheckHasMaterialLocalizations(context)); - assert(debugCheckHasDirectionality(context)); - assert( - !(widget.style != null && - widget.style!.inherit == false && - (widget.style!.fontSize == null || - widget.style!.textBaseline == null)), - 'Inherit false style must supply fontSize and textBaseline.', - ); - - final ThemeData theme = Theme.of(context); - final DefaultSelectionStyle selectionStyle = - DefaultSelectionStyle.of(context); - final Brightness keyboardAppearance = - widget.keyboardAppearance ?? theme.brightness; - final TextEditingController controller = _effectiveController; - final FocusNode focusNode = _effectiveFocusNode; - - final MoonTextInputSizeProperties effectiveMoonTextInputSize = - _getMoonTextInputSize(context, widget.textInputSize); - - final BorderRadiusGeometry effectiveBorderRadius = - widget.borderRadius ?? effectiveMoonTextInputSize.borderRadius; - - final Color effectiveBackgroundColor = widget.backgroundColor ?? - context.moonTheme?.textInputTheme.colors.backgroundColor ?? - MoonColors.light.goku; - - final Color effectiveActiveBorderColor = widget.activeBorderColor ?? - context.moonTheme?.textInputTheme.colors.activeBorderColor ?? - MoonColors.light.piccolo; - - final Color effectiveInactiveBorderColor = widget.inactiveBorderColor ?? - context.moonTheme?.textInputTheme.colors.inactiveBorderColor ?? - MoonColors.light.beerus; - - final Color effectiveErrorColor = widget.errorColor ?? - context.moonTheme?.textInputTheme.colors.errorColor ?? - MoonColors.light.chichi; - - final Color effectiveCursorErrorColor = widget.cursorErrorColor ?? - context.moonTheme?.textInputTheme.colors.errorColor ?? - MoonColors.light.chichi; - - final Color effectiveHoverBorderColor = widget.hoverBorderColor ?? - context.moonTheme?.textInputTheme.colors.hoverBorderColor ?? - MoonColors.light.beerus; - - final Color effectiveTextColor = widget.textColor ?? - context.moonTheme?.textInputTheme.colors.textColor ?? - MoonColors.light.textPrimary; - - final Color effectiveHintTextColor = widget.hintTextColor ?? - context.moonTheme?.textInputTheme.colors.helperTextColor ?? - MoonColors.light.trunks; - - final double effectiveGap = widget.gap ?? effectiveMoonTextInputSize.gap; - - final double effectiveHeight = - widget.height ?? effectiveMoonTextInputSize.height; - - final double effectiveDisabledOpacityValue = - context.moonOpacities?.disabled ?? MoonOpacities.opacities.disabled; - - final Duration effectiveTransitionDuration = widget.transitionDuration ?? - context.moonTheme?.textInputTheme.properties.transitionDuration ?? - const Duration(milliseconds: 167); - - final Curve effectiveTransitionCurve = widget.transitionCurve ?? - context.moonTheme?.textInputTheme.properties.transitionCurve ?? - Curves.fastOutSlowIn; - - final EdgeInsetsGeometry effectivePadding = - widget.padding ?? effectiveMoonTextInputSize.padding; - - final EdgeInsets resolvedContentPadding = - effectivePadding.resolve(Directionality.of(context)); - - final EdgeInsetsGeometry effectiveHelperPadding = widget.helperPadding ?? - context.moonTheme?.textInputTheme.properties.helperPadding ?? - EdgeInsets.only( - left: MoonSizes.sizes.x3s, - top: MoonSizes.sizes.x4s, - right: MoonSizes.sizes.x3s, - ); - - final TextStyle effectiveTextStyle = - widget.style ?? effectiveMoonTextInputSize.textStyle; - - final TextStyle effectiveHelperTextStyle = widget.helperTextStyle ?? - context.moonTheme?.textInputTheme.properties.helperTextStyle ?? - MoonTypography.typography.body.text12; - - final MoonSquircleBorder defaultBorder = MoonSquircleBorder( - borderRadius: effectiveBorderRadius.squircleBorderRadius(context), - side: BorderSide( - color: effectiveInactiveBorderColor, - width: MoonBorders.borders.defaultBorderWidth, - ), - ); - - final MoonSquircleBorder hoverBorder = MoonSquircleBorder( - borderRadius: effectiveBorderRadius.squircleBorderRadius(context), - side: BorderSide( - color: effectiveHoverBorderColor, - width: MoonBorders.borders.activeBorderWidth, - ), - ); - - final MoonSquircleBorder focusBorder = MoonSquircleBorder( - borderRadius: effectiveBorderRadius.squircleBorderRadius(context), - side: BorderSide( - color: effectiveActiveBorderColor, - width: MoonBorders.borders.activeBorderWidth, - ), - ); - - final MoonSquircleBorder errorBorder = MoonSquircleBorder( - borderRadius: effectiveBorderRadius.squircleBorderRadius(context), - side: BorderSide( - color: widget.errorBorderColor ?? effectiveErrorColor, - width: MoonBorders.borders.activeBorderWidth, - ), - ); - - final MoonSquircleBorder resolvedBorder = _hasError - ? errorBorder - : _hasFocus - ? focusBorder - : _isHovering - ? hoverBorder - : defaultBorder; - - final bool paintCursorAboveText; - final Color cursorColor; - final Color selectionColor; - - bool? cursorOpacityAnimates = widget.cursorOpacityAnimates; - Color? autocorrectionTextRectColor; - Offset? cursorOffset; - Radius? cursorRadius = widget.cursorRadius; - VoidCallback? handleDidGainAccessibilityFocus; - TextSelectionControls? textSelectionControls = widget.selectionControls; - - final MouseCursor effectiveMouseCursor = - WidgetStateProperty.resolveAs( - widget.mouseCursor ?? WidgetStateMouseCursor.textable, - _materialState, - ); - - final List formatters = [ - ...?widget.inputFormatters, - if (widget.maxLength != null) - LengthLimitingTextInputFormatter( - widget.maxLength, - maxLengthEnforcement: _effectiveMaxLengthEnforcement, - ), - ]; - - // Sets configuration as disabled if not specified; otherwise, ensures the correct style - // for misspelled words on the current platform, unless a custom style is provided. - final SpellCheckConfiguration spellCheckConfiguration; - switch (defaultTargetPlatform) { - case TargetPlatform.iOS: - case TargetPlatform.macOS: - spellCheckConfiguration = - CupertinoTextField.inferIOSSpellCheckConfiguration( - widget.spellCheckConfiguration, - ); - case TargetPlatform.android: - case TargetPlatform.fuchsia: - case TargetPlatform.linux: - case TargetPlatform.windows: - spellCheckConfiguration = TextField.inferAndroidSpellCheckConfiguration( - widget.spellCheckConfiguration, - ); - } - - final int? semanticsMaxValueLength; - if (_effectiveMaxLengthEnforcement != MaxLengthEnforcement.none && - widget.maxLength != null && - widget.maxLength! > 0) { - semanticsMaxValueLength = widget.maxLength; - } else { - semanticsMaxValueLength = null; - } - - switch (theme.platform) { - case TargetPlatform.iOS: - final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context); - forcePressEnabled = true; - textSelectionControls ??= cupertinoTextSelectionHandleControls; - paintCursorAboveText = true; - cursorOpacityAnimates ??= true; - cursorColor = _hasError - ? effectiveCursorErrorColor - : widget.cursorColor ?? effectiveTextColor; - selectionColor = selectionStyle.selectionColor ?? - cupertinoTheme.primaryColor.withOpacity(0.40); - cursorRadius ??= const Radius.circular(2.0); - cursorOffset = Offset( - iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context), - 0, - ); - autocorrectionTextRectColor = selectionColor; - - case TargetPlatform.macOS: - final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context); - forcePressEnabled = false; - textSelectionControls ??= cupertinoDesktopTextSelectionHandleControls; - paintCursorAboveText = true; - cursorOpacityAnimates ??= false; - cursorColor = _hasError - ? effectiveCursorErrorColor - : widget.cursorColor ?? effectiveTextColor; - selectionColor = selectionStyle.selectionColor ?? - cupertinoTheme.primaryColor.withOpacity(0.40); - cursorRadius ??= const Radius.circular(2.0); - cursorOffset = Offset( - iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context), - 0, - ); - handleDidGainAccessibilityFocus = () { - // Automatically activates MoonTextInput on receiving accessibility focus. - if (!_hasFocus && _effectiveFocusNode.canRequestFocus) { - _effectiveFocusNode.requestFocus(); - } - }; - - case TargetPlatform.android: - case TargetPlatform.fuchsia: - forcePressEnabled = false; - textSelectionControls ??= materialTextSelectionHandleControls; - paintCursorAboveText = false; - cursorOpacityAnimates ??= false; - cursorColor = _hasError - ? effectiveCursorErrorColor - : widget.cursorColor ?? effectiveTextColor; - selectionColor = selectionStyle.selectionColor ?? - theme.colorScheme.primary.withOpacity(0.40); - - case TargetPlatform.linux: - forcePressEnabled = false; - textSelectionControls ??= desktopTextSelectionHandleControls; - paintCursorAboveText = false; - cursorOpacityAnimates ??= false; - cursorColor = _hasError - ? effectiveCursorErrorColor - : widget.cursorColor ?? effectiveTextColor; - selectionColor = selectionStyle.selectionColor ?? - theme.colorScheme.primary.withOpacity(0.40); - - case TargetPlatform.windows: - forcePressEnabled = false; - textSelectionControls ??= desktopTextSelectionHandleControls; - paintCursorAboveText = false; - cursorOpacityAnimates ??= false; - cursorColor = _hasError - ? effectiveCursorErrorColor - : widget.cursorColor ?? effectiveTextColor; - selectionColor = selectionStyle.selectionColor ?? - theme.colorScheme.primary.withOpacity(0.40); - handleDidGainAccessibilityFocus = () { - // Automatically activates MoonTextInput on receiving accessibility focus. - if (!_hasFocus && _effectiveFocusNode.canRequestFocus) { - _effectiveFocusNode.requestFocus(); - } - }; - } - - Widget child = RepaintBoundary( - child: UnmanagedRestorationScope( - bucket: bucket, - child: EditableText( - key: editableTextKey, - autocorrect: widget.autocorrect, - autocorrectionTextRectColor: autocorrectionTextRectColor, - autofillClient: this, - autofocus: widget.autofocus, - backgroundCursorColor: CupertinoColors.inactiveGray, - clipBehavior: widget.clipBehavior, - contentInsertionConfiguration: widget.contentInsertionConfiguration, - contextMenuBuilder: widget.contextMenuBuilder, - controller: controller, - cursorColor: cursorColor, - cursorHeight: widget.cursorHeight, - cursorOffset: cursorOffset, - cursorOpacityAnimates: cursorOpacityAnimates, - cursorRadius: cursorRadius, - cursorWidth: widget.cursorWidth, - dragStartBehavior: widget.dragStartBehavior, - enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning, - enableInteractiveSelection: widget.enableInteractiveSelection, - enableSuggestions: widget.enableSuggestions, - expands: widget.expands, - focusNode: focusNode, - inputFormatters: formatters, - keyboardAppearance: keyboardAppearance, - keyboardType: widget.keyboardType, - magnifierConfiguration: widget.magnifierConfiguration ?? - TextMagnifier.adaptiveMagnifierConfiguration, - maxLines: widget.maxLines, - minLines: widget.minLines, - mouseCursor: MouseCursor.defer, - // MoonTextInput will handle the cursor. - obscureText: widget.obscureText, - obscuringCharacter: widget.obscuringCharacter, - onAppPrivateCommand: widget.onAppPrivateCommand, - onChanged: widget.onChanged, - onEditingComplete: widget.onEditingComplete, - onSelectionChanged: _handleSelectionChanged, - onSelectionHandleTapped: _handleSelectionHandleTapped, - onSubmitted: widget.onSubmitted, - onTapOutside: widget.onTapOutside, - paintCursorAboveText: paintCursorAboveText, - readOnly: widget.readOnly || !_isEnabled, - rendererIgnoresPointer: true, - restorationId: 'editable', - scribbleEnabled: widget.scribbleEnabled, - scrollController: widget.scrollController, - scrollPadding: widget.scrollPadding, - scrollPhysics: widget.scrollPhysics, - selectionColor: focusNode.hasFocus ? selectionColor : null, - selectionControls: - widget.selectionEnabled ? textSelectionControls : null, - selectionHeightStyle: widget.selectionHeightStyle, - selectionWidthStyle: widget.selectionWidthStyle, - showCursor: widget.showCursor, - showSelectionHandles: _showSelectionHandles, - smartDashesType: widget.smartDashesType, - smartQuotesType: widget.smartQuotesType, - spellCheckConfiguration: spellCheckConfiguration, - strutStyle: widget.strutStyle, - style: effectiveTextStyle.copyWith(color: effectiveTextColor), - textAlign: widget.textAlign, - textCapitalization: widget.textCapitalization, - textDirection: widget.textDirection, - textInputAction: widget.textInputAction, - undoController: widget.undoController, - ), - ), - ); - - child = AnimatedBuilder( - animation: Listenable.merge([focusNode, controller]), - builder: (BuildContext context, Widget? child) { - return BorderContainer( - backgroundColor: effectiveBackgroundColor, - border: resolvedBorder, - decoration: widget.decoration, - expands: widget.expands, - width: widget.width, - height: effectiveHeight, - duration: effectiveTransitionDuration, - curve: effectiveTransitionCurve, - child: Row( - crossAxisAlignment: widget.expands - ? CrossAxisAlignment.center - : CrossAxisAlignment.stretch, - children: [ - if (widget.leading != null) - Padding( - padding: EdgeInsetsDirectional.only( - start: resolvedContentPadding.left, - end: effectiveGap, - ), - child: widget.leading, - ), - Expanded( - child: Padding( - padding: EdgeInsetsDirectional.only( - start: widget.leading != null - ? 0 - : resolvedContentPadding.left, - end: widget.trailing != null - ? 0 - : resolvedContentPadding.right, - ), - child: Stack( - children: [ - Align( - alignment: widget.hasFloatingLabel - ? Alignment.bottomCenter - : widget.textAlignVertical == TextAlignVertical.top - ? Alignment.topCenter - : Alignment.center, - child: Padding( - padding: EdgeInsets.symmetric( - vertical: resolvedContentPadding.bottom, - ), - child: child, - ), - ), - if (widget.hintText != null) - Padding( - padding: EdgeInsets.symmetric( - vertical: resolvedContentPadding.top, - ), - child: AnimatedScale( - alignment: - Directionality.of(context) == TextDirection.ltr - ? Alignment.topLeft - : Alignment.topRight, - duration: effectiveTransitionDuration, - scale: widget.hasFloatingLabel && - (focusNode.hasFocus || - controller.value.text.isNotEmpty) - ? 0.75 - : 1.0, - child: AnimatedAlign( - duration: effectiveTransitionDuration, - alignment: widget.textAlignVertical == - TextAlignVertical.top || - (widget.hasFloatingLabel && - (focusNode.hasFocus || - controller.value.text.isNotEmpty)) - ? AlignmentDirectional.topStart - : AlignmentDirectional.centerStart, - child: AnimatedOpacity( - opacity: (controller.value.text.isEmpty || - widget.hasFloatingLabel) - ? 1.0 - : 0.0, - duration: effectiveTransitionDuration, - curve: effectiveTransitionCurve, - child: Text( - widget.hintText!, - maxLines: 1, - overflow: TextOverflow.ellipsis, - textAlign: widget.textAlign, - style: effectiveTextStyle.copyWith( - color: effectiveHintTextColor, - ), - ), - ), - ), - ), - ), - ], - ), - ), - ), - if (widget.trailing != null) - Padding( - padding: EdgeInsetsDirectional.only( - start: effectiveGap, - end: resolvedContentPadding.right, - ), - child: widget.trailing, - ), - ], - ), - ); - }, - child: child, - ); - - child = AnimatedOpacity( - opacity: widget.enabled ? 1.0 : effectiveDisabledOpacityValue, - curve: effectiveTransitionCurve, - duration: effectiveTransitionDuration, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - child, - if (widget.helper != null || (widget.errorText != null)) - RepaintBoundary( - child: IconTheme( - data: IconThemeData( - color: widget.errorText != null - ? effectiveErrorColor - : effectiveHintTextColor, - ), - child: DefaultTextStyle( - style: effectiveHelperTextStyle.copyWith( - color: widget.errorText != null - ? effectiveErrorColor - : effectiveHintTextColor, - ), - child: widget.errorText != null - ? widget.errorBuilder?.call(context, widget.errorText) ?? - Padding( - padding: effectiveHelperPadding, - child: - MoonErrorMessage(errorText: widget.errorText!), - ) - : Padding( - padding: effectiveHelperPadding, - child: widget.helper, - ), - ), - ), - ), - ], - ), - ); - - return MouseRegion( - cursor: effectiveMouseCursor, - onEnter: (PointerEnterEvent event) => _handleHover(true), - onExit: (PointerExitEvent event) => _handleHover(false), - child: TextFieldTapRegion( - child: IgnorePointer( - ignoring: !_isEnabled, - child: AnimatedBuilder( - animation: controller, // Changes the _currentLength. - builder: (BuildContext context, Widget? child) { - return Semantics( - maxValueLength: semanticsMaxValueLength, - currentValueLength: _currentLength, - onTap: widget.readOnly - ? null - : () { - if (!_effectiveController.selection.isValid) { - _effectiveController.selection = - TextSelection.collapsed( - offset: _effectiveController.text.length, - ); - } - _requestKeyboard(); - }, - onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus, - child: child, - ); - }, - child: _selectionGestureDetectorBuilder.buildGestureDetector( - behavior: HitTestBehavior.translucent, - child: child, - ), - ), - ), - ), - ); - } -} - -class _MoonTextInputSelectionGestureDetectorBuilder - extends TextSelectionGestureDetectorBuilder { - _MoonTextInputSelectionGestureDetectorBuilder({ - required _MoonTextInputState state, - }) : _state = state, - super(delegate: state); - - final _MoonTextInputState _state; - - @override - void onForcePressStart(ForcePressDetails details) { - super.onForcePressStart(details); - if (delegate.selectionEnabled && shouldShowSelectionToolbar) { - editableText.showToolbar(); - } - } - - @override - void onForcePressEnd(ForcePressDetails details) { - // Not required. - } - - @override - void onSingleTapUp(TapDragUpDetails details) { - super.onSingleTapUp(details); - _state._requestKeyboard(); - } - - @override - bool get onUserTapAlwaysCalled => _state.widget.onTapAlwaysCalled; - - @override - void onUserTap() { - _state.widget.onTap?.call(); - } - - @override - void onSingleLongTapStart(LongPressStartDetails details) { - super.onSingleLongTapStart(details); - if (delegate.selectionEnabled) { - switch (Theme.of(_state.context).platform) { - case TargetPlatform.iOS: - case TargetPlatform.macOS: - break; - case TargetPlatform.android: - case TargetPlatform.fuchsia: - case TargetPlatform.linux: - case TargetPlatform.windows: - Feedback.forLongPress(_state.context); - } - } - } -} diff --git a/lib/src/widgets/text_input_group/text_input_group.dart b/lib/src/widgets/text_input_group/text_input_group.dart deleted file mode 100644 index ea2113db..00000000 --- a/lib/src/widgets/text_input_group/text_input_group.dart +++ /dev/null @@ -1,459 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:moon_design/src/theme/theme.dart'; -import 'package:moon_design/src/theme/tokens/sizes.dart'; -import 'package:moon_design/src/theme/tokens/transitions.dart'; -import 'package:moon_design/src/theme/tokens/typography/typography.dart'; -import 'package:moon_design/src/utils/extensions.dart'; -import 'package:moon_design/src/utils/squircle/squircle_border.dart'; -import 'package:moon_design/src/widgets/common/base_control.dart'; -import 'package:moon_design/src/widgets/common/border_container.dart'; -import 'package:moon_design/src/widgets/common/error_message_widgets.dart'; -import 'package:moon_design/src/widgets/text_input/form_text_input.dart'; -import 'package:moon_tokens/moon_tokens.dart'; - -enum MoonTextInputGroupOrientation { - vertical, - horizontal, -} - -typedef MoonTextInputGroupErrorBuilder = Widget Function( - BuildContext context, - List errorText, -); - -class MoonTextInputGroup extends StatefulWidget { - /// Whether the text input group is enabled. When false, taps are ignored and - /// the opacity reduced. - final bool enabled; - - /// The border radius of the text input group. - final BorderRadiusGeometry? borderRadius; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip? clipBehavior; - - /// The background color of the text input group. - final Color? backgroundColor; - - /// The default border color of the text input group. - final Color? borderColor; - - /// The color of the text input group in error state. - final Color? errorColor; - - /// The text color of the text input group hint. - final Color? hintTextColor; - - /// The border color of the text input group on hover. - final Color? hoverBorderColor; - - /// The duration of the text input group transition animation - /// (enable and disable). - final Duration? transitionDuration; - - /// The curve of the text input group transition animation - /// (enable and disable). - final Curve? transitionCurve; - - /// The padding of the [helper] and [errorBuilder] widgets. - final EdgeInsetsGeometry? helperPadding; - - /// The padding of the text content. - final EdgeInsetsGeometry? textPadding; - - /// The custom decoration of the text input group. - final Decoration? decoration; - - /// The semantic label for the text input group widget. - final String? semanticLabel; - - /// The text style of the [helper] or error state text. - final TextStyle? helperTextStyle; - - /// The orientation of the text input group. - final MoonTextInputGroupOrientation orientation; - - /// The list of text inputs to display as the children of the text input group. - final List children; - - /// A builder to build the text input group error widget. - final MoonTextInputGroupErrorBuilder? errorBuilder; - - /// The widget to display below the text input group. Not displayed in error - /// state. - final Widget? helper; - - /// Creates a Moon Design text input group. - const MoonTextInputGroup({ - super.key, - this.enabled = true, - this.borderRadius, - this.clipBehavior, - this.backgroundColor, - this.borderColor, - this.errorColor, - this.hintTextColor, - this.hoverBorderColor, - this.transitionDuration, - this.transitionCurve, - this.helperPadding, - this.textPadding, - this.decoration, - this.semanticLabel, - this.helperTextStyle, - this.orientation = MoonTextInputGroupOrientation.vertical, - required this.children, - this.errorBuilder, - this.helper, - }); - - @override - State createState() => _MoonTextInputGroupState(); -} - -class _MoonTextInputGroupState extends State { - late final List _validatorErrors = - List.filled(widget.children.length, null); - - late List _previousValidatorErrors; - - bool get _groupHasValidationError => - _validatorErrors.nonNulls.toList().isNotEmpty; - - bool get _groupHasErrorText => widget.children - .any((MoonFormTextInput child) => child.configuration.errorText != null); - - bool get _groupHasError => _groupHasValidationError || _groupHasErrorText; - - bool get _groupHasAllValidationErrors => - _validatorErrors.nonNulls.length == widget.children.length; - - bool get _groupHasAllErrorTexts => widget.children.every( - (MoonFormTextInput child) => child.configuration.errorText != null, - ); - - bool get _groupIsInErrorState => - _groupHasAllValidationErrors || _groupHasAllErrorTexts; - - bool get _shouldShowError => _groupHasError || _groupIsInErrorState; - - void _handleValidationError(int index, String? errorText) { - _previousValidatorErrors = _validatorErrors; - - if (_previousValidatorErrors[index] == errorText) return; - - _validatorErrors[index] = errorText; - - WidgetsBinding.instance.addPostFrameCallback((Duration _) { - // Rebuild the widget to show the error. - if (mounted) setState(() {}); - }); - } - - @override - Widget build(BuildContext context) { - final BorderRadiusGeometry effectiveBorderRadius = widget.borderRadius ?? - context.moonTheme?.textInputGroupTheme.properties.borderRadius ?? - BorderRadius.circular(8); - - final Color effectiveBackgroundColor = widget.backgroundColor ?? - context.moonTheme?.textInputGroupTheme.colors.backgroundColor ?? - MoonColors.light.goku; - - final Color effectiveBorderColor = widget.borderColor ?? - context.moonTheme?.textInputGroupTheme.colors.borderColor ?? - MoonColors.light.beerus; - - final Color effectiveErrorColor = widget.errorColor ?? - context.moonTheme?.textInputGroupTheme.colors.errorColor ?? - MoonColors.light.chichi; - - final Color effectiveHelperTextColor = widget.hintTextColor ?? - context.moonTheme?.textInputGroupTheme.colors.helperTextColor ?? - MoonColors.light.trunks; - - final EdgeInsetsGeometry effectiveHelperPadding = widget.helperPadding ?? - context.moonTheme?.textInputGroupTheme.properties.helperPadding ?? - EdgeInsets.only( - left: MoonSizes.sizes.x3s, - top: MoonSizes.sizes.x4s, - right: MoonSizes.sizes.x3s, - ); - - final TextStyle effectiveHelperTextStyle = widget.helperTextStyle ?? - context.moonTheme?.textInputGroupTheme.properties.helperTextStyle ?? - MoonTypography.typography.body.text12; - - final Duration effectiveTransitionDuration = widget.transitionDuration ?? - context.moonTheme?.textInputGroupTheme.properties.transitionDuration ?? - MoonTransitions.transitions.defaultTransitionDuration; - - final Curve effectiveTransitionCurve = widget.transitionCurve ?? - context.moonTheme?.textInputGroupTheme.properties.transitionCurve ?? - MoonTransitions.transitions.defaultTransitionCurve; - - final List effectiveErrorMessages = - _validatorErrors.nonNulls.toList().isNotEmpty - ? _validatorErrors.nonNulls.toList() - : widget.children - .map((MoonFormTextInput child) => child.configuration.errorText) - .nonNulls - .toList(); - - List childrenWithDivider({required bool shouldHideDivider}) => - List.generate( - widget.children.length * 2 - 1, - (int index) { - final int derivedIndex = index ~/ 2; - - final MoonFormTextInputConfiguration configuration = - widget.children[derivedIndex].configuration; - - final bool selfShowError = ((configuration.errorText != null && - _validatorErrors[derivedIndex] == null) && - !_groupHasValidationError && - !_groupHasAllErrorTexts) || - (_validatorErrors[derivedIndex] != null && - !_groupHasAllValidationErrors); - - Widget child = MoonFormTextInput( - activeBorderColor: configuration.activeBorderColor, - autocorrect: configuration.autocorrect, - autofillHints: configuration.autofillHints, - autofocus: configuration.autofocus, - autovalidateMode: configuration.autovalidateMode, - backgroundColor: Colors.transparent, - borderRadius: configuration.borderRadius, - canRequestFocus: configuration.canRequestFocus, - clipBehavior: configuration.clipBehavior, - contentInsertionConfiguration: - configuration.contentInsertionConfiguration, - contextMenuBuilder: configuration.contextMenuBuilder, - controller: configuration.controller, - cursorColor: configuration.cursorColor, - cursorHeight: configuration.cursorHeight, - cursorOpacityAnimates: configuration.cursorOpacityAnimates, - cursorRadius: configuration.cursorRadius, - cursorWidth: configuration.cursorWidth, - decoration: configuration.decoration, - dragStartBehavior: configuration.dragStartBehavior, - enabled: configuration.enabled, - enableIMEPersonalizedLearning: - configuration.enableIMEPersonalizedLearning, - enableInteractiveSelection: - configuration.enableInteractiveSelection, - enableSuggestions: configuration.enableSuggestions, - errorBorderColor: - selfShowError ? configuration.errorColor : Colors.transparent, - errorBuilder: (BuildContext context, String? errorText) => - const SizedBox.shrink(), - errorColor: configuration.errorColor, - errorText: configuration.errorText, - expands: configuration.expands, - focusNode: configuration.focusNode, - gap: configuration.gap, - hasFloatingLabel: configuration.hasFloatingLabel, - height: configuration.height, - helper: configuration.helper, - helperPadding: configuration.helperPadding, - helperTextStyle: configuration.helperTextStyle, - hintText: configuration.hintText, - hintTextColor: configuration.hintTextColor, - hoverBorderColor: configuration.hoverBorderColor, - inactiveBorderColor: Colors.transparent, - initialValue: configuration.initialValue, - inputFormatters: configuration.inputFormatters, - keyboardAppearance: configuration.keyboardAppearance, - keyboardType: configuration.keyboardType, - leading: configuration.leading, - magnifierConfiguration: configuration.magnifierConfiguration, - maxLength: configuration.maxLength, - maxLengthEnforcement: configuration.maxLengthEnforcement, - maxLines: configuration.maxLines, - minLines: configuration.minLines, - mouseCursor: configuration.mouseCursor, - obscureText: configuration.obscureText, - obscuringCharacter: configuration.obscuringCharacter, - onAppPrivateCommand: configuration.onAppPrivateCommand, - onChanged: configuration.onChanged, - onEditingComplete: configuration.onEditingComplete, - onSubmitted: configuration.onSubmitted, - onSaved: configuration.onSaved, - onTap: configuration.onTap, - onTapOutside: configuration.onTapOutside, - padding: configuration.padding, - readOnly: configuration.readOnly, - restorationId: configuration.restorationId, - scribbleEnabled: configuration.scribbleEnabled, - scrollController: configuration.scrollController, - scrollPadding: configuration.scrollPadding, - scrollPhysics: configuration.scrollPhysics, - selectionControls: configuration.selectionControls, - selectionHeightStyle: configuration.selectionHeightStyle, - selectionWidthStyle: configuration.selectionWidthStyle, - showCursor: configuration.showCursor, - smartDashesType: configuration.smartDashesType, - smartQuotesType: configuration.smartQuotesType, - spellCheckConfiguration: configuration.spellCheckConfiguration, - strutStyle: configuration.strutStyle, - style: configuration.style, - textAlign: configuration.textAlign, - textAlignVertical: configuration.textAlignVertical, - textCapitalization: configuration.textCapitalization, - textColor: configuration.textColor, - textDirection: configuration.textDirection, - textInputAction: configuration.textInputAction, - textInputSize: configuration.textInputSize, - trailing: configuration.trailing, - transitionCurve: configuration.transitionCurve, - transitionDuration: configuration.transitionDuration, - undoController: configuration.undoController, - validationStatusCallback: (errorText) => - _handleValidationError(derivedIndex, errorText), - validator: configuration.validator, - width: configuration.width, - ); - - child = configuration.width != null - ? SizedBox( - width: configuration.width, - child: child, - ) - : Flexible(child: child); - - return index.isEven - ? child - // Animated divider - : BorderContainer( - height: widget.orientation == - MoonTextInputGroupOrientation.horizontal - ? double.infinity - : 1, - width: widget.orientation == - MoonTextInputGroupOrientation.vertical - ? double.infinity - : 1, - duration: effectiveTransitionDuration, - curve: effectiveTransitionCurve, - border: MoonSquircleBorder( - borderRadius: - effectiveBorderRadius.squircleBorderRadius(context), - side: BorderSide( - color: (!_groupHasValidationError && - _groupHasAllErrorTexts) || - _groupHasAllValidationErrors - ? effectiveErrorColor - : shouldHideDivider || - (_groupHasError || _groupHasValidationError) - ? Colors.transparent - : effectiveBorderColor, - ), - ), - child: const SizedBox.shrink(), - ); - }, - growable: false, - ); - - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - MoonBaseControl( - semanticLabel: widget.semanticLabel, - isFocusable: false, - showFocusEffect: false, - onTap: widget.enabled ? () {} : null, - propagateGesturesToChild: true, - builder: ( - BuildContext context, - bool isEnabled, - bool isHovered, - bool isFocused, - bool isPressed, - ) { - return BorderContainer( - clipBehavior: widget.clipBehavior ?? Clip.none, - backgroundColor: effectiveBackgroundColor, - decoration: widget.decoration, - duration: effectiveTransitionDuration, - curve: effectiveTransitionCurve, - border: MoonSquircleBorder( - borderRadius: - effectiveBorderRadius.squircleBorderRadius(context), - side: BorderSide( - color: - (!_groupHasValidationError && _groupHasAllErrorTexts) || - _groupHasAllValidationErrors - ? effectiveErrorColor - : effectiveBorderColor, - ), - ), - child: _InputGroupOrientation( - orientation: widget.orientation, - children: childrenWithDivider( - shouldHideDivider: isHovered || isFocused, - ), - ), - ); - }, - ), - if (widget.helper != null || _shouldShowError) - IconTheme( - data: IconThemeData( - color: _shouldShowError - ? effectiveErrorColor - : effectiveHelperTextColor, - ), - child: DefaultTextStyle( - style: effectiveHelperTextStyle.copyWith( - color: _shouldShowError - ? effectiveErrorColor - : effectiveHelperTextColor, - ), - child: Padding( - padding: effectiveHelperPadding, - child: _shouldShowError - ? (widget.errorBuilder - ?.call(context, effectiveErrorMessages) ?? - MoonErrorMessages(errors: effectiveErrorMessages)) - : widget.helper, - ), - ), - ), - ], - ); - } -} - -class _InputGroupOrientation extends StatelessWidget { - final MoonTextInputGroupOrientation orientation; - final List children; - - const _InputGroupOrientation({ - required this.orientation, - required this.children, - }); - - @override - Widget build(BuildContext context) { - return switch (orientation) { - MoonTextInputGroupOrientation.vertical => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: children, - ), - MoonTextInputGroupOrientation.horizontal => SizedBox( - height: 56, - width: 300, - child: Row( - mainAxisSize: MainAxisSize.min, - children: children, - ), - ), - }; - } -} diff --git a/test/text_input_test.dart b/test/input_test.dart similarity index 65% rename from test/text_input_test.dart rename to test/input_test.dart index 38dc57ac..55707b6e 100644 --- a/test/text_input_test.dart +++ b/test/input_test.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:moon_design/moon_design.dart'; -const Key _textInputKey = Key("textInputKey"); +const Key _inputKey = Key("inputKey"); const Key _submitButtonKey = Key("submitButtonKey"); const String _hintText = 'Hint text'; @@ -10,8 +10,8 @@ const String _errorText = 'Error text'; const String _helperText = 'Helper text'; const String _validText = 'Valid text'; const String _invalidText = 'Invalid text'; -const IconData _textInputLeadingIcon = MoonIcons.other_frame_24_light; -const IconData _textInputTrailingIcon = MoonIcons.controls_close_small_24_light; +const IconData _inputLeadingIcon = MoonIcons.other_frame_24_light; +const IconData _inputTrailingIcon = MoonIcons.controls_close_small_24_light; void main() { Future submit(WidgetTester tester) async { @@ -21,24 +21,24 @@ void main() { testWidgets("Provided key is used.", (tester) async { await tester.pumpWidget( - const _TextInputTestWidget( - textInputKey: _textInputKey, + const _InputTestWidget( + inputKey: _inputKey, ), ); - expect(find.byKey(_textInputKey), findsOneWidget); + expect(find.byKey(_inputKey), findsOneWidget); }); testWidgets("When valid text is entered and submitted, error is not shown.", (tester) async { await tester.pumpWidget( - const _TextInputTestWidget( - textInputKey: _textInputKey, + const _InputTestWidget( + inputKey: _inputKey, ), ); - final textInput = find.byKey(_textInputKey); + final input = find.byKey(_inputKey); - await tester.enterText(textInput, _validText); + await tester.enterText(input, _validText); await tester.pumpAndSettle(); expect(find.text(_validText), findsOneWidget); @@ -51,13 +51,13 @@ void main() { testWidgets("When invalid text is entered and submitted, error is shown.", (tester) async { await tester.pumpWidget( - const _TextInputTestWidget( - textInputKey: _textInputKey, + const _InputTestWidget( + inputKey: _inputKey, ), ); - final textInput = find.byKey(_textInputKey); + final input = find.byKey(_inputKey); - await tester.enterText(textInput, _invalidText); + await tester.enterText(input, _invalidText); await tester.pumpAndSettle(); expect(find.text(_invalidText), findsOneWidget); @@ -69,7 +69,7 @@ void main() { testWidgets("Helper text is shown.", (tester) async { await tester.pumpWidget( - const _TextInputTestWidget( + const _InputTestWidget( showHelper: true, ), ); @@ -79,7 +79,7 @@ void main() { testWidgets("Hint text is shown.", (tester) async { await tester.pumpWidget( - const _TextInputTestWidget( + const _InputTestWidget( showHelper: true, ), ); @@ -87,28 +87,28 @@ void main() { expect(find.text(_hintText), findsOneWidget); }); - testWidgets("Text input has a leading and trailing widget.", (tester) async { + testWidgets("Input has a leading and trailing widget.", (tester) async { await tester.pumpWidget( - const _TextInputTestWidget( + const _InputTestWidget( showLeading: true, showTrailing: true, ), ); - expect(find.byIcon(_textInputLeadingIcon), findsOneWidget); - expect(find.byIcon(_textInputTrailingIcon), findsOneWidget); + expect(find.byIcon(_inputLeadingIcon), findsOneWidget); + expect(find.byIcon(_inputTrailingIcon), findsOneWidget); }); - testWidgets("When text input is disabled, input cannot be entered.", + testWidgets("When input is disabled, text cannot be entered.", (tester) async { await tester.pumpWidget( - const _TextInputTestWidget( - textInputKey: _textInputKey, + const _InputTestWidget( + inputKey: _inputKey, enabled: false, ), ); - final textArea = find.byKey(_textInputKey); + final textArea = find.byKey(_inputKey); await tester.enterText(textArea, _validText); await tester.pumpAndSettle(); @@ -117,15 +117,15 @@ void main() { }); } -class _TextInputTestWidget extends StatelessWidget { - final Key? textInputKey; +class _InputTestWidget extends StatelessWidget { + final Key? inputKey; final bool enabled; final bool showHelper; final bool showLeading; final bool showTrailing; - const _TextInputTestWidget({ - this.textInputKey, + const _InputTestWidget({ + this.inputKey, this.enabled = true, this.showHelper = false, this.showLeading = false, @@ -141,17 +141,15 @@ class _TextInputTestWidget extends StatelessWidget { builder: (BuildContext context) { return Column( children: [ - MoonFormTextInput( - key: textInputKey, + MoonInput.form( + key: inputKey, enabled: enabled, hintText: _hintText, validator: (String? value) => value != null && value.length > 10 ? _errorText : null, - leading: - showLeading ? const Icon(_textInputLeadingIcon) : null, - trailing: showTrailing - ? const Icon(_textInputTrailingIcon) - : null, + leading: showLeading ? const Icon(_inputLeadingIcon) : null, + trailing: + showTrailing ? const Icon(_inputTrailingIcon) : null, helper: showHelper ? const Text(_helperText) : null, ), MoonFilledButton(