mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-12 19:33:41 +00:00
fix(ui): improve cross-platform text input behavior (#1477)
This commit is contained in:
19 files changed
+305
-28
No files matched your search
@@ -5,6 +5,7 @@ import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
|
||||
import '../theme/app_design_tokens.dart';
|
||||
import '../theme/app_palette.dart';
|
||||
import '../../l10n/l10n.dart';
|
||||
import 'app_text_field.dart';
|
||||
|
||||
class AppHttpHeadersController extends ChangeNotifier {
|
||||
AppHttpHeadersController({Map<String, String>? headers, Iterable<String> defaultNames = const []}) {
|
||||
@@ -171,7 +172,7 @@ class _HeaderTextField extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final palette = AppPalette.of(context);
|
||||
return shad.TextField(
|
||||
return AppTextField(
|
||||
controller: controller,
|
||||
hintText: hintText,
|
||||
filled: true,
|
||||
|
||||
@@ -38,7 +38,7 @@ class AppLoadingButton extends StatelessWidget {
|
||||
children: [
|
||||
leading,
|
||||
const SizedBox(width: AppDesignTokens.space8),
|
||||
effectiveChild,
|
||||
Flexible(child: effectiveChild),
|
||||
],
|
||||
);
|
||||
final effectiveOnPressed = loading ? null : onPressed;
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
|
||||
|
||||
import 'app_text_field.dart';
|
||||
|
||||
/// A bounded numeric text field with the app's standard spinner controls.
|
||||
class AppNumberInput extends StatefulWidget {
|
||||
const AppNumberInput({
|
||||
@@ -80,7 +82,7 @@ class _AppNumberInputState extends State<AppNumberInput> {
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
_NumericalRangeFormatter(min: widget.min.toInt(), max: widget.max?.toInt()),
|
||||
];
|
||||
return shad.TextField(
|
||||
return AppTextField(
|
||||
key: widget.fieldKey,
|
||||
controller: widget.controller,
|
||||
hintText: widget.hintText,
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
|
||||
|
||||
import '../../core/utils/breakpoints.dart';
|
||||
import '../services/download_directory_picker.dart';
|
||||
import 'app_text_field.dart';
|
||||
import 'app_tooltip.dart';
|
||||
|
||||
enum AppPathPickerButtonStyle { ghost, outline }
|
||||
@@ -119,7 +120,7 @@ class _AppPathPickerFieldState extends State<AppPathPickerField> {
|
||||
onEnter: (_) => _syncTooltipPath(),
|
||||
child: AppTooltip(
|
||||
message: _tooltipPath,
|
||||
child: shad.TextField(
|
||||
child: AppTextField(
|
||||
key: widget.fieldKey,
|
||||
controller: widget.controller,
|
||||
hintText: widget.hintText,
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import 'package:flutter/material.dart' as material show AdaptiveTextSelectionToolbar;
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
|
||||
|
||||
/// The app-wide text field.
|
||||
///
|
||||
/// shadcn_flutter's custom context menu is rendered in a full-screen overlay
|
||||
/// that can cover the app with an opaque surface. Use Flutter's adaptive
|
||||
/// platform toolbar everywhere so long-press and right-click text actions have
|
||||
/// consistent native behavior on mobile, desktop, and web.
|
||||
class AppTextField extends shad.TextField {
|
||||
const AppTextField({
|
||||
super.key,
|
||||
super.groupId,
|
||||
super.controller,
|
||||
super.initialValue,
|
||||
super.focusNode,
|
||||
super.undoController,
|
||||
super.decoration,
|
||||
super.padding,
|
||||
super.placeholder,
|
||||
super.crossAxisAlignment,
|
||||
super.clearButtonSemanticLabel,
|
||||
super.keyboardType,
|
||||
super.textInputAction,
|
||||
super.textCapitalization,
|
||||
super.style,
|
||||
super.strutStyle,
|
||||
super.textAlign,
|
||||
super.textAlignVertical,
|
||||
super.textDirection,
|
||||
super.readOnly,
|
||||
super.showCursor,
|
||||
super.autofocus,
|
||||
super.obscuringCharacter,
|
||||
super.obscureText,
|
||||
super.autocorrect,
|
||||
super.smartDashesType,
|
||||
super.smartQuotesType,
|
||||
super.enableSuggestions,
|
||||
super.maxLines,
|
||||
super.minLines,
|
||||
super.expands,
|
||||
super.maxLength,
|
||||
super.maxLengthEnforcement,
|
||||
super.onChanged,
|
||||
super.onEditingComplete,
|
||||
super.onSubmitted,
|
||||
super.onTapOutside,
|
||||
super.onTapUpOutside,
|
||||
super.inputFormatters,
|
||||
super.enabled,
|
||||
super.cursorWidth,
|
||||
super.cursorHeight,
|
||||
super.cursorRadius,
|
||||
super.cursorOpacityAnimates,
|
||||
super.cursorColor,
|
||||
super.selectionHeightStyle,
|
||||
super.selectionWidthStyle,
|
||||
super.keyboardAppearance,
|
||||
super.scrollPadding,
|
||||
super.enableInteractiveSelection,
|
||||
super.selectionControls,
|
||||
super.dragStartBehavior,
|
||||
super.scrollController,
|
||||
super.scrollPhysics,
|
||||
super.onTap,
|
||||
super.autofillHints,
|
||||
super.clipBehavior,
|
||||
super.restorationId,
|
||||
super.stylusHandwritingEnabled,
|
||||
super.enableIMEPersonalizedLearning,
|
||||
super.contentInsertionConfiguration,
|
||||
super.contextMenuBuilder = appTextFieldContextMenuBuilder,
|
||||
super.hintText,
|
||||
super.border,
|
||||
super.borderRadius,
|
||||
super.filled,
|
||||
super.statesController,
|
||||
super.magnifierConfiguration,
|
||||
super.spellCheckConfiguration,
|
||||
super.features,
|
||||
super.submitFormatters,
|
||||
super.skipInputFeatureFocusTraversal,
|
||||
super.onDragSelectionStart,
|
||||
super.onDragSelectionUpdate,
|
||||
super.onDragSelectionEnd,
|
||||
});
|
||||
}
|
||||
|
||||
Widget appTextFieldContextMenuBuilder(BuildContext context, EditableTextState editableTextState) {
|
||||
// Match Flutter's platform text-menu policy without falling back to
|
||||
// shadcn_flutter's full-screen overlay. EditableText bypasses this builder
|
||||
// on Web while the browser context menu is enabled.
|
||||
if (SystemContextMenu.isSupportedByField(editableTextState)) {
|
||||
return SystemContextMenu.editableText(editableTextState: editableTextState);
|
||||
}
|
||||
return material.AdaptiveTextSelectionToolbar.editableText(editableTextState: editableTextState);
|
||||
}
|
||||
Reference in new issue
Block a user