Unverified Commit fbb3036b authored by J-P Nurmi's avatar J-P Nurmi Committed by GitHub

FloatingActionButton: add themeable mouse cursor (#103473)

parent 2756f868
...@@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart'; ...@@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart';
import 'button.dart'; import 'button.dart';
import 'color_scheme.dart'; import 'color_scheme.dart';
import 'floating_action_button_theme.dart'; import 'floating_action_button_theme.dart';
import 'material_state.dart';
import 'scaffold.dart'; import 'scaffold.dart';
import 'text_theme.dart'; import 'text_theme.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -603,7 +604,7 @@ class FloatingActionButton extends StatelessWidget { ...@@ -603,7 +604,7 @@ class FloatingActionButton extends StatelessWidget {
Widget result = RawMaterialButton( Widget result = RawMaterialButton(
onPressed: onPressed, onPressed: onPressed,
mouseCursor: mouseCursor, mouseCursor: _EffectiveMouseCursor(mouseCursor, floatingActionButtonTheme.mouseCursor),
elevation: elevation, elevation: elevation,
focusElevation: focusElevation, focusElevation: focusElevation,
hoverElevation: hoverElevation, hoverElevation: hoverElevation,
...@@ -664,6 +665,26 @@ class FloatingActionButton extends StatelessWidget { ...@@ -664,6 +665,26 @@ class FloatingActionButton extends StatelessWidget {
} }
} }
// This MaterialStateProperty is passed along to RawMaterialButton which
// resolves the property against MaterialState.pressed, MaterialState.hovered,
// MaterialState.focused, MaterialState.disabled.
class _EffectiveMouseCursor extends MaterialStateMouseCursor {
const _EffectiveMouseCursor(this.widgetCursor, this.themeCursor);
final MouseCursor? widgetCursor;
final MaterialStateProperty<MouseCursor?>? themeCursor;
@override
MouseCursor resolve(Set<MaterialState> states) {
return MaterialStateProperty.resolveAs<MouseCursor?>(widgetCursor, states)
?? themeCursor?.resolve(states)
?? MaterialStateMouseCursor.clickable.resolve(states);
}
@override
String get debugDescription => 'MaterialStateMouseCursor(FloatActionButton)';
}
// This widget's size matches its child's size unless its constraints // This widget's size matches its child's size unless its constraints
// force it to be larger or smaller. The child is centered. // force it to be larger or smaller. The child is centered.
// //
......
...@@ -7,6 +7,8 @@ import 'dart:ui' show lerpDouble; ...@@ -7,6 +7,8 @@ import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'material_state.dart';
/// Defines default property values for descendant [FloatingActionButton] /// Defines default property values for descendant [FloatingActionButton]
/// widgets. /// widgets.
/// ///
...@@ -51,6 +53,7 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -51,6 +53,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
this.extendedIconLabelSpacing, this.extendedIconLabelSpacing,
this.extendedPadding, this.extendedPadding,
this.extendedTextStyle, this.extendedTextStyle,
this.mouseCursor,
}); });
/// Color to be used for the unselected, enabled [FloatingActionButton]'s /// Color to be used for the unselected, enabled [FloatingActionButton]'s
...@@ -129,6 +132,11 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -129,6 +132,11 @@ class FloatingActionButtonThemeData with Diagnosticable {
/// The text style for an extended [FloatingActionButton]'s label. /// The text style for an extended [FloatingActionButton]'s label.
final TextStyle? extendedTextStyle; final TextStyle? extendedTextStyle;
/// {@macro flutter.material.RawMaterialButton.mouseCursor}
///
/// If specified, overrides the default value of [FloatingActionButton.mouseCursor].
final MaterialStateProperty<MouseCursor?>? mouseCursor;
/// Creates a copy of this object with the given fields replaced with the /// Creates a copy of this object with the given fields replaced with the
/// new values. /// new values.
FloatingActionButtonThemeData copyWith({ FloatingActionButtonThemeData copyWith({
...@@ -152,6 +160,7 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -152,6 +160,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
double? extendedIconLabelSpacing, double? extendedIconLabelSpacing,
EdgeInsetsGeometry? extendedPadding, EdgeInsetsGeometry? extendedPadding,
TextStyle? extendedTextStyle, TextStyle? extendedTextStyle,
MaterialStateProperty<MouseCursor?>? mouseCursor,
}) { }) {
return FloatingActionButtonThemeData( return FloatingActionButtonThemeData(
foregroundColor: foregroundColor ?? this.foregroundColor, foregroundColor: foregroundColor ?? this.foregroundColor,
...@@ -174,6 +183,7 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -174,6 +183,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
extendedIconLabelSpacing: extendedIconLabelSpacing ?? this.extendedIconLabelSpacing, extendedIconLabelSpacing: extendedIconLabelSpacing ?? this.extendedIconLabelSpacing,
extendedPadding: extendedPadding ?? this.extendedPadding, extendedPadding: extendedPadding ?? this.extendedPadding,
extendedTextStyle: extendedTextStyle ?? this.extendedTextStyle, extendedTextStyle: extendedTextStyle ?? this.extendedTextStyle,
mouseCursor: mouseCursor ?? this.mouseCursor,
); );
} }
...@@ -208,6 +218,7 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -208,6 +218,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
extendedIconLabelSpacing: lerpDouble(a?.extendedIconLabelSpacing, b?.extendedIconLabelSpacing, t), extendedIconLabelSpacing: lerpDouble(a?.extendedIconLabelSpacing, b?.extendedIconLabelSpacing, t),
extendedPadding: EdgeInsetsGeometry.lerp(a?.extendedPadding, b?.extendedPadding, t), extendedPadding: EdgeInsetsGeometry.lerp(a?.extendedPadding, b?.extendedPadding, t),
extendedTextStyle: TextStyle.lerp(a?.extendedTextStyle, b?.extendedTextStyle, t), extendedTextStyle: TextStyle.lerp(a?.extendedTextStyle, b?.extendedTextStyle, t),
mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor,
); );
} }
...@@ -232,7 +243,10 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -232,7 +243,10 @@ class FloatingActionButtonThemeData with Diagnosticable {
extendedSizeConstraints, extendedSizeConstraints,
extendedIconLabelSpacing, extendedIconLabelSpacing,
extendedPadding, extendedPadding,
extendedTextStyle, Object.hash(
extendedTextStyle,
mouseCursor,
),
); );
@override @override
...@@ -263,7 +277,8 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -263,7 +277,8 @@ class FloatingActionButtonThemeData with Diagnosticable {
&& other.extendedSizeConstraints == extendedSizeConstraints && other.extendedSizeConstraints == extendedSizeConstraints
&& other.extendedIconLabelSpacing == extendedIconLabelSpacing && other.extendedIconLabelSpacing == extendedIconLabelSpacing
&& other.extendedPadding == extendedPadding && other.extendedPadding == extendedPadding
&& other.extendedTextStyle == extendedTextStyle; && other.extendedTextStyle == extendedTextStyle
&& other.mouseCursor == mouseCursor;
} }
@override @override
...@@ -290,5 +305,6 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -290,5 +305,6 @@ class FloatingActionButtonThemeData with Diagnosticable {
properties.add(DoubleProperty('extendedIconLabelSpacing', extendedIconLabelSpacing, defaultValue: null)); properties.add(DoubleProperty('extendedIconLabelSpacing', extendedIconLabelSpacing, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('extendedPadding', extendedPadding, defaultValue: null)); properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('extendedPadding', extendedPadding, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle>('extendedTextStyle', extendedTextStyle, defaultValue: null)); properties.add(DiagnosticsProperty<TextStyle>('extendedTextStyle', extendedTextStyle, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null));
} }
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -287,6 +288,7 @@ void main() { ...@@ -287,6 +288,7 @@ void main() {
extendedIconLabelSpacing: 12, extendedIconLabelSpacing: 12,
extendedPadding: EdgeInsetsDirectional.only(start: 7.0, end: 8.0), extendedPadding: EdgeInsetsDirectional.only(start: 7.0, end: 8.0),
extendedTextStyle: TextStyle(letterSpacing: 2.0), extendedTextStyle: TextStyle(letterSpacing: 2.0),
mouseCursor: MaterialStateMouseCursor.clickable,
).debugFillProperties(builder); ).debugFillProperties(builder);
final List<String> description = builder.properties final List<String> description = builder.properties
...@@ -315,8 +317,33 @@ void main() { ...@@ -315,8 +317,33 @@ void main() {
'extendedIconLabelSpacing: 12.0', 'extendedIconLabelSpacing: 12.0',
'extendedPadding: EdgeInsetsDirectional(7.0, 0.0, 8.0, 0.0)', 'extendedPadding: EdgeInsetsDirectional(7.0, 0.0, 8.0, 0.0)',
'extendedTextStyle: TextStyle(inherit: true, letterSpacing: 2.0)', 'extendedTextStyle: TextStyle(inherit: true, letterSpacing: 2.0)',
'mouseCursor: MaterialStateMouseCursor(clickable)',
]); ]);
}); });
testWidgets('FloatingActionButton.mouseCursor uses FloatingActionButtonThemeData.mouseCursor when specified.', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
theme: ThemeData().copyWith(
floatingActionButtonTheme: FloatingActionButtonThemeData(
mouseCursor: MaterialStateProperty.all(SystemMouseCursors.text),
),
),
home: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () { },
child: const Icon(Icons.add),
),
),
));
await tester.pumpAndSettle();
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.moveTo(tester.getCenter(find.byType(FloatingActionButton)));
await tester.pumpAndSettle();
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
});
} }
RawMaterialButton _getRawMaterialButton(WidgetTester tester) { RawMaterialButton _getRawMaterialButton(WidgetTester tester) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment