sync flutter widgets

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-02-12 11:01:35 +08:00
parent fbf7116edf
commit 483953cf56
31 changed files with 804 additions and 646 deletions

View File

@@ -15,7 +15,7 @@ import 'dart:math' as math;
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' hide InkWell;
import 'package:flutter/material.dart' hide ButtonStyleButton, InkWell;
import 'package:flutter/rendering.dart';
/// The base [StatefulWidget] class for buttons whose style is defined by a [ButtonStyle] object.
@@ -121,7 +121,7 @@ abstract class ButtonStyleButton extends StatefulWidget {
/// Defaults to true.
final bool? isSemanticButton;
/// {@macro flutter.material.ButtonStyleButton.iconAlignment}
/// {@macro flutter.material.ButtonStyle.iconAlignment}
@Deprecated(
'Remove this parameter as it is now ignored. '
'Use ButtonStyle.iconAlignment instead. '
@@ -722,9 +722,9 @@ class _RenderInputPadding extends RenderShiftedBox {
}) {
if (child != null) {
final Size childSize = layoutChild(child!, constraints);
final double height = math.max(childSize.width, minSize.width);
final double width = math.max(childSize.height, minSize.height);
return constraints.constrain(Size(height, width));
final double width = math.max(childSize.width, minSize.width);
final double height = math.max(childSize.height, minSize.height);
return constraints.constrain(Size(width, height));
}
return Size.zero;
}
@@ -764,7 +764,7 @@ class _RenderInputPadding extends RenderShiftedBox {
layoutChild: ChildLayoutHelper.layoutChild,
);
if (child != null) {
final BoxParentData childParentData = child!.parentData! as BoxParentData;
final childParentData = child!.parentData! as BoxParentData;
childParentData.offset = Alignment.center.alongOffset(
size - child!.size as Offset,
);

View File

@@ -18,7 +18,7 @@ import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart' hide InkWell;
import 'package:flutter/rendering.dart';
abstract class _ParentInkResponseState {
@@ -265,14 +265,16 @@ class InkResponse extends StatelessWidget {
/// The cursor for a mouse pointer when it enters or is hovering over the
/// widget.
///
/// {@template flutter.material.InkWell.mouseCursor}
/// If [mouseCursor] is a [WidgetStateMouseCursor],
/// [WidgetStateProperty.resolve] is used for the following [WidgetState]s:
///
/// * [WidgetState.hovered].
/// * [WidgetState.focused].
/// * [WidgetState.disabled].
/// {@endtemplate}
///
/// If this property is null, [WidgetStateMouseCursor.clickable] will be used.
/// If this property is null, [WidgetStateMouseCursor.adaptiveClickable] will be used.
final MouseCursor? mouseCursor;
/// Whether this ink response should be clipped its bounds.
@@ -638,7 +640,7 @@ class _InkResponseStateWidget extends StatefulWidget {
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
final List<String> gestures = <String>[
final gestures = <String>[
if (onTap != null) 'tap',
if (onDoubleTap != null) 'double tap',
if (onLongPress != null) 'long press',
@@ -900,7 +902,7 @@ class _InkResponseState extends State<_InkResponseStateWidget>
_HighlightType.hover =>
widget.hoverColor ?? Theme.of(context).hoverColor,
};
final RenderBox referenceBox = context.findRenderObject()! as RenderBox;
final referenceBox = context.findRenderObject()! as RenderBox;
_highlights[type] = InkHighlight(
controller: Material.of(context),
referenceBox: referenceBox,
@@ -951,7 +953,7 @@ class _InkResponseState extends State<_InkResponseStateWidget>
InteractiveInkFeature _createSplash(Offset globalPosition) {
final MaterialInkController inkController = Material.of(context);
final RenderBox referenceBox = context.findRenderObject()! as RenderBox;
final referenceBox = context.findRenderObject()! as RenderBox;
final Offset position = referenceBox.globalToLocal(globalPosition);
final Color color =
widget.overlayColor?.resolve(statesController.value) ??
@@ -1054,7 +1056,7 @@ class _InkResponseState extends State<_InkResponseStateWidget>
final Offset globalPosition;
if (context != null) {
final RenderBox referenceBox = context.findRenderObject()! as RenderBox;
final referenceBox = context.findRenderObject()! as RenderBox;
assert(
referenceBox.hasSize,
'InkResponse must be done with layout before starting a splash.',
@@ -1139,7 +1141,7 @@ class _InkResponseState extends State<_InkResponseStateWidget>
if (_splashes != null) {
final Set<InteractiveInkFeature> splashes = _splashes!;
_splashes = null;
for (final InteractiveInkFeature splash in splashes) {
for (final splash in splashes) {
splash.dispose();
}
_currentSplash = null;
@@ -1205,7 +1207,7 @@ class _InkResponseState extends State<_InkResponseStateWidget>
assert(widget.debugCheckContext(context));
final ThemeData theme = Theme.of(context);
const Set<WidgetState> highlightableStates = <WidgetState>{
const highlightableStates = <WidgetState>{
WidgetState.focused,
WidgetState.hovered,
WidgetState.pressed,
@@ -1216,15 +1218,15 @@ class _InkResponseState extends State<_InkResponseStateWidget>
);
// Each highlightable state will be resolved separately to get the corresponding color.
// For this resolution to be correct, the non-highlightable states should be preserved.
final Set<WidgetState> pressed = <WidgetState>{
final pressed = <WidgetState>{
...nonHighlightableStates,
WidgetState.pressed,
};
final Set<WidgetState> focused = <WidgetState>{
final focused = <WidgetState>{
...nonHighlightableStates,
WidgetState.focused,
};
final Set<WidgetState> hovered = <WidgetState>{
final hovered = <WidgetState>{
...nonHighlightableStates,
WidgetState.hovered,
};
@@ -1260,7 +1262,7 @@ class _InkResponseState extends State<_InkResponseStateWidget>
final MouseCursor effectiveMouseCursor =
WidgetStateProperty.resolveAs<MouseCursor>(
widget.mouseCursor ?? WidgetStateMouseCursor.clickable,
widget.mouseCursor ?? WidgetStateMouseCursor.adaptiveClickable,
statesController.value,
);

View File

@@ -14,7 +14,8 @@ import 'dart:ui' show lerpDouble;
import 'package:PiliPlus/common/widgets/flutter/dyn/button.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' hide InkWell, ButtonStyleButton;
import 'package:flutter/material.dart'
hide ButtonStyleButton, TextButton, InkWell;
/// A Material Design "Text Button".
///
@@ -84,7 +85,7 @@ class TextButton extends ButtonStyleButton {
super.statesController,
super.isSemanticButton,
required Widget super.child,
});
}) : _addPadding = false;
/// Create a text button from a pair of widgets that serve as the button's
/// [icon] and [label].
@@ -92,56 +93,38 @@ class TextButton extends ButtonStyleButton {
/// The icon and label are arranged in a row and padded by 8 logical pixels
/// at the ends, with an 8 pixel gap in between.
///
/// If [icon] is null, will create a [TextButton] instead.
/// If [icon] is null, this constructor will create a [TextButton]
/// that doesn't display an icon.
///
/// {@macro flutter.material.ButtonStyleButton.iconAlignment}
/// {@macro flutter.material.ButtonStyle.iconAlignment}
///
factory TextButton.icon({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
ButtonStyle? style,
FocusNode? focusNode,
bool? autofocus,
Clip? clipBehavior,
WidgetStatesController? statesController,
TextButton.icon({
super.key,
required super.onPressed,
super.onLongPress,
super.onHover,
super.onFocusChange,
super.style,
super.focusNode,
super.autofocus = false,
super.clipBehavior = Clip.none,
super.statesController,
Widget? icon,
required Widget label,
IconAlignment? iconAlignment,
}) {
if (icon == null) {
return TextButton(
key: key,
onPressed: onPressed,
onLongPress: onLongPress,
onHover: onHover,
onFocusChange: onFocusChange,
style: style,
focusNode: focusNode,
autofocus: autofocus ?? false,
clipBehavior: clipBehavior ?? Clip.none,
statesController: statesController,
child: label,
);
}
return _TextButtonWithIcon(
key: key,
onPressed: onPressed,
onLongPress: onLongPress,
onHover: onHover,
onFocusChange: onFocusChange,
style: style,
focusNode: focusNode,
autofocus: autofocus ?? false,
clipBehavior: clipBehavior ?? Clip.none,
statesController: statesController,
icon: icon,
label: label,
iconAlignment: iconAlignment,
);
}
}) : _addPadding = icon != null,
super(
child: icon != null
? _TextButtonWithIconChild(
label: label,
icon: icon,
buttonStyle: style,
iconAlignment: iconAlignment,
)
: label,
);
final bool _addPadding;
/// A static convenience method that constructs a text button
/// [ButtonStyle] given simple values.
@@ -339,9 +322,7 @@ class TextButton extends ButtonStyleButton {
/// * `maximumSize` - Size.infinite
/// * `side` - null
/// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
/// * `mouseCursor`
/// * disabled - SystemMouseCursors.basic
/// * others - SystemMouseCursors.click
/// * `mouseCursor` - WidgetStateMouseCursor.adaptiveClickable
/// * `visualDensity` - theme.visualDensity
/// * `tapTargetSize` - theme.materialTapTargetSize
/// * `animationDuration` - kThemeChangeDuration
@@ -389,9 +370,7 @@ class TextButton extends ButtonStyleButton {
/// * `maximumSize` - Size.infinite
/// * `side` - null
/// * `shape` - StadiumBorder()
/// * `mouseCursor`
/// * disabled - SystemMouseCursors.basic
/// * others - SystemMouseCursors.click
/// * `mouseCursor` - WidgetStateMouseCursor.adaptiveClickable
/// * `visualDensity` - theme.visualDensity
/// * `tapTargetSize` - theme.materialTapTargetSize
/// * `animationDuration` - kThemeChangeDuration
@@ -406,8 +385,7 @@ class TextButton extends ButtonStyleButton {
ButtonStyle defaultStyleOf(BuildContext context) {
final ThemeData theme = Theme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
return Theme.of(context).useMaterial3
final ButtonStyle buttonStyle = theme.useMaterial3
? _TextButtonDefaultsM3(context)
: styleFrom(
foregroundColor: colorScheme.primary,
@@ -425,7 +403,9 @@ class TextButton extends ButtonStyleButton {
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
),
enabledMouseCursor: SystemMouseCursors.click,
enabledMouseCursor: kIsWeb
? SystemMouseCursors.click
: SystemMouseCursors.basic,
disabledMouseCursor: SystemMouseCursors.basic,
visualDensity: theme.visualDensity,
tapTargetSize: theme.materialTapTargetSize,
@@ -434,6 +414,28 @@ class TextButton extends ButtonStyleButton {
alignment: Alignment.center,
splashFactory: InkRipple.splashFactory,
);
// Only apply padding when TextButton has an Icon.
if (_addPadding) {
final double defaultFontSize =
buttonStyle.textStyle?.resolve(const <WidgetState>{})?.fontSize ??
14.0;
final double effectiveTextScale =
MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0;
final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
theme.useMaterial3
? const EdgeInsetsDirectional.fromSTEB(12, 8, 16, 8)
: const EdgeInsets.all(8),
const EdgeInsets.symmetric(horizontal: 4),
const EdgeInsets.symmetric(horizontal: 4),
effectiveTextScale,
);
return buttonStyle.copyWith(
padding: WidgetStatePropertyAll<EdgeInsetsGeometry>(scaledPadding),
);
}
return buttonStyle;
}
/// Returns the [TextButtonThemeData.style] of the closest
@@ -459,53 +461,6 @@ EdgeInsetsGeometry _scaledPadding(BuildContext context) {
);
}
class _TextButtonWithIcon extends TextButton {
_TextButtonWithIcon({
super.key,
required super.onPressed,
super.onLongPress,
super.onHover,
super.onFocusChange,
super.style,
super.focusNode,
bool? autofocus,
super.clipBehavior,
super.statesController,
required Widget icon,
required Widget label,
IconAlignment? iconAlignment,
}) : super(
autofocus: autofocus ?? false,
child: _TextButtonWithIconChild(
icon: icon,
label: label,
buttonStyle: style,
iconAlignment: iconAlignment,
),
);
@override
ButtonStyle defaultStyleOf(BuildContext context) {
final bool useMaterial3 = Theme.of(context).useMaterial3;
final ButtonStyle buttonStyle = super.defaultStyleOf(context);
final double defaultFontSize =
buttonStyle.textStyle?.resolve(const <WidgetState>{})?.fontSize ?? 14.0;
final double effectiveTextScale =
MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0;
final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
useMaterial3
? const EdgeInsetsDirectional.fromSTEB(12, 8, 16, 8)
: const EdgeInsets.all(8),
const EdgeInsets.symmetric(horizontal: 4),
const EdgeInsets.symmetric(horizontal: 4),
effectiveTextScale,
);
return buttonStyle.copyWith(
padding: WidgetStatePropertyAll<EdgeInsetsGeometry>(scaledPadding),
);
}
}
class _TextButtonWithIconChild extends StatelessWidget {
const _TextButtonWithIconChild({
required this.label,
@@ -557,72 +512,72 @@ class _TextButtonWithIconChild extends StatelessWidget {
// dart format off
class _TextButtonDefaultsM3 extends ButtonStyle {
_TextButtonDefaultsM3(this.context)
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
);
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
WidgetStateProperty<TextStyle?> get textStyle =>
WidgetStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
WidgetStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
@override
WidgetStateProperty<Color?>? get backgroundColor =>
const WidgetStatePropertyAll<Color>(Colors.transparent);
const WidgetStatePropertyAll<Color>(Colors.transparent);
@override
WidgetStateProperty<Color?>? get foregroundColor =>
WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return _colors.onSurface.withValues(alpha: 0.38);
}
return _colors.primary;
});
WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return _colors.onSurface.withValues(alpha: 0.38);
}
return _colors.primary;
});
@override
WidgetStateProperty<Color?>? get overlayColor =>
WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.pressed)) {
return _colors.primary.withValues(alpha: 0.1);
}
if (states.contains(WidgetState.hovered)) {
return _colors.primary.withValues(alpha: 0.08);
}
if (states.contains(WidgetState.focused)) {
return _colors.primary.withValues(alpha: 0.1);
}
return null;
});
WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.pressed)) {
return _colors.primary.withValues(alpha: 0.1);
}
if (states.contains(WidgetState.hovered)) {
return _colors.primary.withValues(alpha: 0.08);
}
if (states.contains(WidgetState.focused)) {
return _colors.primary.withValues(alpha: 0.1);
}
return null;
});
@override
WidgetStateProperty<Color>? get shadowColor =>
const WidgetStatePropertyAll<Color>(Colors.transparent);
const WidgetStatePropertyAll<Color>(Colors.transparent);
@override
WidgetStateProperty<Color>? get surfaceTintColor =>
const WidgetStatePropertyAll<Color>(Colors.transparent);
const WidgetStatePropertyAll<Color>(Colors.transparent);
@override
WidgetStateProperty<double>? get elevation =>
const WidgetStatePropertyAll<double>(0.0);
const WidgetStatePropertyAll<double>(0.0);
@override
WidgetStateProperty<EdgeInsetsGeometry>? get padding =>
WidgetStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
WidgetStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
@override
WidgetStateProperty<Size>? get minimumSize =>
const WidgetStatePropertyAll<Size>(Size(64.0, 40.0));
const WidgetStatePropertyAll<Size>(Size(64.0, 40.0));
// No default fixedSize
@override
WidgetStateProperty<double>? get iconSize =>
const WidgetStatePropertyAll<double>(18.0);
const WidgetStatePropertyAll<double>(18.0);
@override
WidgetStateProperty<Color>? get iconColor {
@@ -645,22 +600,16 @@ class _TextButtonDefaultsM3 extends ButtonStyle {
@override
WidgetStateProperty<Size>? get maximumSize =>
const WidgetStatePropertyAll<Size>(Size.infinite);
const WidgetStatePropertyAll<Size>(Size.infinite);
// No default side
@override
WidgetStateProperty<OutlinedBorder>? get shape =>
const WidgetStatePropertyAll<OutlinedBorder>(StadiumBorder());
const WidgetStatePropertyAll<OutlinedBorder>(StadiumBorder());
@override
WidgetStateProperty<MouseCursor?>? get mouseCursor =>
WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return SystemMouseCursors.basic;
}
return SystemMouseCursors.click;
});
WidgetStateProperty<MouseCursor?>? get mouseCursor => WidgetStateMouseCursor.adaptiveClickable;
@override
VisualDensity? get visualDensity => Theme.of(context).visualDensity;