fix(ui): improve cross-platform text input behavior (#1477)

This commit is contained in:
Levi authored and GitHub committed 2026-09-06 17:18:57 +08:00
1 parent 16f2d30476
commit d511e66d56
19 files changed
+305 -28

No files matched your search

@@ -6,6 +6,7 @@ import '../../../../l10n/l10n.dart';
import '../../../../shared/theme/app_design_tokens.dart';
import '../../../../shared/theme/app_palette.dart';
import '../../../../shared/widgets/app_loading_button.dart';
import '../../../../shared/widgets/app_text_field.dart';
class LoginForm extends StatelessWidget {
const LoginForm({
@@ -159,7 +160,7 @@ class _LoginField extends StatelessWidget {
style: TextStyle(color: palette.textPrimary, fontSize: 13, fontWeight: FontWeight.w600),
),
const SizedBox(height: AppDesignTokens.space8),
shad.TextField(
AppTextField(
key: fieldKey,
controller: controller,
focusNode: focusNode,
@@ -15,6 +15,7 @@ import '../../../../core/window/app_window_chrome.dart';
import '../../../../shared/theme/app_design_tokens.dart';
import '../../../../shared/theme/app_palette.dart';
import '../../../../shared/widgets/app_primary_button.dart';
import '../../../../shared/widgets/app_text_field.dart';
import '../../../../shared/widgets/app_tooltip.dart';
import '../../../../shared/widgets/app_toast.dart';
import '../../../../l10n/l10n.dart';
@@ -629,7 +630,7 @@ class _Toolbar extends StatelessWidget {
Widget build(BuildContext context) {
final palette = AppPalette.of(context);
final manualInstallBusy = state.busyExtensionIds.contains(ExtensionsController.manualInstallBusyKey);
final search = shad.TextField(
final search = AppTextField(
key: const ValueKey('extension-search-input'),
controller: searchController,
placeholder: Text(context.l10n.searchExtensions, style: TextStyle(color: palette.searchHint)),
@@ -826,7 +827,7 @@ class _InstallPopoverState extends State<_InstallPopover> {
style: TextStyle(color: palette.textPrimary, fontSize: 13, fontWeight: FontWeight.w700),
),
const SizedBox(height: 10),
shad.TextField(
AppTextField(
key: const ValueKey('extension-install-url-input'),
controller: widget.controller,
autofocus: true,
@@ -1222,9 +1223,9 @@ class _ExtensionSettingField extends StatelessWidget {
],
const SizedBox(height: 8),
if (setting.type == api_extension.SettingType.boolean)
shad.TextField(controller: controller, placeholder: Text(context.l10n.booleanValueHint))
AppTextField(controller: controller, placeholder: Text(context.l10n.booleanValueHint))
else
shad.TextField(
AppTextField(
controller: controller,
keyboardType: setting.type == api_extension.SettingType.number ? TextInputType.number : TextInputType.text,
),
@@ -5,6 +5,7 @@ import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
import '../../../../shared/theme/app_design_tokens.dart';
import '../../../../shared/theme/app_palette.dart';
import '../../../../shared/widgets/app_primary_button.dart';
import '../../../../shared/widgets/app_text_field.dart';
import '../../../../l10n/l10n.dart';
class TasksTopBar extends StatelessWidget {
@@ -110,7 +111,7 @@ class _SearchField extends StatelessWidget {
),
child: SizedBox(
height: 40,
child: shad.TextField(
child: AppTextField(
controller: controller,
style: TextStyle(color: palette.textPrimary, fontSize: 14, height: 1),
placeholder: Text(
@@ -24,6 +24,7 @@ import '../../../../shared/widgets/app_choice_segmented_control.dart';
import '../../../../shared/widgets/app_loading_button.dart';
import '../../../../shared/widgets/app_number_input.dart';
import '../../../../shared/widgets/app_path_picker_field.dart';
import '../../../../shared/widgets/app_text_field.dart';
import '../../../../shared/widgets/app_tooltip.dart';
import '../../../../shared/widgets/app_toast.dart';
import '../../../../shared/widgets/responsive_menu_layout.dart';
@@ -1849,7 +1850,7 @@ class _TextSettingControl extends StatelessWidget {
final desktop = MediaQuery.sizeOf(context).width >= Breakpoints.mobile;
return SizedBox(
width: desktop ? AppDesignTokens.settingsFormControlWidth : double.infinity,
child: shad.TextField(
child: AppTextField(
key: fieldKey,
controller: controller,
hintText: hintText,
@@ -1919,7 +1920,7 @@ class _HostPortControl extends StatelessWidget {
child: Row(
children: [
Expanded(
child: shad.TextField(
child: AppTextField(
key: hostKey,
controller: hostController,
hintText: context.l10n.server,
@@ -8,6 +8,7 @@ import '../../../../shared/theme/app_design_tokens.dart';
import '../../../../shared/theme/app_palette.dart';
import '../../../../shared/widgets/app_path_picker_field.dart';
import '../../../../shared/widgets/app_primary_button.dart';
import '../../../../shared/widgets/app_text_field.dart';
import '../../../../l10n/l10n.dart';
class DownloadCategoryDraft {
@@ -169,7 +170,7 @@ Future<DownloadCategoryDraft?> showDownloadCategoryDialog(
children: [
Text(dialogContext.l10n.categoryName, style: TextStyle(color: palette.textSecondary, fontSize: 12)),
const SizedBox(height: 6),
shad.TextField(key: const ValueKey('download-category-name'), controller: nameController),
AppTextField(key: const ValueKey('download-category-name'), controller: nameController),
const SizedBox(height: 14),
Text(dialogContext.l10n.categoryPath, style: TextStyle(color: palette.textSecondary, fontSize: 12)),
const SizedBox(height: 6),
@@ -9,6 +9,7 @@ import '../../../../shared/theme/app_palette.dart';
import '../../../../shared/widgets/app_loading_button.dart';
import '../../../../shared/widgets/app_path_picker_field.dart';
import '../../../../shared/widgets/app_primary_button.dart';
import '../../../../shared/widgets/app_text_field.dart';
import '../../../../l10n/l10n.dart';
class SettingsListEntry {
@@ -189,7 +190,7 @@ Future<String?> showTextSettingDialog(
Text(fieldLabel, style: TextStyle(color: palette.textSecondary, fontSize: 12)),
const SizedBox(height: 6),
if (pickPath == null)
shad.TextField(controller: controller)
AppTextField(controller: controller)
else
AppPathPickerField(
controller: controller,
@@ -302,7 +303,7 @@ Future<GithubMirrorDraft?> showGithubMirrorDialog(BuildContext context, {GithubM
const SizedBox(height: 14),
Text(dialogContext.l10n.githubMirrorUrl, style: TextStyle(color: palette.textSecondary, fontSize: 12)),
const SizedBox(height: 6),
shad.TextField(controller: controller),
AppTextField(controller: controller),
if (validationMessage != null) ...[
const SizedBox(height: 8),
Text(validationMessage!, style: TextStyle(color: palette.error, fontSize: 12)),
@@ -32,6 +32,7 @@ import '../../../../shared/widgets/app_http_headers_editor.dart';
import '../../../../shared/widgets/app_number_input.dart';
import '../../../../shared/widgets/app_path_picker_field.dart';
import '../../../../shared/widgets/app_primary_button.dart';
import '../../../../shared/widgets/app_text_field.dart';
import '../../../../shared/widgets/app_tooltip.dart';
import '../../../../shared/widgets/app_toast.dart';
import '../../../../l10n/l10n.dart';
@@ -257,7 +258,12 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
final page = Scaffold(
backgroundColor: palette.sideBg,
child: Padding(
padding: EdgeInsets.only(top: AppWindowChrome.reservesHeaderInset ? AppDesignTokens.windowHeaderHeight : 0),
key: const ValueKey('create-task-safe-content'),
padding: EdgeInsets.only(
top: AppWindowChrome.reservesHeaderInset
? AppDesignTokens.windowHeaderHeight
: MediaQuery.paddingOf(context).top,
),
child: Column(
children: [
Container(
@@ -300,7 +306,7 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
children: [
SizedBox(
height: 120,
child: TextField(
child: AppTextField(
controller: _urlController,
hintText: context.l10n.pasteDownloadLinks,
keyboardType: TextInputType.multiline,
@@ -645,7 +651,7 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
label: context.l10n.trackers,
child: SizedBox(
height: 96,
child: TextField(
child: AppTextField(
controller: _trackersController,
hintText: context.l10n.oneTrackerPerLine,
keyboardType: TextInputType.multiline,
@@ -1233,7 +1239,7 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
children: [
SizedBox(
height: 38,
child: TextField(
child: AppTextField(
key: const ValueKey('create-history-filter'),
controller: filterController,
placeholder: Text(
@@ -1660,7 +1666,7 @@ class _WindowTextField extends StatelessWidget {
@override
Widget build(BuildContext context) {
final palette = AppPalette.of(context);
return TextField(
return AppTextField(
controller: controller,
hintText: hintText,
keyboardType: keyboardType,
@@ -9,6 +9,7 @@ import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
import '../../../../shared/theme/app_design_tokens.dart';
import '../../../../shared/theme/app_palette.dart';
import '../../../../shared/widgets/app_copy_icon_button.dart';
import '../../../../shared/widgets/app_text_field.dart';
import '../../../../shared/widgets/app_tooltip.dart';
import '../../../../shared/widgets/detail/app_detail_surface.dart';
import '../../../../core/utils/text_wrap.dart';
@@ -512,7 +513,7 @@ class _EditableUrlBlock extends StatelessWidget {
children: [
SizedBox(
height: 76,
child: shad.TextField(
child: AppTextField(
controller: controller,
minLines: null,
maxLines: null,
@@ -4,6 +4,7 @@ import 'package:shadcn_flutter/shadcn_flutter.dart' hide Column, Expanded, Row;
import '../../../../shared/theme/app_palette.dart';
import '../../../../shared/widgets/app_http_headers_editor.dart';
import '../../../../shared/widgets/app_primary_button.dart';
import '../../../../shared/widgets/app_text_field.dart';
import '../../../../l10n/l10n.dart';
import '../../domain/task_record.dart';
@@ -47,7 +48,7 @@ Future<TaskUrlUpdate?> showTaskUpdateUrlDialog(BuildContext context, TaskRecord
children: [
Text(dialogContext.l10n.downloadLink, style: TextStyle(color: palette.textPrimary, fontSize: 12)),
const SizedBox(height: 6),
TextField(controller: urlController),
AppTextField(controller: urlController),
const SizedBox(height: 18),
AppHttpHeadersEditor(
controller: headersController,