diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8eef335..dcfdadf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
# 10.0.0
-* Migrate to 2.10.0
+* Migrate to 2.10.0.
+* Add shouldShowSelectionHandles and textSelectionGestureDetectorBuilder call back to define the behavior of handles and toolbar.
+* Shortcut support for web and desktop.
# 9.0.3
diff --git a/README-ZH.md b/README-ZH.md
index 55b4eba..eb8e949 100644
--- a/README-ZH.md
+++ b/README-ZH.md
@@ -2,9 +2,12 @@
[![pub package](https://img.shields.io/pub/v/extended_text_field.svg)](https://pub.dartlang.org/packages/extended_text_field) [![GitHub stars](https://img.shields.io/github/stars/fluttercandies/extended_text_field)](https://github.com/fluttercandies/extended_text_field/stargazers) [![GitHub forks](https://img.shields.io/github/forks/fluttercandies/extended_text_field)](https://github.com/fluttercandies/extended_text_field/network) [![GitHub license](https://img.shields.io/github/license/fluttercandies/extended_text_field)](https://github.com/fluttercandies/extended_text_field/blob/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/fluttercandies/extended_text_field)](https://github.com/fluttercandies/extended_text_field/issues)
+文档语言: [English](README.md) | 中文简体
+
官方输入框的扩展组件,支持图片,@某人,自定义文字背景。也支持自定义菜单和选择器。
-文档语言: [English](README.md) | [中文简体](README-ZH.md)
+[ExtendedTextField 在线 Demo](https://fluttercandies.github.io/extended_text_field/)
+
- [extended_text_field](#extended_text_field)
- [限制](#限制)
diff --git a/README.md b/README.md
index 70e1544..c485efa 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,11 @@
[![pub package](https://img.shields.io/pub/v/extended_text_field.svg)](https://pub.dartlang.org/packages/extended_text_field) [![GitHub stars](https://img.shields.io/github/stars/fluttercandies/extended_text_field)](https://github.com/fluttercandies/extended_text_field/stargazers) [![GitHub forks](https://img.shields.io/github/forks/fluttercandies/extended_text_field)](https://github.com/fluttercandies/extended_text_field/network) [![GitHub license](https://img.shields.io/github/license/fluttercandies/extended_text_field)](https://github.com/fluttercandies/extended_text_field/blob/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/fluttercandies/extended_text_field)](https://github.com/fluttercandies/extended_text_field/issues)
+Language: English | [中文简体](README-ZH.md)
+
Extended official text field to build special text like inline image, @somebody, custom background etc quickly.It also support to build custom seleciton toolbar and handles.
-Language: [English](README.md) | [中文简体](README-ZH.md)
+[Web demo for ExtendedTextField](https://fluttercandies.github.io/extended_text_field/)
- [extended_text_field](#extended_text_field)
- [Limitation](#limitation)
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 4b0da0b..a995197 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -173,7 +173,7 @@ linter:
- slash_for_doc_comments
# - sort_child_properties_last # not yet tested
- sort_constructors_first
- - sort_pub_dependencies
+ # - sort_pub_dependencies
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml
index 61b6c4d..4b0da0b 100644
--- a/example/analysis_options.yaml
+++ b/example/analysis_options.yaml
@@ -1,29 +1,207 @@
-# This file configures the analyzer, which statically analyzes Dart code to
-# check for errors, warnings, and lints.
+# Specify analysis options.
#
-# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
-# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
-# invoked from the command line by running `flutter analyze`.
+# Until there are meta linter rules, each desired lint must be explicitly enabled.
+# See: https://github.com/dart-lang/linter/issues/288
+#
+# For a list of lints, see: http://dart-lang.github.io/linter/lints/
+# See the configuration guide for more
+# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
+#
+# There are other similar analysis options files in the flutter repos,
+# which should be kept in sync with this file:
+#
+# - analysis_options.yaml (this file)
+# - packages/flutter/lib/analysis_options_user.yaml
+# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
+# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
+#
+# This file contains the analysis options used by Flutter tools, such as IntelliJ,
+# Android Studio, and the `flutter analyze` command.
-# The following line activates a set of recommended lints for Flutter apps,
-# packages, and plugins designed to encourage good coding practices.
-include: package:flutter_lints/flutter.yaml
+analyzer:
+ strong-mode:
+ implicit-casts: false
+ implicit-dynamic: false
+ errors:
+ # treat missing required parameters as a warning (not a hint)
+ missing_required_param: warning
+ # treat missing returns as a warning (not a hint)
+ missing_return: warning
+ # allow having TODOs in the code
+ todo: ignore
+ # Ignore analyzer hints for updating pubspecs when using Future or
+ # Stream and not importing dart:async
+ # Please see https://github.com/flutter/flutter/pull/24528 for details.
+ sdk_version_async_exported_from_core: ignore
+ # exclude:
+ # - "bin/cache/**"
+ # # the following two are relative to the stocks example and the flutter package respectively
+ # # see https://github.com/dart-lang/sdk/issues/28463
+ # - "lib/i18n/messages_*.dart"
+ # - "lib/src/http/**"
linter:
- # The lint rules applied to this project can be customized in the
- # section below to disable rules from the `package:flutter_lints/flutter.yaml`
- # included above or to enable additional rules. A list of all available lints
- # and their documentation is published at
- # https://dart-lang.github.io/linter/lints/index.html.
- #
- # Instead of disabling a lint rule for the entire project in the
- # section below, it can also be suppressed for a single line of code
- # or a specific dart file by using the `// ignore: name_of_lint` and
- # `// ignore_for_file: name_of_lint` syntax on the line or in the file
- # producing the lint.
rules:
- # avoid_print: false # Uncomment to disable the `avoid_print` rule
- # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
-
-# Additional information about this file can be found at
-# https://dart.dev/guides/language/analysis-options
+ # these rules are documented on and in the same order as
+ # the Dart Lint rules page to make maintenance easier
+ # https://github.com/dart-lang/linter/blob/master/example/all.yaml
+ - always_declare_return_types
+ - always_put_control_body_on_new_line
+ # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
+ - always_require_non_null_named_parameters
+ - always_specify_types
+ - annotate_overrides
+ # - avoid_annotating_with_dynamic # conflicts with always_specify_types
+ # - avoid_as # required for implicit-casts: true
+ - avoid_bool_literals_in_conditional_expressions
+ # - avoid_catches_without_on_clauses # we do this commonly
+ # - avoid_catching_errors # we do this commonly
+ - avoid_classes_with_only_static_members
+ # - avoid_double_and_int_checks # only useful when targeting JS runtime
+ - avoid_empty_else
+ # - avoid_equals_and_hash_code_on_mutable_classes # not yet tested
+ - avoid_field_initializers_in_const_classes
+ - avoid_function_literals_in_foreach_calls
+ # - avoid_implementing_value_types # not yet tested
+ - avoid_init_to_null
+ # - avoid_js_rounded_ints # only useful when targeting JS runtime
+ - avoid_null_checks_in_equality_operators
+ # - avoid_positional_boolean_parameters # not yet tested
+ # - avoid_print # not yet tested
+ # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
+ # - avoid_redundant_argument_values # not yet tested
+ - avoid_relative_lib_imports
+ - avoid_renaming_method_parameters
+ - avoid_return_types_on_setters
+ # - avoid_returning_null # there are plenty of valid reasons to return null
+ # - avoid_returning_null_for_future # not yet tested
+ - avoid_returning_null_for_void
+ # - avoid_returning_this # there are plenty of valid reasons to return this
+ # - avoid_setters_without_getters # not yet tested
+ # - avoid_shadowing_type_parameters # not yet tested
+ - avoid_single_cascade_in_expression_statements
+ - avoid_slow_async_io
+ - avoid_types_as_parameter_names
+ # - avoid_types_on_closure_parameters # conflicts with always_specify_types
+ # - avoid_unnecessary_containers # not yet tested
+ - avoid_unused_constructor_parameters
+ - avoid_void_async
+ # - avoid_web_libraries_in_flutter # not yet tested
+ - await_only_futures
+ - camel_case_extensions
+ - camel_case_types
+ - cancel_subscriptions
+ # - cascade_invocations # not yet tested
+ # - close_sinks # not reliable enough
+ # - comment_references # blocked on https://github.com/flutter/flutter/issues/20765
+ # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
+ - control_flow_in_finally
+ # - curly_braces_in_flow_control_structures # not yet tested
+ # - diagnostic_describe_all_properties # not yet tested
+ - directives_ordering
+ - empty_catches
+ - empty_constructor_bodies
+ - empty_statements
+ # - file_names # not yet tested
+ - flutter_style_todos
+ - hash_and_equals
+ - implementation_imports
+ # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
+ - iterable_contains_unrelated_type
+ # - join_return_with_assignment # not yet tested
+ - library_names
+ - library_prefixes
+ # - lines_longer_than_80_chars # not yet tested
+ - list_remove_unrelated_type
+ # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
+ # - missing_whitespace_between_adjacent_strings # not yet tested
+ - no_adjacent_strings_in_list
+ - no_duplicate_case_values
+ # - no_logic_in_create_state # not yet tested
+ # - no_runtimeType_toString # not yet tested
+ - non_constant_identifier_names
+ # - null_closures # not yet tested
+ # - omit_local_variable_types # opposite of always_specify_types
+ # - one_member_abstracts # too many false positives
+ # - only_throw_errors # https://github.com/flutter/flutter/issues/5792
+ - overridden_fields
+ - package_api_docs
+ - package_names
+ - package_prefixed_library_names
+ # - parameter_assignments # we do this commonly
+ - prefer_adjacent_string_concatenation
+ - prefer_asserts_in_initializer_lists
+ # - prefer_asserts_with_message # not yet tested
+ - prefer_collection_literals
+ - prefer_conditional_assignment
+ - prefer_const_constructors
+ - prefer_const_constructors_in_immutables
+ - prefer_const_declarations
+ - prefer_const_literals_to_create_immutables
+ # - prefer_constructors_over_static_methods # not yet tested
+ - prefer_contains
+ # - prefer_double_quotes # opposite of prefer_single_quotes
+ - prefer_equal_for_default_values
+ # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
+ - prefer_final_fields
+ - prefer_final_in_for_each
+ - prefer_final_locals
+ - prefer_for_elements_to_map_fromIterable
+ - prefer_foreach
+ # - prefer_function_declarations_over_variables # not yet tested
+ - prefer_generic_function_type_aliases
+ - prefer_if_elements_to_conditional_expressions
+ - prefer_if_null_operators
+ - prefer_initializing_formals
+ - prefer_inlined_adds
+ # - prefer_int_literals # not yet tested
+ # - prefer_interpolation_to_compose_strings # not yet tested
+ - prefer_is_empty
+ - prefer_is_not_empty
+ - prefer_is_not_operator
+ - prefer_iterable_whereType
+ # - prefer_mixin # https://github.com/dart-lang/language/issues/32
+ # - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932
+ # - prefer_relative_imports # not yet tested
+ - prefer_single_quotes
+ - prefer_spread_collections
+ - prefer_typing_uninitialized_variables
+ - prefer_void_to_null
+ # - provide_deprecation_message # not yet tested
+ # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
+ - recursive_getters
+ - slash_for_doc_comments
+ # - sort_child_properties_last # not yet tested
+ - sort_constructors_first
+ - sort_pub_dependencies
+ - sort_unnamed_constructors_first
+ - test_types_in_equals
+ - throw_in_finally
+ # - type_annotate_public_apis # subset of always_specify_types
+ - type_init_formals
+ # - unawaited_futures # too many false positives
+ # - unnecessary_await_in_return # not yet tested
+ - unnecessary_brace_in_string_interps
+ - unnecessary_const
+ # - unnecessary_final # conflicts with prefer_final_locals
+ - unnecessary_getters_setters
+ # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
+ - unnecessary_new
+ - unnecessary_null_aware_assignments
+ - unnecessary_null_in_if_null_operators
+ - unnecessary_overrides
+ - unnecessary_parenthesis
+ - unnecessary_statements
+ - unnecessary_string_interpolations
+ - unnecessary_this
+ - unrelated_type_equality_checks
+ # - unsafe_html # not yet tested
+ - use_full_hex_values_for_flutter_colors
+ # - use_function_type_syntax_for_parameters # not yet tested
+ # - use_key_in_widget_constructors # not yet tested
+ - use_rethrow_when_possible
+ # - use_setters_to_change_properties # not yet tested
+ # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
+ # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
+ - valid_regexps
+ - void_checks
diff --git a/example/lib/pages/complex/text_demo.dart b/example/lib/pages/complex/text_demo.dart
index aa14724..448694f 100644
--- a/example/lib/pages/complex/text_demo.dart
+++ b/example/lib/pages/complex/text_demo.dart
@@ -127,6 +127,14 @@ class _TextDemoState extends State {
//EditableText(controller: controller, focusNode: focusNode, style: style, cursorColor: cursorColor, backgroundCursorColor: backgroundCursorColor)
ExtendedTextField(
key: _key,
+ // StrutStyle get strutStyle {
+ // if (_strutStyle == null) {
+ // return StrutStyle.fromTextStyle(style, forceStrutHeight: true);
+ // }
+ // return _strutStyle!.inheritFromTextStyle(style);
+ // }
+ // default strutStyle is not good for WidgetSpan
+ strutStyle: const StrutStyle(),
specialTextSpanBuilder: MySpecialTextSpanBuilder(
showAtBackground: true,
),
diff --git a/example/lib/pages/main_page.dart b/example/lib/pages/main_page.dart
index 75c097d..dfdbcf0 100644
--- a/example/lib/pages/main_page.dart
+++ b/example/lib/pages/main_page.dart
@@ -1,6 +1,7 @@
import 'package:collection/collection.dart';
import 'package:example/example_routes.dart';
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -55,17 +56,18 @@ class MainPage extends StatelessWidget {
},
),
),
- ButtonTheme(
- padding: const EdgeInsets.only(right: 10.0),
- minWidth: 0.0,
- child: TextButton(
- child:
- Image.network('https://pub.idqqimg.com/wpa/images/group.png'),
- onPressed: () {
- launch('https://jq.qq.com/?_wv=1027&k=5bcc0gy');
- },
- ),
- )
+ if (!kIsWeb)
+ ButtonTheme(
+ padding: const EdgeInsets.only(right: 10.0),
+ minWidth: 0.0,
+ child: TextButton(
+ child: Image.network(
+ 'https://pub.idqqimg.com/wpa/images/group.png'),
+ onPressed: () {
+ launch('https://jq.qq.com/?_wv=1027&k=5bcc0gy');
+ },
+ ),
+ )
],
),
body: ListView.builder(
diff --git a/example/lib/pages/simple/custom_toolbar.dart b/example/lib/pages/simple/custom_toolbar.dart
index a0f20a3..c57daf0 100644
--- a/example/lib/pages/simple/custom_toolbar.dart
+++ b/example/lib/pages/simple/custom_toolbar.dart
@@ -1,3 +1,5 @@
+// ignore_for_file: always_put_control_body_on_new_line
+
import 'package:example/special_text/my_extended_text_selection_controls.dart';
import 'package:example/special_text/my_special_text_span_builder.dart';
import 'package:extended_text_field/extended_text_field.dart';
@@ -47,9 +49,91 @@ class _CustomToolBarState extends State {
specialTextSpanBuilder: _mySpecialTextSpanBuilder,
controller: controller,
maxLines: null,
+ // StrutStyle get strutStyle {
+ // if (_strutStyle == null) {
+ // return StrutStyle.fromTextStyle(style, forceStrutHeight: true);
+ // }
+ // return _strutStyle!.inheritFromTextStyle(style);
+ // }
+ // default strutStyle is not good for WidgetSpan
+ strutStyle: const StrutStyle(),
+ shouldShowSelectionHandles: _shouldShowSelectionHandles,
+ textSelectionGestureDetectorBuilder: ({
+ required ExtendedTextSelectionGestureDetectorBuilderDelegate
+ delegate,
+ required Function showToolbar,
+ required Function hideToolbar,
+ required Function? onTap,
+ required BuildContext context,
+ required Function? requestKeyboard,
+ }) {
+ return MyCommonTextSelectionGestureDetectorBuilder(
+ delegate: delegate,
+ showToolbar: showToolbar,
+ hideToolbar: hideToolbar,
+ onTap: onTap,
+ context: context,
+ requestKeyboard: requestKeyboard,
+ );
+ },
),
),
),
);
}
+
+ bool _shouldShowSelectionHandles(
+ SelectionChangedCause? cause,
+ CommonTextSelectionGestureDetectorBuilder selectionGestureDetectorBuilder,
+ TextEditingValue editingValue,
+ ) {
+ // When the text field is activated by something that doesn't trigger the
+ // selection overlay, we shouldn't show the handles either.
+
+ //
+ // 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) return true;
+
+ if (editingValue.text.isNotEmpty) return true;
+
+ return false;
+ }
+}
+
+class MyCommonTextSelectionGestureDetectorBuilder
+ extends CommonTextSelectionGestureDetectorBuilder {
+ MyCommonTextSelectionGestureDetectorBuilder(
+ {required ExtendedTextSelectionGestureDetectorBuilderDelegate delegate,
+ required Function showToolbar,
+ required Function hideToolbar,
+ required Function? onTap,
+ required BuildContext context,
+ required Function? requestKeyboard})
+ : super(
+ delegate: delegate,
+ showToolbar: showToolbar,
+ hideToolbar: hideToolbar,
+ onTap: onTap,
+ context: context,
+ requestKeyboard: requestKeyboard,
+ );
+ @override
+ void onTapDown(TapDownDetails details) {
+ super.onTapDown(details);
+
+ /// always show toolbar
+ shouldShowSelectionToolbar = true;
+ }
+
+ @override
+ bool get showToolbarInWeb => true;
}
diff --git a/example/lib/pages/simple/widget_span.dart b/example/lib/pages/simple/widget_span.dart
index 99675f0..b60b5c4 100644
--- a/example/lib/pages/simple/widget_span.dart
+++ b/example/lib/pages/simple/widget_span.dart
@@ -64,6 +64,14 @@ class _WidgetSpanDemoState extends State {
controller: controller,
specialTextSpanBuilder: _emailSpanBuilder,
maxLines: null,
+ // StrutStyle get strutStyle {
+ // if (_strutStyle == null) {
+ // return StrutStyle.fromTextStyle(style, forceStrutHeight: true);
+ // }
+ // return _strutStyle!.inheritFromTextStyle(style);
+ // }
+ // default strutStyle is not good for WidgetSpan
+ strutStyle: const StrutStyle(),
decoration: InputDecoration(
suffixIcon: IconButton(
icon: const Icon(Icons.add),
diff --git a/example/lib/special_text/emoji_text.dart b/example/lib/special_text/emoji_text.dart
index 8ea80e7..406ae81 100644
--- a/example/lib/special_text/emoji_text.dart
+++ b/example/lib/special_text/emoji_text.dart
@@ -1,5 +1,4 @@
import 'package:extended_text_library/extended_text_library.dart';
-import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
///emoji/image text
@@ -12,9 +11,7 @@ class EmojiText extends SpecialText {
InlineSpan finishText() {
final String key = toString();
- ///https://github.com/flutter/flutter/issues/42086
- /// widget span is not working on web
- if (EmojiUitl.instance.emojiMap.containsKey(key) && !kIsWeb) {
+ if (EmojiUitl.instance.emojiMap.containsKey(key)) {
//fontsize id define image height
//size = 30.0/26.0 * fontSize
const double size = 20.0;
diff --git a/example/lib/special_text/my_special_text_span_builder.dart b/example/lib/special_text/my_special_text_span_builder.dart
index e623aa7..ae1bbe7 100644
--- a/example/lib/special_text/my_special_text_span_builder.dart
+++ b/example/lib/special_text/my_special_text_span_builder.dart
@@ -1,6 +1,5 @@
import 'package:example/special_text/image_text.dart';
import 'package:extended_text_library/extended_text_library.dart';
-import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'at_text.dart';
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index f89209e..eafa0d2 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -15,7 +15,7 @@ version: 1.0.0+1
environment:
sdk: '>=2.12.0 <3.0.0'
- flutter: ">=2.8.0"
+ flutter: ">=2.10.0"
dependencies:
# The following adds the Cupertino Icons font to your application.
@@ -24,8 +24,8 @@ dependencies:
cupertino_icons: ^1.0.4
- extended_text_field:
- path: ../
+ extended_text: ^9.0.0
+ extended_text_library: ^9.0.0
ff_annotation_route_library: ^3.0.0
flutter:
sdk: flutter
@@ -38,7 +38,9 @@ dev_dependencies:
flutter_test:
sdk: flutter
dependency_overrides:
-
+ extended_text_field:
+ path: ../
+
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
diff --git a/lib/src/extended_editable_text.dart b/lib/src/extended_editable_text.dart
index 4088681..b054c0c 100644
--- a/lib/src/extended_editable_text.dart
+++ b/lib/src/extended_editable_text.dart
@@ -177,6 +177,7 @@ class ExtendedEditableText extends StatefulWidget {
this.restorationId,
this.scrollBehavior,
this.enableIMEPersonalizedLearning = true,
+ this.showToolbarInWeb = false,
}) : assert(controller != null),
assert(focusNode != null),
assert(obscuringCharacter != null && obscuringCharacter.length == 1),
@@ -233,6 +234,8 @@ class ExtendedEditableText extends StatefulWidget {
showCursor = showCursor ?? !readOnly,
super(key: key);
+ final bool showToolbarInWeb;
+
///build your ccustom text span
final SpecialTextSpanBuilder? specialTextSpanBuilder;
@@ -596,7 +599,7 @@ class ExtendedEditableText extends StatefulWidget {
///
/// For [CupertinoTextField]s, the value is set to the ambient
/// [CupertinoThemeData.primaryColor] with 20% opacity. For [TextField]s, the
- /// value is set to the ambient [ThemeData.textSelectionColor].
+ /// value is set to the ambient [TextSelectionThemeData.selectionColor].
final Color? selectionColor;
/// {@template flutter.widgets.editableText.selectionControls}
@@ -1233,8 +1236,7 @@ class ExtendedEditableTextState extends State
final ValueNotifier _cursorVisibilityNotifier =
ValueNotifier(true);
final GlobalKey _editableKey = GlobalKey();
- final ClipboardStatusNotifier? _clipboardStatus =
- kIsWeb ? null : ClipboardStatusNotifier();
+ ClipboardStatusNotifier? _clipboardStatus;
TextInputConnection? _textInputConnection;
ExtendedTextSelectionOverlay? _selectionOverlay;
@@ -1426,6 +1428,8 @@ class ExtendedEditableTextState extends State
vsync: this,
duration: _fadeDuration,
)..addListener(_onCursorColorTick);
+ _clipboardStatus =
+ kIsWeb && !widget.showToolbarInWeb ? null : ClipboardStatusNotifier();
_clipboardStatus?.addListener(_onChangedClipboardStatus);
widget.controller.addListener(_didChangeTextEditingValue);
widget.focusNode.addListener(_handleFocusChanged);
@@ -1531,6 +1535,11 @@ class ExtendedEditableTextState extends State
widget.selectionControls?.canPaste(this) == true) {
_clipboardStatus?.update();
}
+
+ if (oldWidget.showToolbarInWeb != widget.showToolbarInWeb) {
+ _clipboardStatus =
+ kIsWeb && !widget.showToolbarInWeb ? null : ClipboardStatusNotifier();
+ }
}
@override
@@ -2585,12 +2594,12 @@ class ExtendedEditableTextState extends State
///
/// Returns `false` if a toolbar couldn't be shown, such as when the toolbar
/// is already shown, or when no text selection currently exists.
- bool showToolbar() {
+ bool showToolbar({bool showToolbarInWeb = false}) {
// Web is using native dom elements to enable clipboard functionality of the
// toolbar: copy, paste, select, cut. It might also provide additional
// functionality depending on the browser (such as translate). Due to this
// we should not show a Flutter toolbar for the editable text elements.
- if (kIsWeb) {
+ if (kIsWeb && !showToolbarInWeb) {
return false;
}
diff --git a/lib/src/extended_text_field.dart b/lib/src/extended_text_field.dart
index 51a4ef4..35d80ed 100644
--- a/lib/src/extended_text_field.dart
+++ b/lib/src/extended_text_field.dart
@@ -241,6 +241,8 @@ class ExtendedTextField extends StatefulWidget {
this.clipBehavior = Clip.hardEdge,
this.restorationId,
this.enableIMEPersonalizedLearning = true,
+ this.shouldShowSelectionHandles,
+ this.textSelectionGestureDetectorBuilder,
}) : assert(textAlign != null),
assert(readOnly != null),
assert(autofocus != null),
@@ -298,7 +300,16 @@ class ExtendedTextField extends StatefulWidget {
)),
super(key: key);
- ///build your ccustom text span
+ /// create custom TextSelectionGestureDetectorBuilder
+ final TextSelectionGestureDetectorBuilderCallback?
+ textSelectionGestureDetectorBuilder;
+
+ /// Whether should show selection handles
+ /// handles are not shown in desktop or web as default
+ /// you can define your behavior
+ final ShouldShowSelectionHandlesCallback? shouldShowSelectionHandles;
+
+ /// build your ccustom text span
final SpecialTextSpanBuilder? specialTextSpanBuilder;
/// Controls the text being edited.
@@ -900,19 +911,8 @@ class _ExtendedTextFieldState extends State
@override
void initState() {
super.initState();
- _selectionGestureDetectorBuilder =
- CommonTextSelectionGestureDetectorBuilder(
- delegate: this,
- hideToolbar: () {
- _editableText!.hideToolbar();
- },
- showToolbar: () {
- _editableText!.showToolbar();
- },
- onTap: widget.onTap,
- context: context,
- requestKeyboard: _requestKeyboard,
- );
+ _initGestureDetectorBuilder();
+
if (widget.controller == null) {
_createLocalController();
}
@@ -920,6 +920,42 @@ class _ExtendedTextFieldState extends State
_effectiveFocusNode.addListener(_handleFocusChanged);
}
+ void _initGestureDetectorBuilder() {
+ if (widget.textSelectionGestureDetectorBuilder != null) {
+ _selectionGestureDetectorBuilder =
+ widget.textSelectionGestureDetectorBuilder!(
+ delegate: this,
+ hideToolbar: () {
+ _editableText!.hideToolbar();
+ },
+ showToolbar: () {
+ _editableText!.showToolbar(
+ showToolbarInWeb: _selectionGestureDetectorBuilder.showToolbarInWeb,
+ );
+ },
+ onTap: widget.onTap,
+ context: context,
+ requestKeyboard: _requestKeyboard,
+ );
+ } else {
+ _selectionGestureDetectorBuilder =
+ CommonTextSelectionGestureDetectorBuilder(
+ delegate: this,
+ hideToolbar: () {
+ _editableText!.hideToolbar();
+ },
+ showToolbar: () {
+ _editableText!.showToolbar(
+ showToolbarInWeb: _selectionGestureDetectorBuilder.showToolbarInWeb,
+ );
+ },
+ onTap: widget.onTap,
+ context: context,
+ requestKeyboard: _requestKeyboard,
+ );
+ }
+ }
+
bool get _canRequestFocus {
final NavigationMode mode = MediaQuery.maybeOf(context)?.navigationMode ??
NavigationMode.traditional;
@@ -962,6 +998,9 @@ class _ExtendedTextFieldState extends State
_showSelectionHandles = !widget.readOnly;
}
}
+ if (widget.textSelectionGestureDetectorBuilder !=
+ oldWidget.textSelectionGestureDetectorBuilder)
+ _initGestureDetectorBuilder();
}
@override
@@ -1004,6 +1043,13 @@ class _ExtendedTextFieldState extends State
}
bool _shouldShowSelectionHandles(SelectionChangedCause? cause) {
+ if (widget.shouldShowSelectionHandles != null) {
+ return widget.shouldShowSelectionHandles!(
+ cause,
+ _selectionGestureDetectorBuilder,
+ _editableText!.textEditingValue,
+ );
+ }
// When the text field is activated by something that doesn't trigger the
// selection overlay, we shouldn't show the handles either.
if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar)
@@ -1116,8 +1162,8 @@ class _ExtendedTextFieldState extends State
final TextSelectionThemeData selectionTheme =
TextSelectionTheme.of(context);
final TextStyle style = theme.textTheme.subtitle1!.merge(widget.style);
- final Brightness keyboardAppearance =
- widget.keyboardAppearance ?? theme.primaryColorBrightness;
+ final Brightness keyboardAppearance = widget.keyboardAppearance ??
+ ThemeData.estimateBrightnessForColor(theme.primaryColor);
final TextEditingController controller = _effectiveController;
final FocusNode focusNode = _effectiveFocusNode;
final List formatters = [
@@ -1278,8 +1324,9 @@ class _ExtendedTextFieldState extends State
autofillClient: this,
autocorrectionTextRectColor: autocorrectionTextRectColor,
clipBehavior: widget.clipBehavior,
- restorationId: 'editable',
+ restorationId: 'ExtendedEditableText',
enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning,
+ showToolbarInWeb: _selectionGestureDetectorBuilder.showToolbarInWeb,
),
),
);