Skip to content

Commit

Permalink
TF-1884 Support RTL mode for TypeAheadFormField
Browse files Browse the repository at this point in the history
  • Loading branch information
dab246 committed Jun 14, 2023
1 parent 1dd10cf commit 1f33f6d
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 50 deletions.
110 changes: 110 additions & 0 deletions core/lib/presentation/views/text/type_ahead_form_field_builder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import 'package:core/utils/direction_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';

class TypeAheadFormFieldBuilder<T> extends StatefulWidget {

final TextDirection textDirection;
final Duration debounceDuration;
final SuggestionsCallback<T> suggestionsCallback;
final ItemBuilder<T> itemBuilder;
final SuggestionSelectionCallback<T> onSuggestionSelected;
final SuggestionsBoxDecoration suggestionsBoxDecoration;
final WidgetBuilder? noItemsFoundBuilder;
final bool hideOnEmpty;
final bool hideOnError;
final bool hideOnLoading;
final TextEditingController? controller;
final FocusNode? focusNode;
final ValueChanged<String>? onTextChange;
final ValueChanged<String>? onTextSubmitted;
final TextInputAction? textInputAction;
final bool autocorrect;
final List<String>? autofillHints;
final TextInputType keyboardType;
final InputDecoration decoration;

const TypeAheadFormFieldBuilder({
super.key,
required this.suggestionsCallback,
required this.itemBuilder,
required this.onSuggestionSelected,
this.suggestionsBoxDecoration = const SuggestionsBoxDecoration(),
this.textDirection = TextDirection.ltr,
this.debounceDuration = const Duration(milliseconds: 300),
this.decoration = const InputDecoration(),
this.noItemsFoundBuilder,
this.hideOnEmpty = false,
this.hideOnError = false,
this.hideOnLoading = false,
this.autocorrect = false,
this.keyboardType = TextInputType.text,
this.controller,
this.focusNode,
this.autofillHints,
this.textInputAction,
this.onTextChange,
this.onTextSubmitted,
});

@override
State<TypeAheadFormFieldBuilder<T>> createState() => _TypeAheadFormFieldBuilderState<T>();
}

class _TypeAheadFormFieldBuilderState<T> extends State<TypeAheadFormFieldBuilder<T>> {

late TextEditingController _controller;
late TextDirection _textDirection;

@override
void initState() {
_textDirection = widget.textDirection;
_controller = widget.controller ?? TextEditingController();
super.initState();
}

@override
Widget build(BuildContext context) {
return TypeAheadFormField<T>(
key: widget.key,
textFieldConfiguration: TextFieldConfiguration(
controller: widget.controller,
textInputAction: widget.textInputAction,
autocorrect: widget.autocorrect,
autofillHints: widget.autofillHints,
keyboardType: widget.keyboardType,
decoration: widget.decoration,
textDirection: _textDirection,
onChanged: (value) {
widget.onTextChange?.call(value);
if (value.isNotEmpty) {
final directionByText = DirectionUtils.getDirectionByEndsText(value);
if (directionByText != _textDirection) {
setState(() {
_textDirection = directionByText;
});
}
}
},
onSubmitted: widget.onTextSubmitted
),
debounceDuration: widget.debounceDuration,
suggestionsCallback: widget.suggestionsCallback,
itemBuilder: widget.itemBuilder,
onSuggestionSelected: widget.onSuggestionSelected,
suggestionsBoxDecoration: widget.suggestionsBoxDecoration,
noItemsFoundBuilder: widget.noItemsFoundBuilder,
hideOnEmpty: widget.hideOnEmpty,
hideOnError: widget.hideOnError,
hideOnLoading: widget.hideOnLoading,
);
}

@override
void dispose() {
if (widget.controller == null) {
_controller.dispose();
}
super.dispose();
}
}
12 changes: 10 additions & 2 deletions core/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,10 @@ packages:
dependency: "direct main"
description:
name: flutter_keyboard_visibility
sha256: "86b71bbaffa38e885f5c21b1182408b9be6951fd125432cf6652c636254cef2d"
sha256: "4983655c26ab5b959252ee204c2fffa4afeb4413cd030455194ec0caa3b8e7cb"
url: "https://pub.dev"
source: hosted
version: "5.4.0"
version: "5.4.1"
flutter_keyboard_visibility_linux:
dependency: transitive
description:
Expand Down Expand Up @@ -259,6 +259,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_typeahead:
dependency: "direct main"
description:
name: flutter_typeahead
sha256: f31211a8536f87908c3dcbdb88666e2f4d77f5f06c2b3a48eaad5599969ff32d
url: "https://pub.dev"
source: hosted
version: "4.6.0"
flutter_web_plugins:
dependency: transitive
description: flutter
Expand Down
6 changes: 6 additions & 0 deletions core/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ dependencies:
# flutter_localizations depends on intl 0.17.0
intl: 0.17.0

flutter_typeahead: 4.6.0

dev_dependencies:
flutter_test:
sdk: flutter
Expand All @@ -83,6 +85,10 @@ dev_dependencies:
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

dependency_overrides:

flutter_keyboard_visibility: 5.4.1

flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@

import 'package:core/core.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
Expand Down Expand Up @@ -40,18 +42,16 @@ class IdentityInputWithDropListFieldBuilder extends StatelessWidget {
fontWeight: FontWeight.normal,
color: AppColor.colorContentEmail)),
const SizedBox(height: 8),
TypeAheadFormField<EmailAddress>(
textFieldConfiguration: TextFieldConfiguration(
focusNode: focusNode,
controller: editingController,
textInputAction: TextInputAction.done,
decoration: (IdentityInputDecorationBuilder()
..setContentPadding(const EdgeInsets.symmetric(
vertical: BuildUtils.isWeb ? 16 : 12,
horizontal: 12))
..setErrorText(_error))
.build()
),
TypeAheadFormFieldBuilder<EmailAddress>(
focusNode: focusNode,
controller: editingController,
textInputAction: TextInputAction.done,
decoration: (IdentityInputDecorationBuilder()
..setContentPadding(const EdgeInsets.symmetric(
vertical: PlatformInfo.isWeb ? 16 : 12,
horizontal: 12))
..setErrorText(_error))
.build(),
debounceDuration: const Duration(milliseconds: 500),
suggestionsCallback: (pattern) async {
if (onChangeInputSuggestionAction != null) {
Expand Down
35 changes: 15 additions & 20 deletions lib/features/login/presentation/base_login_view.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:get/get.dart';
Expand Down Expand Up @@ -106,32 +107,26 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
Widget buildUserNameInput(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 24, right: 24, left: 24),
child: TypeAheadFormField<RecentLoginUsername>(
child: TypeAheadFormFieldBuilder<RecentLoginUsername>(
key: const Key('login_username_input'),
textFieldConfiguration: TextFieldConfiguration(
controller: loginController.usernameInputController,
onChanged: (value) => loginController.setUserNameText(value),
textInputAction: TextInputAction.next,
autocorrect: false,
autofillHints: [AutofillHints.email],
keyboardType: TextInputType.emailAddress,
decoration: (LoginInputDecorationBuilder()
..setLabelText(AppLocalizations.of(context).email)
..setHintText(AppLocalizations.of(context).email))
.build(),
),
controller: loginController.usernameInputController,
onTextChange: loginController.setUserNameText,
textInputAction: TextInputAction.next,
autocorrect: false,
autofillHints: const [AutofillHints.email],
keyboardType: TextInputType.emailAddress,
decoration: (LoginInputDecorationBuilder()
..setLabelText(AppLocalizations.of(context).email)
..setHintText(AppLocalizations.of(context).email))
.build(),
debounceDuration: const Duration(milliseconds: 300),
suggestionsCallback: (pattern) async {
return await loginController.getAllRecentLoginUsernameAction(pattern);
},
itemBuilder: (context, loginUsername) =>
RecentItemTileWidget(loginUsername, imagePath: imagePaths),
suggestionsCallback: loginController.getAllRecentLoginUsernameAction,
itemBuilder: (context, loginUsername) => RecentItemTileWidget(loginUsername, imagePath: imagePaths),
onSuggestionSelected: (recentUsername) {
loginController.setUsername(recentUsername.username);
passFocusNode.requestFocus();
},
suggestionsBoxDecoration: const SuggestionsBoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(14))),
suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))),
noItemsFoundBuilder: (context) => const SizedBox(),
hideOnEmpty: true,
hideOnError: true,
Expand Down
27 changes: 12 additions & 15 deletions lib/features/login/presentation/login_view.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
Expand Down Expand Up @@ -135,27 +136,23 @@ class LoginView extends BaseLoginView {
Widget _buildUrlInput(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(right: 24, left: 24, bottom: 24),
child: TypeAheadFormField<RecentLoginUrl>(
textFieldConfiguration: TextFieldConfiguration(
controller: loginController.urlInputController,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.url,
onSubmitted: (value) => controller.handleNextInUrlInputFormPress(),
decoration: (LoginInputDecorationBuilder()
..setLabelText(AppLocalizations.of(context).prefix_https)
..setPrefixText(AppLocalizations.of(context).prefix_https))
.build()
),
child: TypeAheadFormFieldBuilder<RecentLoginUrl>(
controller: loginController.urlInputController,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.url,
onTextSubmitted: (value) => controller.handleNextInUrlInputFormPress(),
decoration: (LoginInputDecorationBuilder()
..setLabelText(AppLocalizations.of(context).prefix_https)
..setPrefixText(AppLocalizations.of(context).prefix_https))
.build(),
debounceDuration: const Duration(milliseconds: 300),
suggestionsCallback: (pattern) async {
loginController.formatUrl(pattern);
return loginController.getAllRecentLoginUrlAction(pattern);
},
itemBuilder: (context, loginUrl) =>
RecentItemTileWidget(loginUrl, imagePath: imagePaths),
itemBuilder: (context, loginUrl) => RecentItemTileWidget(loginUrl, imagePath: imagePaths),
onSuggestionSelected: (loginUrl) => controller.formatUrl(loginUrl.url),
suggestionsBoxDecoration: const SuggestionsBoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(14))),
suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))),
noItemsFoundBuilder: (context) => const SizedBox(),
hideOnEmpty: true,
hideOnError: true,
Expand Down

0 comments on commit 1f33f6d

Please sign in to comment.