Skip to content

Commit a6504ea

Browse files
authored
TabBar: add themeable mouse cursor (#96737)
1 parent 36a8f0f commit a6504ea

File tree

3 files changed

+53
-3
lines changed

3 files changed

+53
-3
lines changed

packages/flutter/lib/src/material/tab_bar_theme.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class TabBarTheme with Diagnosticable {
3737
this.unselectedLabelStyle,
3838
this.overlayColor,
3939
this.splashFactory,
40+
this.mouseCursor,
4041
});
4142

4243
/// Default value for [TabBar.indicator].
@@ -70,6 +71,11 @@ class TabBarTheme with Diagnosticable {
7071
/// Default value for [TabBar.splashFactory].
7172
final InteractiveInkFeatureFactory? splashFactory;
7273

74+
/// {@macro flutter.material.tabs.mouseCursor}
75+
///
76+
/// If specified, overrides the default value of [TabBar.mouseCursor].
77+
final MaterialStateProperty<MouseCursor?>? mouseCursor;
78+
7379
/// Creates a copy of this object but with the given fields replaced with the
7480
/// new values.
7581
TabBarTheme copyWith({
@@ -82,6 +88,7 @@ class TabBarTheme with Diagnosticable {
8288
TextStyle? unselectedLabelStyle,
8389
MaterialStateProperty<Color?>? overlayColor,
8490
InteractiveInkFeatureFactory? splashFactory,
91+
MaterialStateProperty<MouseCursor?>? mouseCursor,
8592
}) {
8693
return TabBarTheme(
8794
indicator: indicator ?? this.indicator,
@@ -93,6 +100,7 @@ class TabBarTheme with Diagnosticable {
93100
unselectedLabelStyle: unselectedLabelStyle ?? this.unselectedLabelStyle,
94101
overlayColor: overlayColor ?? this.overlayColor,
95102
splashFactory: splashFactory ?? this.splashFactory,
103+
mouseCursor: mouseCursor ?? this.mouseCursor,
96104
);
97105
}
98106

@@ -120,6 +128,7 @@ class TabBarTheme with Diagnosticable {
120128
unselectedLabelStyle: TextStyle.lerp(a.unselectedLabelStyle, b.unselectedLabelStyle, t),
121129
overlayColor: _LerpColors(a.overlayColor, b.overlayColor, t),
122130
splashFactory: t < 0.5 ? a.splashFactory : b.splashFactory,
131+
mouseCursor: t < 0.5 ? a.mouseCursor : b.mouseCursor,
123132
);
124133
}
125134

@@ -135,6 +144,7 @@ class TabBarTheme with Diagnosticable {
135144
unselectedLabelStyle,
136145
overlayColor,
137146
splashFactory,
147+
mouseCursor,
138148
);
139149
}
140150

@@ -153,7 +163,8 @@ class TabBarTheme with Diagnosticable {
153163
&& other.unselectedLabelColor == unselectedLabelColor
154164
&& other.unselectedLabelStyle == unselectedLabelStyle
155165
&& other.overlayColor == overlayColor
156-
&& other.splashFactory == splashFactory;
166+
&& other.splashFactory == splashFactory
167+
&& other.mouseCursor == mouseCursor;
157168
}
158169
}
159170

packages/flutter/lib/src/material/tabs.dart

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -803,10 +803,23 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
803803
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
804804
final DragStartBehavior dragStartBehavior;
805805

806+
/// {@template flutter.material.tabs.mouseCursor}
806807
/// The cursor for a mouse pointer when it enters or is hovering over the
807808
/// individual tab widgets.
808809
///
809-
/// If this property is null, [SystemMouseCursors.click] will be used.
810+
/// If [mouseCursor] is a [MaterialStateProperty<MouseCursor>],
811+
/// [MaterialStateProperty.resolve] is used for the following [MaterialState]s:
812+
///
813+
/// * [MaterialState.selected].
814+
/// {@endtemplate}
815+
///
816+
/// If null, then the value of [TabBarTheme.mouseCursor] is used. If
817+
/// that is also null, then [MaterialStateMouseCursor.clickable] is used.
818+
///
819+
/// See also:
820+
///
821+
/// * [MaterialStateMouseCursor], which can be used to create a [MouseCursor]
822+
/// that is also a [MaterialStateProperty<MouseCursor>].
810823
final MouseCursor? mouseCursor;
811824

812825
/// Whether detected gestures should provide acoustic and/or haptic feedback.
@@ -1222,8 +1235,16 @@ class _TabBarState extends State<TabBar> {
12221235
// the same share of the tab bar's overall width.
12231236
final int tabCount = widget.tabs.length;
12241237
for (int index = 0; index < tabCount; index += 1) {
1238+
final Set<MaterialState> states = <MaterialState>{
1239+
if (index == _currentIndex) MaterialState.selected,
1240+
};
1241+
1242+
final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs<MouseCursor?>(widget.mouseCursor, states)
1243+
?? tabBarTheme.mouseCursor?.resolve(states)
1244+
?? MaterialStateMouseCursor.clickable.resolve(states);
1245+
12251246
wrappedTabs[index] = InkWell(
1226-
mouseCursor: widget.mouseCursor ?? SystemMouseCursors.click,
1247+
mouseCursor: effectiveMouseCursor,
12271248
onTap: () { _handleTap(index); },
12281249
enableFeedback: widget.enableFeedback ?? true,
12291250
overlayColor: widget.overlayColor ?? tabBarTheme.overlayColor,

packages/flutter/test/material/tab_bar_theme_test.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// machines.
77
@Tags(<String>['reduced-test-set'])
88

9+
import 'package:flutter/gestures.dart';
910
import 'package:flutter/material.dart';
1011
import 'package:flutter/rendering.dart';
1112
import 'package:flutter_test/flutter_test.dart';
@@ -67,6 +68,7 @@ void main() {
6768
expect(const TabBarTheme().unselectedLabelStyle, null);
6869
expect(const TabBarTheme().overlayColor, null);
6970
expect(const TabBarTheme().splashFactory, null);
71+
expect(const TabBarTheme().mouseCursor, null);
7072
});
7173

7274
testWidgets('Tab bar defaults - label style and selected/unselected label colors', (WidgetTester tester) async {
@@ -302,6 +304,22 @@ void main() {
302304
);
303305
});
304306

307+
testWidgets('Tab bar theme overrides tab mouse cursor', (WidgetTester tester) async {
308+
const TabBarTheme tabBarTheme = TabBarTheme(mouseCursor: MaterialStateMouseCursor.textable);
309+
310+
await tester.pumpWidget(_withTheme(tabBarTheme));
311+
312+
final Offset tabBar = tester.getCenter(
313+
find.ancestor(of: find.text('tab 1'),matching: find.byType(TabBar)),
314+
);
315+
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
316+
await gesture.addPointer();
317+
addTearDown(gesture.removePointer);
318+
await gesture.moveTo(tabBar);
319+
await tester.pumpAndSettle();
320+
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
321+
});
322+
305323
testWidgets('Tab bar theme - custom tab indicator', (WidgetTester tester) async {
306324
final TabBarTheme tabBarTheme = TabBarTheme(
307325
indicator: BoxDecoration(

0 commit comments

Comments
 (0)