Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/content/docs/form/otp-field.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: OTP Field
description: A one-time password input field for verification codes.
apiReference: https://pub.dev/documentation/forui/latest/forui.widgets.otp_field/
---

import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { Widget } from '@/components/demo/widget';
import { CodeSnippet } from '@/components/code-snippet/code-snippet';
import { UsageSnippet } from '@/components/usage-snippet/usage-snippet';
import defaultSnippet from '@/snippets/examples/otp-field/default.json';
import dividerSnippet from '@/snippets/examples/otp-field/divider.json';
import noFormatterSnippet from '@/snippets/examples/otp-field/no-formatter.json';
import otpFieldUsage from '@/snippets/usages/widgets/otp_field/otpField.json';

<Tabs items={['Preview', 'Code']}>
<Tab value="Preview">
<Widget name='otp-field'/>
</Tab>
<Tab value="Code">
<CodeSnippet snippet={defaultSnippet} />
</Tab>
</Tabs>

## CLI

To generate a specific style for customization:

<Tabs items={['OTP Field']}>
<Tab value="OTP Field">
```shell copy
dart run forui style create otp-field
```
</Tab>
</Tabs>

## Usage

### `FOtpField(...)`
<UsageSnippet usage={otpFieldUsage} />

## Examples

### With Divider

<Tabs items={['Preview', 'Code']}>
<Tab value="Preview">
<Widget name='otp-field' variant='divider'/>
</Tab>
<Tab value="Code">
<CodeSnippet snippet={dividerSnippet} />
</Tab>
</Tabs>

### No Formatter

<Tabs items={['Preview', 'Code']}>
<Tab value="Preview">
<Widget name='otp-field' variant='no-formatter'/>
</Tab>
<Tab value="Code">
<CodeSnippet snippet={noFormatterSnippet} />
</Tab>
</Tabs>

40 changes: 40 additions & 0 deletions docs_snippets/lib/examples/widgets/otp_field.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:flutter/widgets.dart';

import 'package:auto_route/auto_route.dart';
import 'package:forui/forui.dart';

import 'package:docs_snippets/example.dart';

@RoutePage()
class OtpFieldPage extends Example {
OtpFieldPage({@queryParam super.theme});

@override
Widget example(BuildContext _) => FOtpField();
}

@RoutePage()
class DividerOtpFieldPage extends Example {
DividerOtpFieldPage({@queryParam super.theme});

@override
Widget example(BuildContext _) => FOtpField(
// {@highlight}
control: const .managed(
children: [FOtpItem(), FOtpItem(), FOtpItem(), FOtpDivider(), FOtpItem(), FOtpItem(), FOtpItem()],
),
// {@endhighlight}
);
}

@RoutePage()
class NoFormatterOtpFieldPage extends Example {
NoFormatterOtpFieldPage({@queryParam super.theme});

@override
Widget example(BuildContext _) => FOtpField(
// {@highlight}
inputFormatters: const [],
// {@endhighlight}
);
}
3 changes: 3 additions & 0 deletions docs_snippets/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ class _AppRouter extends RootStackRouter {
AutoRoute(path: '/multi-select/sorted', page: SortedMultiSelectRoute.page),
AutoRoute(path: '/multi-select/popover-builder', page: PopoverBuilderMultiSelectRoute.page),
AutoRoute(path: '/multi-select/form', page: FormMultiSelectRoute.page),
AutoRoute(path: '/otp-field/default', page: OtpFieldRoute.page),
AutoRoute(path: '/otp-field/divider', page: DividerOtpFieldRoute.page),
AutoRoute(path: '/otp-field/no-formatter', page: NoFormatterOtpFieldRoute.page),
AutoRoute(path: '/pagination/default', page: PaginationRoute.page),
AutoRoute(path: '/pagination/siblings', page: SiblingsPaginationRoute.page),
AutoRoute(path: '/pagination/hide-edges', page: HideEdgesPaginationRoute.page),
Expand Down
75 changes: 75 additions & 0 deletions docs_snippets/lib/usages/widgets/otp_field.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// ignore_for_file: avoid_redundant_argument_values

import 'package:flutter/widgets.dart';

import 'package:forui/forui.dart';

final otpField = FOtpField(
// {@category "Control"}
control: const .managed(),
// {@endcategory}
// {@category "Form"}
label: const Text('Verification Code'),
description: const Text('Enter the 6-digit code.'),
errorBuilder: FFormFieldProperties.defaultErrorBuilder,
forceErrorText: null,
onSaved: null,
onReset: null,
validator: null,
autovalidateMode: .disabled,
formFieldKey: null,
// {@endcategory}
// {@category "Field"}
builder: FOtpField.defaultBuilder,
magnifierConfiguration: null,
groupId: EditableText,
keyboardType: null,
textInputAction: .done,
textCapitalization: .none,
textDirection: null,
autofocus: false,
focusNode: null,
readOnly: false,
onTapAlwaysCalled: false,
onEditingComplete: () {},
onSubmit: (value) {},
onAppPrivateCommand: (action, data) {},
onTap: () {},
onTapOutside: (event) {},
inputFormatters: FOtpField.defaultInputFormatters,
ignorePointers: null,
enableInteractiveSelection: true,
selectionControls: null,
dragStartBehavior: .start,
mouseCursor: null,
autofillHints: const [AutofillHints.oneTimeCode],
restorationId: null,
stylusHandwritingEnabled: true,
enableIMEPersonalizedLearning: true,
contentInsertionConfiguration: null,
contextMenuBuilder: null,
canRequestFocus: true,
undoController: null,
statesController: null,
// {@endcategory}
// {@category "Core"}
style: const .context(),
enabled: true,
// {@endcategory}
);

// {@category "Control" "`.managed()` with internal controller"}
/// Manages the controller internally. Allows configuring children and initial value.
final FOtpFieldControl managedInternal = .managed(
children: const [FOtpItem(), FOtpItem(), FOtpItem(), FOtpDivider(), FOtpItem(), FOtpItem(), FOtpItem()],
initial: null,
onChange: (value) {},
);

// {@category "Control" "`.managed()` with external controller"}
/// Uses an external `FOtpController` to control the OTP field's state.
final FOtpFieldControl managedExternal = .managed(
// Don't create a controller inline. Store it in a State instead.
controller: FOtpController(),
onChange: (value) {},
);
2 changes: 0 additions & 2 deletions docs_snippets/lib/usages/widgets/popover_menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ final popoverMenu = FPopoverMenu(
// {@category "Core"}
style: const .delta(maxWidth: 250),
divider: .full,
hover: true,
menu: [
.group(
children: [
Expand Down Expand Up @@ -97,7 +96,6 @@ final popoverMenuTiles = FPopoverMenu.tiles(
// {@category "Core"}
style: const .delta(maxWidth: 250),
divider: .full,
hover: true,
menu: [
.group(
children: [
Expand Down
10 changes: 9 additions & 1 deletion forui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@
### `FPopoverMenu`
* Add `FSubmenuItem`.
* Add `FSubmenuTile`.
* Add `FPopoverMenu.hover`.
* Add `FPopoverMenuStyle.motion`.
* Add `FPopoverMenuMotion`.
* Add `FPopoverMenuStyle.minWidth`.


### `FTextField`
* Add `FTextFieldStyle.cursorWidth`.
* Add `FTextFieldStyle.cursorOpacityAnimates`.


### `FTabs`
* Add swipe navigation when `expands` is true. When swipe navigation is enabled (i.e. `expands` is true and `swipeablePhysics` resolves to true), the content area's `physics` defaults to `BouncingScrollPhysics`.
* Add `FTabs.swipeablePhysics` to toggle swipe navigation independently from `scrollable`.
Expand All @@ -33,6 +37,10 @@
* Add `FTileMixin.submenu(...)` shorthand for `FSubmenuTile`.


### `FOtpField`
* Add `FOtpField`.


### `FOverlay`
* Add `FOverlay`.

Expand Down
18 changes: 16 additions & 2 deletions forui/bin/commands/style/style.dart

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions forui/lib/forui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export 'widgets/header.dart';
export 'widgets/item.dart';
export 'widgets/label.dart';
export 'widgets/line_calendar.dart';
export 'widgets/otp_field.dart';
export 'widgets/pagination.dart';
export 'widgets/picker.dart';
export 'widgets/popover.dart';
Expand Down
10 changes: 10 additions & 0 deletions forui/lib/src/theme/theme_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ final class FThemeData with Diagnosticable, _$FThemeDataFunctions {
@override
final FModalSheetStyle modalSheetStyle;

/// The OTP field style.
@override
final FOtpFieldStyle otpFieldStyle;

/// The pagination style.
///
/// ## CLI
Expand Down Expand Up @@ -610,6 +614,7 @@ final class FThemeData with Diagnosticable, _$FThemeDataFunctions {
FLineCalendarStyle? lineCalendarStyle,
FMultiSelectStyle? multiSelectStyle,
FModalSheetStyle? modalSheetStyle,
FOtpFieldStyle? otpFieldStyle,
FPaginationStyle? paginationStyle,
FPersistentSheetStyle? persistentSheetStyle,
FPickerStyle? pickerStyle,
Expand Down Expand Up @@ -692,6 +697,7 @@ final class FThemeData with Diagnosticable, _$FThemeDataFunctions {
multiSelectStyle:
multiSelectStyle ?? .inherit(colors: colors, typography: typography, style: style, touch: touch),
modalSheetStyle: modalSheetStyle ?? .inherit(colors: colors),
otpFieldStyle: otpFieldStyle ?? .inherit(colors: colors, typography: typography, style: style, touch: touch),
paginationStyle: paginationStyle ?? .inherit(colors: colors, typography: typography, style: style, touch: touch),
persistentSheetStyle: persistentSheetStyle ?? const FPersistentSheetStyle(),
pickerStyle: pickerStyle ?? .inherit(colors: colors, style: style, typography: typography),
Expand Down Expand Up @@ -820,6 +826,7 @@ final class FThemeData with Diagnosticable, _$FThemeDataFunctions {
lineCalendarStyle: a.lineCalendarStyle.lerp(b.lineCalendarStyle, t),
multiSelectStyle: a.multiSelectStyle.lerp(b.multiSelectStyle, t),
modalSheetStyle: a.modalSheetStyle.lerp(b.modalSheetStyle, t),
otpFieldStyle: a.otpFieldStyle.lerp(b.otpFieldStyle, t),
paginationStyle: a.paginationStyle.lerp(b.paginationStyle, t),
persistentSheetStyle: a.persistentSheetStyle.lerp(b.persistentSheetStyle, t),
pickerStyle: a.pickerStyle.lerp(b.pickerStyle, t),
Expand Down Expand Up @@ -903,6 +910,7 @@ final class FThemeData with Diagnosticable, _$FThemeDataFunctions {
required this.lineCalendarStyle,
required this.multiSelectStyle,
required this.modalSheetStyle,
required this.otpFieldStyle,
required this.paginationStyle,
required this.persistentSheetStyle,
required this.pickerStyle,
Expand Down Expand Up @@ -1389,6 +1397,7 @@ final class FThemeData with Diagnosticable, _$FThemeDataFunctions {
FLineCalendarStyleDelta? lineCalendarStyle,
FMultiSelectStyleDelta? multiSelectStyle,
FModalSheetStyleDelta? modalSheetStyle,
FOtpFieldStyleDelta? otpFieldStyle,
FPaginationStyleDelta? paginationStyle,
FPersistentSheetStyleDelta? persistentSheetStyle,
FPickerStyleDelta? pickerStyle,
Expand Down Expand Up @@ -1457,6 +1466,7 @@ final class FThemeData with Diagnosticable, _$FThemeDataFunctions {
lineCalendarStyle: lineCalendarStyle?.call(this.lineCalendarStyle) ?? this.lineCalendarStyle,
multiSelectStyle: multiSelectStyle?.call(this.multiSelectStyle) ?? this.multiSelectStyle,
modalSheetStyle: modalSheetStyle?.call(this.modalSheetStyle) ?? this.modalSheetStyle,
otpFieldStyle: otpFieldStyle?.call(this.otpFieldStyle) ?? this.otpFieldStyle,
paginationStyle: paginationStyle?.call(this.paginationStyle) ?? this.paginationStyle,
persistentSheetStyle: persistentSheetStyle?.call(this.persistentSheetStyle) ?? this.persistentSheetStyle,
pickerStyle: pickerStyle?.call(this.pickerStyle) ?? this.pickerStyle,
Expand Down
6 changes: 6 additions & 0 deletions forui/lib/src/widgets/autocomplete/autocomplete_style.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' show InputBorder;
import 'package:flutter/widgets.dart';
Expand Down Expand Up @@ -116,6 +118,8 @@ class FAutocompleteFieldStyle extends FTextFieldStyle with _$FAutocompleteFieldS
required super.descriptionTextStyle,
required super.errorTextStyle,
super.cursorColor,
super.cursorWidth,
super.cursorOpacityAnimates,
super.contentPadding,
super.clearButtonPadding,
super.obscureButtonPadding,
Expand All @@ -140,6 +144,8 @@ class FAutocompleteFieldStyle extends FTextFieldStyle with _$FAutocompleteFieldS
keyboardAppearance: field.keyboardAppearance,
color: field.color,
cursorColor: field.cursorColor,
cursorWidth: field.cursorWidth,
cursorOpacityAnimates: field.cursorOpacityAnimates,
contentPadding: field.contentPadding,
clearButtonPadding: field.clearButtonPadding,
obscureButtonPadding: field.obscureButtonPadding,
Expand Down
8 changes: 4 additions & 4 deletions forui/lib/src/widgets/item/item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -471,12 +471,12 @@ class FItemStyle with Diagnosticable, _$FItemStyleFunctions {
static ({EdgeInsetsGeometry suffixedPadding, EdgeInsetsGeometry unsuffixedPadding, EdgeInsetsGeometry margin})
menuInsets({required bool touch}) => touch
? (
suffixedPadding: const EdgeInsetsDirectional.fromSTEB(10, 12.5, 6, 12.5),
suffixedPadding: const .fromSTEB(10, 12.5, 6, 12.5),
unsuffixedPadding: const .symmetric(horizontal: 10, vertical: 12.5),
margin: const .symmetric(horizontal: 4),
)
: (
suffixedPadding: const EdgeInsetsDirectional.fromSTEB(10, 6.5, 5, 6.5),
suffixedPadding: const .fromSTEB(10, 6.5, 5, 6.5),
unsuffixedPadding: const .symmetric(horizontal: 10, vertical: 6.5),
margin: const .symmetric(horizontal: 4),
);
Expand All @@ -485,12 +485,12 @@ class FItemStyle with Diagnosticable, _$FItemStyleFunctions {
static ({EdgeInsetsGeometry suffixedPadding, EdgeInsetsGeometry unsuffixedPadding, EdgeInsetsGeometry margin})
selectInsets({required bool touch}) => touch
? (
suffixedPadding: const EdgeInsetsDirectional.fromSTEB(10, 12.5, 6, 12.5),
suffixedPadding: const .fromSTEB(10, 12.5, 6, 12.5),
unsuffixedPadding: const .symmetric(horizontal: 10, vertical: 12.5),
margin: const .symmetric(horizontal: 4),
)
: (
suffixedPadding: const EdgeInsetsDirectional.fromSTEB(10, 6.5, 5, 6.5),
suffixedPadding: const .fromSTEB(10, 6.5, 5, 6.5),
unsuffixedPadding: const .symmetric(horizontal: 10, vertical: 6.5),
margin: const .symmetric(horizontal: 4),
);
Expand Down
2 changes: 0 additions & 2 deletions forui/lib/src/widgets/item/item_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ mixin FItemMixin on Widget {
TraversalEdgeBehavior? submenuTraversalEdgeBehavior,
double submenuMaxHeight = .infinity,
FItemDivider submenuDivider = .full,
bool? hover,
Key? key,
}) => .new(
title: title,
Expand Down Expand Up @@ -145,7 +144,6 @@ mixin FItemMixin on Widget {
submenuTraversalEdgeBehavior: submenuTraversalEdgeBehavior,
submenuMaxHeight: submenuMaxHeight,
submenuDivider: submenuDivider,
hover: hover,
key: key,
);

Expand Down
Loading
Loading