mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-07 00:50:27 +00:00
fix(ui): unify text field appearance
This commit is contained in:
1 parent
ecfd07ccb0
commit
a834ca0d74
7 files changed
+83
-57
No files matched your search
@@ -356,18 +356,12 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
|
||||
const SizedBox(height: 16),
|
||||
_FormRow(
|
||||
label: context.l10n.connections,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: SizedBox(
|
||||
width: AppDesignTokens.settingsNumberControlWidth,
|
||||
child: AppNumberInput(
|
||||
fieldKey: const ValueKey('create-task-connections-input'),
|
||||
controller: _connectionsController,
|
||||
min: 1,
|
||||
max: 256,
|
||||
hintText: context.l10n.enterCount,
|
||||
),
|
||||
),
|
||||
child: AppNumberInput(
|
||||
fieldKey: const ValueKey('create-task-connections-input'),
|
||||
controller: _connectionsController,
|
||||
min: 1,
|
||||
max: 256,
|
||||
hintText: context.l10n.enterCount,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -382,10 +376,6 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
|
||||
pickerKey: const ValueKey('create-task-directory-picker'),
|
||||
controller: _directoryController,
|
||||
hintText: context.l10n.chooseDownloadDirectory,
|
||||
filled: true,
|
||||
border: Border.all(color: palette.border),
|
||||
borderRadius: BorderRadius.circular(AppDesignTokens.controlRadius),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
onChanged: (_) {
|
||||
if (_asDefaultPath) {
|
||||
setState(() => _asDefaultPath = false);
|
||||
@@ -1654,17 +1644,12 @@ class _WindowTextField extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final palette = AppPalette.of(context);
|
||||
return AppTextField(
|
||||
controller: controller,
|
||||
hintText: hintText,
|
||||
keyboardType: keyboardType,
|
||||
inputFormatters: inputFormatters,
|
||||
obscureText: obscureText,
|
||||
filled: true,
|
||||
border: Border.all(color: palette.border),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,42 +18,50 @@ class AppComponentThemes extends StatelessWidget {
|
||||
align: 1,
|
||||
border: Border.all(color: theme.colorScheme.ring.withValues(alpha: 0.65), width: 1),
|
||||
),
|
||||
child: shad.ComponentTheme<shad.SecondaryButtonTheme>(
|
||||
data: shad.SecondaryButtonTheme(
|
||||
decoration: (context, states, value) {
|
||||
final decoration = switch (value) {
|
||||
final BoxDecoration decoration => decoration,
|
||||
_ => BoxDecoration(borderRadius: BorderRadius.circular(theme.radiusMd)),
|
||||
};
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return decoration.copyWith(color: palette.surfaceSoft);
|
||||
}
|
||||
if (theme.brightness == Brightness.light) {
|
||||
final background = Color.alphaBlend(palette.textPrimary.withValues(alpha: 0.08), palette.surfaceSoft);
|
||||
final hoverBackground = Color.alphaBlend(
|
||||
palette.textPrimary.withValues(alpha: 0.13),
|
||||
palette.surfaceSoft,
|
||||
);
|
||||
return decoration.copyWith(color: states.contains(WidgetState.hovered) ? hoverBackground : background);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
textStyle: (context, states, value) => states.contains(WidgetState.disabled)
|
||||
? value.copyWith(color: palette.textMuted.withValues(alpha: 0.62))
|
||||
: value,
|
||||
iconTheme: (context, states, value) => states.contains(WidgetState.disabled)
|
||||
? value.copyWith(color: palette.textMuted.withValues(alpha: 0.62))
|
||||
: value,
|
||||
child: shad.ComponentTheme<shad.TextFieldTheme>(
|
||||
data: shad.TextFieldTheme(
|
||||
filled: true,
|
||||
border: Border.all(color: palette.border),
|
||||
borderRadius: BorderRadius.circular(AppDesignTokens.controlRadius),
|
||||
padding: AppDesignTokens.textFieldPadding,
|
||||
),
|
||||
child: shad.ComponentTheme<shad.CheckboxTheme>(
|
||||
data: shad.CheckboxTheme(
|
||||
size: AppDesignTokens.checkboxSize,
|
||||
gap: AppDesignTokens.checkboxLabelGap,
|
||||
borderColor: theme.brightness == Brightness.dark ? palette.textMuted : palette.border,
|
||||
child: shad.ComponentTheme<shad.SecondaryButtonTheme>(
|
||||
data: shad.SecondaryButtonTheme(
|
||||
decoration: (context, states, value) {
|
||||
final decoration = switch (value) {
|
||||
final BoxDecoration decoration => decoration,
|
||||
_ => BoxDecoration(borderRadius: BorderRadius.circular(theme.radiusMd)),
|
||||
};
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return decoration.copyWith(color: palette.surfaceSoft);
|
||||
}
|
||||
if (theme.brightness == Brightness.light) {
|
||||
final background = Color.alphaBlend(palette.textPrimary.withValues(alpha: 0.08), palette.surfaceSoft);
|
||||
final hoverBackground = Color.alphaBlend(
|
||||
palette.textPrimary.withValues(alpha: 0.13),
|
||||
palette.surfaceSoft,
|
||||
);
|
||||
return decoration.copyWith(color: states.contains(WidgetState.hovered) ? hoverBackground : background);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
textStyle: (context, states, value) => states.contains(WidgetState.disabled)
|
||||
? value.copyWith(color: palette.textMuted.withValues(alpha: 0.62))
|
||||
: value,
|
||||
iconTheme: (context, states, value) => states.contains(WidgetState.disabled)
|
||||
? value.copyWith(color: palette.textMuted.withValues(alpha: 0.62))
|
||||
: value,
|
||||
),
|
||||
child: shad.ComponentTheme<shad.ToastTheme>(
|
||||
data: const shad.ToastTheme(toastConstraints: BoxConstraints(maxWidth: 420)),
|
||||
child: child,
|
||||
child: shad.ComponentTheme<shad.CheckboxTheme>(
|
||||
data: shad.CheckboxTheme(
|
||||
size: AppDesignTokens.checkboxSize,
|
||||
gap: AppDesignTokens.checkboxLabelGap,
|
||||
borderColor: theme.brightness == Brightness.dark ? palette.textMuted : palette.border,
|
||||
),
|
||||
child: shad.ComponentTheme<shad.ToastTheme>(
|
||||
data: const shad.ToastTheme(toastConstraints: BoxConstraints(maxWidth: 420)),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -21,6 +21,8 @@ class AppDesignTokens {
|
||||
static const double settingsFormControlWidth = 420;
|
||||
static const double settingsNumberControlWidth = 168;
|
||||
static const double settingsItemMinLabelWidth = 160;
|
||||
static const EdgeInsets textFieldPadding = EdgeInsets.symmetric(horizontal: 12, vertical: 11);
|
||||
static const EdgeInsets numberTextFieldPadding = EdgeInsets.symmetric(horizontal: 12, vertical: 10);
|
||||
|
||||
static const EdgeInsets pagePadding = EdgeInsets.symmetric(horizontal: 32);
|
||||
static const EdgeInsets sidebarPadding = EdgeInsets.symmetric(horizontal: 16);
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
|
||||
|
||||
import '../theme/app_design_tokens.dart';
|
||||
import 'app_text_field.dart';
|
||||
|
||||
/// A bounded numeric text field with the app's standard spinner controls.
|
||||
@@ -91,6 +92,7 @@ class _AppNumberInputState extends State<AppNumberInput> {
|
||||
: TextInputType.number,
|
||||
inputFormatters: formatters,
|
||||
enabled: widget.enabled,
|
||||
padding: AppDesignTokens.numberTextFieldPadding,
|
||||
features: [
|
||||
shad.InputFeature.spinner(
|
||||
step: widget.step,
|
||||
|
||||
@@ -158,7 +158,10 @@ void main() {
|
||||
|
||||
final connectionsInput = find.byKey(const ValueKey('create-task-connections-input'));
|
||||
expect(connectionsInput, findsOneWidget);
|
||||
expect(tester.getSize(connectionsInput).width, AppDesignTokens.settingsNumberControlWidth);
|
||||
expect(
|
||||
tester.getSize(connectionsInput).width,
|
||||
tester.getSize(find.byKey(const ValueKey('create-task-rename-input'))).width,
|
||||
);
|
||||
final connectionsField = tester.widget<shad.TextField>(connectionsInput);
|
||||
expect(connectionsField.controller!.text, '12');
|
||||
expect(connectionsField.features.single, isA<shad.InputSpinnerFeature>());
|
||||
|
||||
@@ -7,6 +7,30 @@ import 'package:gopeed/shared/theme/app_theme.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
|
||||
|
||||
void main() {
|
||||
testWidgets('provides the standard text field appearance', (tester) async {
|
||||
late shad.TextFieldTheme textFieldTheme;
|
||||
|
||||
await tester.pumpWidget(
|
||||
shad.ShadcnApp(
|
||||
theme: AppTheme.light(),
|
||||
materialTheme: AppTheme.materialLight(),
|
||||
home: AppComponentThemes(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
textFieldTheme = shad.ComponentTheme.of<shad.TextFieldTheme>(context);
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(textFieldTheme.filled, isTrue);
|
||||
expect(textFieldTheme.border, Border.all(color: AppPalette.light.border));
|
||||
expect(textFieldTheme.borderRadius, BorderRadius.circular(AppDesignTokens.controlRadius));
|
||||
expect(textFieldTheme.padding, AppDesignTokens.textFieldPadding);
|
||||
});
|
||||
|
||||
testWidgets('provides the standard checkbox metrics', (tester) async {
|
||||
late shad.CheckboxTheme checkboxTheme;
|
||||
|
||||
|
||||
@@ -2336,7 +2336,9 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
final createDirectoryInput = find.byKey(const ValueKey('create-task-directory-input'));
|
||||
final createRenameInput = find.byKey(const ValueKey('create-task-rename-input'));
|
||||
final createConnectionsInput = find.byKey(const ValueKey('create-task-connections-input'));
|
||||
expect(tester.getSize(createDirectoryInput).height, tester.getSize(createRenameInput).height);
|
||||
expect(tester.getSize(createConnectionsInput), tester.getSize(createRenameInput));
|
||||
final createDirectoryField = tester.widget<shad.TextField>(createDirectoryInput);
|
||||
final createRenameField = tester.widget<shad.TextField>(
|
||||
find.descendant(of: createRenameInput, matching: find.byType(AppTextField)),
|
||||
@@ -4875,7 +4877,7 @@ class _CreateTaskPageHarness extends StatelessWidget {
|
||||
return shad.ShadcnApp(
|
||||
theme: AppTheme.light(),
|
||||
materialTheme: AppTheme.materialLight(),
|
||||
home: const CreateTaskWindowPage(),
|
||||
home: const AppComponentThemes(child: CreateTaskWindowPage()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user