Skip to content

Commit 54e7087

Browse files
committed
feat(rich-text): For alert message, switch, radio, checkbox, text input, PIN code put, phone number input components, update API to use rich text
1 parent e00a5d5 commit 54e7087

11 files changed

Lines changed: 304 additions & 53 deletions

ouds_core/lib/components/alert/ouds_alert_message.dart

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import 'package:ouds_core/components/common/OudsBorder.dart';
2424
import 'package:ouds_core/components/common/ouds_icon_status.dart';
2525
import 'package:ouds_core/components/link/ouds_link.dart';
2626
import 'package:ouds_core/components/utilities/app_assets.dart';
27+
import 'package:ouds_core/components/utilities/markdown_span_builder.dart';
2728
import 'package:ouds_core/l10n/gen/ouds_localizations.dart';
2829
import 'package:ouds_theme_contract/ouds_theme.dart';
2930

@@ -92,16 +93,24 @@ class OudsAlertMessageActionLayout {
9293
/// Each variant conveys a clear semantic meaning and must always be paired with its dedicated functional icon to ensure clarity and accessibility.
9394
/// Use functional alerts to inform user about state changes, confirmations, or issues that are directly connected to system logic or user actions. These
9495
/// messages carry functional meaning and help guide user response or acknowledgment.
95-
/// - [description]: Optional supplementary text in an alert message. Use only when additional detail or guidance is needed beyond the label. It should remain
96-
/// short, clear and scannable, helping the user to understand what happened and what he can do next.
96+
/// - [description]: Optional supplementary text displayed below the alert label. Use it only when additional context, guidance or next steps are needed.
97+
/// The content should remain concise, clear and easy to scan.
98+
/// Supports lightweight markdown rich text formatting:
99+
/// - Strong text using `**bold**`,
100+
/// - Underline bold text using `__**underline bold**__`,
101+
/// - Hyperlinks using `[link](https://example.com)`
102+
///
103+
/// - [onDescriptionLinkTapped]: Callback invoked when a link in the description is tapped. The URL of the link is passed as an argument.
97104
/// - [onClose]: Callback invoked when the close button is clicked. If `null`, the close button is not displayed and the alert message remains visible until
98105
/// the context changes (e.g., the issue is resolved, the screen is refreshed). Otherwise, the alert message is dismissable and includes a close button,
99106
/// allowing the user to dismiss it when he has acknowledged the message.
100107
/// Some alerts must remain visible to ensure user is aware of important information; others can be closed to reduce visual clutter.
101108
/// - [actionLayout]: An optional action link to be displayed in the alert message. It can be used to trigger an action.
102-
/// - [bulletList]: An optional list of bullet points to be displayed in the alert message following the label or the optional [description].
103-
/// Add this list when you need to highlight multiple points, such as service features, plan details, or next steps. Each bullet should be short and written
104-
/// as a clear phrase or fragment — avoid long sentences or complex structures.
109+
/// - [bulletList]: An optional list of bullet points displayed below the label or the optional [description].
110+
/// Use this list to highlight multiple items such as service features, plan details or next steps.
111+
/// Each bullet should remain short, clear and easy to scan. Avoid long sentences or complex structures.
112+
/// Supports lightweight inline markdown formatting for text emphasis :
113+
/// - Strong text `**bold**`.
105114
///
106115
/// ## Usage Example:
107116
///
@@ -121,6 +130,7 @@ class OudsAlertMessage extends StatefulWidget {
121130
required this.status,
122131
this.description,
123132
this.onClose,
133+
this.onDescriptionLinkTapped,
124134
this.actionLayout,
125135
this.bulletList,
126136
});
@@ -137,6 +147,9 @@ class OudsAlertMessage extends StatefulWidget {
137147
/// A callback invoked when the close button is clicked. If `null`, the close button is not shown.
138148
final VoidCallback? onClose;
139149

150+
/// A callback invoked when a link in the description is tapped.
151+
final ValueChanged<String>? onDescriptionLinkTapped;
152+
140153
/// An optional clickable link to trigger an action.
141154
final OudsAlertMessageActionLayout? actionLayout;
142155

@@ -182,16 +195,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
182195
// Optional description text.
183196
if (widget.description != null && widget.description!.isNotEmpty) ...[
184197
SizedBox(height: alertTokens.spaceRowGap),
185-
Text(
186-
widget.description!,
187-
style: theme.typographyTokens
188-
.typeLabelDefaultMedium(context)
189-
.copyWith(
190-
color: alertMessageStatusModifier.getStatusTextColor(
191-
widget.status,
192-
),
193-
),
194-
),
198+
_buildDescription(context),
195199
],
196200
// Optional bullet list. A gap is added only if the list is not empty.
197201
if (widget.bulletList != null &&
@@ -376,6 +380,27 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
376380
);
377381
}
378382

383+
/// Builds the description text with support for bold and hyperlinks.
384+
Widget _buildDescription(BuildContext context) {
385+
final theme = OudsTheme.of(context);
386+
final alertMessageStatusModifier = OudsAlertStatusModifier(context);
387+
388+
final textStyle = theme.typographyTokens
389+
.typeLabelDefaultMedium(context)
390+
.copyWith(
391+
color: alertMessageStatusModifier.getStatusTextColor(widget.status),
392+
);
393+
394+
return Text.rich(
395+
MarkdownSpanBuilder.buildRichText(
396+
context,
397+
widget.description ?? '',
398+
baseStyle: textStyle,
399+
onLinkTap: widget.onDescriptionLinkTapped,
400+
),
401+
);
402+
}
403+
379404
/// Builds a single bullet list item for the alert message.
380405
///
381406
/// This widget creates a row containing a bullet icon and a text label,
@@ -436,7 +461,9 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
436461
Flexible(
437462
child: ConstrainedBox(
438463
constraints: BoxConstraints(maxWidth: maxTextWidth),
439-
child: Text(label, style: textStyle),
464+
child: Text.rich(
465+
MarkdownSpanBuilder.buildBoldOnly(label, baseStyle: textStyle),
466+
),
440467
),
441468
),
442469
],

ouds_core/lib/components/checkbox/ouds_checkbox_item.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ import 'package:ouds_core/components/control/ouds_control_item.dart';
5353
/// - [constrainedMaxWidth]: When `true`, the item width is constrained to a maximum value defined by the design system.
5454
/// When `false`, no specific width constraint is applied, allowing the component to size itself or follow external modifiers.
5555
/// Defaults to `false`.
56+
/// - [errorText]: Text shown below the checkbox item indicating an error state. Supports only strong text formatting using `**bold**`.
57+
/// Rich text is supported only for error messages.
58+
///
5659
///
5760
/// ### You can use [OudsCheckboxItem] component in your project, customizing parameters as needed :
5861
///

ouds_core/lib/components/control/ouds_control_item.dart

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ import 'package:ouds_core/components/control/internal/modifier/ouds_control_back
2222
import 'package:ouds_core/components/control/internal/modifier/ouds_control_border_modifier.dart';
2323
import 'package:ouds_core/components/control/internal/modifier/ouds_control_indicator.dart';
2424
import 'package:ouds_core/components/control/internal/modifier/ouds_control_text_modifier.dart';
25+
import 'package:ouds_core/components/control/internal/modifier/ouds_control_tick_modifier.dart';
2526
import 'package:ouds_core/components/control/internal/ouds_control_state.dart';
2627
import 'package:ouds_core/components/divider/ouds_divider.dart';
2728
import 'package:ouds_core/components/utilities/app_assets.dart';
29+
import 'package:ouds_core/components/utilities/markdown_span_builder.dart';
2830
import 'package:ouds_theme_contract/ouds_theme.dart';
2931

30-
import 'internal/modifier/ouds_control_tick_modifier.dart';
31-
3232
enum OudsControlItemType { switchButton, checkbox, radio }
3333

3434
/// Refactor of controls for [Checkbox], [Switch], and [RadioButton].
@@ -264,15 +264,17 @@ class OudsControlItemState extends State<OudsControlItem> {
264264
top: controlItemTokens.spacePaddingBlockTopHelperText,
265265
end: controlItemTokens.spacePaddingInline,
266266
),
267-
child: Text(
268-
widget.errorText ?? '',
269-
style: OudsTheme.of(context).typographyTokens
270-
.typeLabelDefaultMedium(context)
271-
.copyWith(
272-
color: controlItemTextModifier.getErrorMessageTextColor(
273-
controlItemState,
267+
child: Text.rich(
268+
MarkdownSpanBuilder.buildBoldOnly(
269+
widget.errorText ?? '',
270+
baseStyle: OudsTheme.of(context).typographyTokens
271+
.typeLabelDefaultMedium(context)
272+
.copyWith(
273+
color: controlItemTextModifier.getErrorMessageTextColor(
274+
controlItemState,
275+
),
274276
),
275-
),
277+
),
276278
),
277279
),
278280
],

ouds_core/lib/components/form_input/internal/ouds_form_input_decoration.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
* // Software description: Flutter library of reusable graphical components
1111
* //
1212
*/
13-
/// @nodoc
13+
/// {@category Text input}
14+
/// {@category Phone number input}
1415
library;
1516

1617
import 'dart:ui';
@@ -43,7 +44,7 @@ class OudsInputDecoration extends OudsFormInputDecoration {
4344
});
4445
}
4546

46-
/// Configuration for decorating the [OudsformInput] widget.
47+
/// Configuration for decorating the [OudsTextField] and [OudsPhoneNumberInput] widgets.
4748
///
4849
/// Provides properties to customize labels, hints, icons, helper and error texts,
4950
/// loading states, and styling.
@@ -52,8 +53,9 @@ class OudsInputDecoration extends OudsFormInputDecoration {
5253
///
5354
/// - [labelText]: The main label text displayed above or inside the input field.
5455
///
55-
/// - [helperText]: Additional information displayed below the input,
56-
/// often used to guide or assist the user.
56+
/// - [helperText]: Additional information displayed below the input, often used to guide or assist the user.
57+
/// Supports strong text formatting using `**bold**`.
58+
/// Hyperlinks are not supported in helper text. Use the dedicated helper link component instead.
5759
///
5860
/// - [hintText]: A short placeholder or hint shown inside the input when empty,
5961
/// describing the expected input.
@@ -72,6 +74,7 @@ class OudsInputDecoration extends OudsFormInputDecoration {
7274
/// - [suffix]: A string displayed after the user's input, often used for units or context.
7375
///
7476
/// - [errorText]: Text shown below the input indicating an error state or invalid input.
77+
/// Supports strong text formatting using `**bold**`.
7578
///
7679
/// - [loader]: When true, displays a loading indicator inside the input.
7780
///

ouds_core/lib/components/form_input/ouds_phone_number_input.dart

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import 'package:ouds_core/components/form_input/internal/ouds_form_input_control
2929
import 'package:ouds_core/components/form_input/internal/ouds_form_input_decoration.dart';
3030
import 'package:ouds_core/components/utilities/app_assets.dart';
3131
import 'package:ouds_core/components/utilities/input_utils.dart';
32+
import 'package:ouds_core/components/utilities/markdown_span_builder.dart';
3233
import 'package:ouds_core/l10n/gen/ouds_localizations.dart';
3334
import 'package:ouds_theme_contract/ouds_theme.dart';
3435
import 'package:ouds_theme_contract/ouds_theme_contract.dart';
@@ -776,13 +777,15 @@ class _OudsPhoneNumberInputState extends State<OudsPhoneNumberInput> {
776777
left: textInput.spacePaddingInlineDefault,
777778
right: textInput.spacePaddingInlineDefault,
778779
),
779-
child: Text(
780-
text,
781-
style: theme.typographyTokens
782-
.typeLabelDefaultMedium(context)
783-
.copyWith(
784-
color: inputTextTextModifier.getHelperTextColor(state, isError),
785-
),
780+
child: Text.rich(
781+
MarkdownSpanBuilder.buildBoldOnly(
782+
text,
783+
baseStyle: theme.typographyTokens
784+
.typeLabelDefaultMedium(context)
785+
.copyWith(
786+
color: inputTextTextModifier.getHelperTextColor(state, isError),
787+
),
788+
),
786789
),
787790
);
788791
}

ouds_core/lib/components/form_input/ouds_text_input.dart

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import 'package:ouds_core/components/form_input/internal/ouds_form_input_decorat
2424
import 'package:ouds_core/components/link/ouds_link.dart';
2525
import 'package:ouds_core/components/utilities/app_assets.dart';
2626
import 'package:ouds_core/components/utilities/input_utils.dart';
27+
import 'package:ouds_core/components/utilities/markdown_span_builder.dart';
2728
import 'package:ouds_core/l10n/gen/ouds_localizations.dart';
2829
import 'package:ouds_theme_contract/ouds_theme.dart';
2930
import 'package:ouds_theme_contract/ouds_theme_contract.dart';
@@ -660,13 +661,15 @@ class _OudsTextInputState extends State<OudsTextField> {
660661
left: textInput.spacePaddingInlineDefault,
661662
right: textInput.spacePaddingInlineDefault,
662663
),
663-
child: Text(
664-
text,
665-
style: theme.typographyTokens
666-
.typeLabelDefaultMedium(context)
667-
.copyWith(
668-
color: inputTextTextModifier.getHelperTextColor(state, isError),
669-
),
664+
child: Text.rich(
665+
MarkdownSpanBuilder.buildBoldOnly(
666+
text,
667+
baseStyle: theme.typographyTokens
668+
.typeLabelDefaultMedium(context)
669+
.copyWith(
670+
color: inputTextTextModifier.getHelperTextColor(state, isError),
671+
),
672+
),
670673
),
671674
);
672675
}

ouds_core/lib/components/form_input/password_input/ouds_password_input.dart

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import 'package:ouds_core/components/form_input/internal/ouds_form_input_control
2323
import 'package:ouds_core/components/form_input/password_input/ouds_password_input_decoration.dart';
2424
import 'package:ouds_core/components/utilities/app_assets.dart';
2525
import 'package:ouds_core/components/utilities/input_utils.dart';
26+
import 'package:ouds_core/components/utilities/markdown_span_builder.dart';
2627
import 'package:ouds_core/l10n/gen/ouds_localizations.dart';
2728
import 'package:ouds_theme_contract/ouds_theme.dart';
2829
import 'package:ouds_theme_contract/ouds_theme_contract.dart';
@@ -524,13 +525,15 @@ class _OudsPasswordInputState extends State<OudsPasswordInput> {
524525
left: textInput.spacePaddingInlineDefault,
525526
right: textInput.spacePaddingInlineDefault,
526527
),
527-
child: Text(
528-
text,
529-
style: theme.typographyTokens
530-
.typeLabelDefaultMedium(context)
531-
.copyWith(
532-
color: inputTextTextModifier.getHelperTextColor(state, isError),
533-
),
528+
child: Text.rich(
529+
MarkdownSpanBuilder.buildBoldOnly(
530+
text,
531+
baseStyle: theme.typographyTokens
532+
.typeLabelDefaultMedium(context)
533+
.copyWith(
534+
color: inputTextTextModifier.getHelperTextColor(state, isError),
535+
),
536+
),
534537
),
535538
);
536539
}

ouds_core/lib/components/form_input/password_input/ouds_password_input_decoration.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
* //
1212
*/
1313

14-
/// @nodoc
14+
/// {@category Password input}
1515
library;
1616

17-
/// Configuration for decorating the [OudsTextInput] widget.
17+
/// Configuration for decorating the [OudsPasswordInput] widget.
1818
///
1919
/// Provides properties to customize labels, hints, icons, helper and error texts,
2020
/// loading states, and styling.

ouds_core/lib/components/radio_button/ouds_radio_button_item.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ import 'package:ouds_core/components/radio_button/ouds_radio_button.dart';
5151
/// - [constrainedMaxWidth]: When `true`, the item width is constrained to a maximum value defined by the design system.
5252
/// When `false`, no specific width constraint is applied, allowing the component to size itself or follow external modifiers.
5353
/// Defaults to `false`.
54+
/// - [errorText]: Text shown below the radio button item indicating an error state. Supports only strong text formatting using `**bold**`.
55+
/// Rich text is supported only for error messages.
5456
///
5557
///
5658
/// ### You can use [OudsRadioButtonItem] component in your project, customizing parameters as needed :

ouds_core/lib/components/switch/ouds_switch_item.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ import 'package:ouds_core/l10n/gen/ouds_localizations.dart';
4545
/// - [constrainedMaxWidth]: When `true`, the item width is constrained to a maximum value defined by the design system.
4646
/// When `false`, no specific width constraint is applied, allowing the component to size itself or follow external modifiers.
4747
/// Defaults to `false`.
48+
/// - [errorText]: Text shown below the switch item indicating an error state. Supports only strong text formatting using `**bold**`.
49+
/// Rich text is supported only for error messages.
4850
///
4951
///
5052
/// ### You can use [OudsSwitchItem] component in your project, customizing parameters as needed :

0 commit comments

Comments
 (0)