Added TabBar.splashFactory, TabBarTheme.splashFactory,overlayColor (#96252)
diff --git a/packages/flutter/lib/src/material/tab_bar_theme.dart b/packages/flutter/lib/src/material/tab_bar_theme.dart
index 49a95fe..ff442fe 100644
--- a/packages/flutter/lib/src/material/tab_bar_theme.dart
+++ b/packages/flutter/lib/src/material/tab_bar_theme.dart
@@ -5,6 +5,8 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
+import 'ink_well.dart';
+import 'material_state.dart';
import 'tabs.dart';
import 'theme.dart';
@@ -33,6 +35,8 @@
this.labelStyle,
this.unselectedLabelColor,
this.unselectedLabelStyle,
+ this.overlayColor,
+ this.splashFactory,
});
/// Default value for [TabBar.indicator].
@@ -60,6 +64,12 @@
/// Default value for [TabBar.unselectedLabelStyle].
final TextStyle? unselectedLabelStyle;
+ /// Default value for [TabBar.overlayColor].
+ final MaterialStateProperty<Color?>? overlayColor;
+
+ /// Default value for [TabBar.splashFactory].
+ final InteractiveInkFeatureFactory? splashFactory;
+
/// Creates a copy of this object but with the given fields replaced with the
/// new values.
TabBarTheme copyWith({
@@ -70,6 +80,8 @@
TextStyle? labelStyle,
Color? unselectedLabelColor,
TextStyle? unselectedLabelStyle,
+ MaterialStateProperty<Color?>? overlayColor,
+ InteractiveInkFeatureFactory? splashFactory,
}) {
return TabBarTheme(
indicator: indicator ?? this.indicator,
@@ -79,6 +91,8 @@
labelStyle: labelStyle ?? this.labelStyle,
unselectedLabelColor: unselectedLabelColor ?? this.unselectedLabelColor,
unselectedLabelStyle: unselectedLabelStyle ?? this.unselectedLabelStyle,
+ overlayColor: overlayColor ?? this.overlayColor,
+ splashFactory: splashFactory ?? this.splashFactory,
);
}
@@ -104,6 +118,8 @@
labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t),
unselectedLabelColor: Color.lerp(a.unselectedLabelColor, b.unselectedLabelColor, t),
unselectedLabelStyle: TextStyle.lerp(a.unselectedLabelStyle, b.unselectedLabelStyle, t),
+ overlayColor: _LerpColors(a.overlayColor, b.overlayColor, t),
+ splashFactory: t < 0.5 ? a.splashFactory : b.splashFactory,
);
}
@@ -117,6 +133,8 @@
labelStyle,
unselectedLabelColor,
unselectedLabelStyle,
+ overlayColor,
+ splashFactory,
);
}
@@ -133,6 +151,42 @@
&& other.labelPadding == labelPadding
&& other.labelStyle == labelStyle
&& other.unselectedLabelColor == unselectedLabelColor
- && other.unselectedLabelStyle == unselectedLabelStyle;
+ && other.unselectedLabelStyle == unselectedLabelStyle
+ && other.overlayColor == overlayColor
+ && other.splashFactory == splashFactory;
+ }
+}
+
+
+@immutable
+class _LerpColors implements MaterialStateProperty<Color?> {
+ const _LerpColors(this.a, this.b, this.t);
+
+ final MaterialStateProperty<Color?>? a;
+ final MaterialStateProperty<Color?>? b;
+ final double t;
+
+ @override
+ Color? resolve(Set<MaterialState> states) {
+ final Color? resolvedA = a?.resolve(states);
+ final Color? resolvedB = b?.resolve(states);
+ return Color.lerp(resolvedA, resolvedB, t);
+ }
+
+ @override
+ int get hashCode {
+ return hashValues(a, b, t);
+ }
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other))
+ return true;
+ if (other.runtimeType != runtimeType)
+ return false;
+ return other is _LerpColors
+ && other.a == a
+ && other.b == b
+ && other.t == t;
}
}
diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart
index 4028120..e1504a4 100644
--- a/packages/flutter/lib/src/material/tabs.dart
+++ b/packages/flutter/lib/src/material/tabs.dart
@@ -643,6 +643,7 @@
this.enableFeedback,
this.onTap,
this.physics,
+ this.splashFactory,
}) : assert(tabs != null),
assert(isScrollable != null),
assert(dragStartBehavior != null),
@@ -786,14 +787,11 @@
/// [MaterialState.hovered], and [MaterialState.pressed].
///
/// [MaterialState.pressed] triggers a ripple (an ink splash), per
- /// the current Material Design spec. The [overlayColor] doesn't map
- /// a state to [InkResponse.highlightColor] because a separate highlight
- /// is not used by the current design guidelines. See
- /// https://material.io/design/interaction/states.html#pressed
+ /// the current Material Design spec.
///
/// If the overlay color is null or resolves to null, then the default values
- /// for [InkResponse.focusColor], [InkResponse.hoverColor], [InkResponse.splashColor]
- /// will be used instead.
+ /// for [InkResponse.focusColor], [InkResponse.hoverColor], [InkResponse.splashColor],
+ /// and [InkResponse.highlightColor] will be used instead.
final MaterialStateProperty<Color?>? overlayColor;
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
@@ -832,6 +830,25 @@
/// Defaults to matching platform conventions.
final ScrollPhysics? physics;
+ /// Creates the tab bar's [InkWell] splash factory, which defines
+ /// the appearance of "ink" splashes that occur in response to taps.
+ ///
+ /// Use [NoSplash.splashFactory] to defeat ink splash rendering. For example
+ /// to defeat both the splash and the hover/pressed overlay, but not the
+ /// keyboard focused overlay:
+ /// ```dart
+ /// TabBar(
+ /// splashFactory: NoSplash.splashFactory,
+ /// overlayColor: MaterialStateProperty.resolveWith<Color?>(
+ /// (Set<MaterialState> states) {
+ /// return states.contains(MaterialState.focused) ? null : Colors.transparent;
+ /// },
+ /// ),
+ /// ...
+ /// )
+ /// ```
+ final InteractiveInkFeatureFactory? splashFactory;
+
/// A size whose height depends on if the tabs have both icons and text.
///
/// [AppBar] uses this size to compute its own preferred size.
@@ -1187,7 +1204,8 @@
mouseCursor: widget.mouseCursor ?? SystemMouseCursors.click,
onTap: () { _handleTap(index); },
enableFeedback: widget.enableFeedback ?? true,
- overlayColor: widget.overlayColor,
+ overlayColor: widget.overlayColor ?? tabBarTheme.overlayColor,
+ splashFactory: widget.splashFactory ?? tabBarTheme.splashFactory,
child: Padding(
padding: EdgeInsets.only(bottom: widget.indicatorWeight),
child: Stack(
diff --git a/packages/flutter/test/material/tab_bar_theme_test.dart b/packages/flutter/test/material/tab_bar_theme_test.dart
index 319554c..33fb1ab 100644
--- a/packages/flutter/test/material/tab_bar_theme_test.dart
+++ b/packages/flutter/test/material/tab_bar_theme_test.dart
@@ -54,6 +54,21 @@
}
void main() {
+ test('TabBarTheme copyWith, ==, hashCode, defaults', () {
+ expect(const TabBarTheme(), const TabBarTheme().copyWith());
+ expect(const TabBarTheme().hashCode, const TabBarTheme().copyWith().hashCode);
+
+ expect(const TabBarTheme().indicator, null);
+ expect(const TabBarTheme().indicatorSize, null);
+ expect(const TabBarTheme().labelColor, null);
+ expect(const TabBarTheme().labelPadding, null);
+ expect(const TabBarTheme().labelStyle, null);
+ expect(const TabBarTheme().unselectedLabelColor, null);
+ expect(const TabBarTheme().unselectedLabelStyle, null);
+ expect(const TabBarTheme().overlayColor, null);
+ expect(const TabBarTheme().splashFactory, null);
+ });
+
testWidgets('Tab bar defaults - label style and selected/unselected label colors', (WidgetTester tester) async {
// tests for the default label color and label styles when tabBarTheme and tabBar do not provide any
await tester.pumpWidget(_withTheme(null));
diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart
index 6691f61..3c98bb1 100644
--- a/packages/flutter/test/material/tabs_test.dart
+++ b/packages/flutter/test/material/tabs_test.dart
@@ -4322,6 +4322,62 @@
expect(controller3.index, 2);
expect(pageController.page, 2);
});
+
+ testWidgets('TabBar InkWell splashFactory and overlayColor', (WidgetTester tester) async {
+ const InteractiveInkFeatureFactory splashFactory = NoSplash.splashFactory;
+ final MaterialStateProperty<Color?> overlayColor = MaterialStateProperty.resolveWith<Color?>(
+ (Set<MaterialState> states) => Colors.transparent,
+ );
+
+ // TabBarTheme splashFactory and overlayColor
+ await tester.pumpWidget(
+ MaterialApp(
+ theme: ThemeData.light().copyWith(
+ tabBarTheme: TabBarTheme(
+ splashFactory: splashFactory,
+ overlayColor: overlayColor,
+ )),
+ home: DefaultTabController(
+ length: 1,
+ child: Scaffold(
+ appBar: AppBar(
+ bottom: TabBar(
+ tabs: <Widget>[
+ Container(width: 100, height: 100, color: Colors.green),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(tester.widget<InkWell>(find.byType(InkWell)).splashFactory, splashFactory);
+ expect(tester.widget<InkWell>(find.byType(InkWell)).overlayColor, overlayColor);
+
+ // TabBar splashFactory and overlayColor
+ await tester.pumpWidget(
+ MaterialApp(
+ home: DefaultTabController(
+ length: 1,
+ child: Scaffold(
+ appBar: AppBar(
+ bottom: TabBar(
+ splashFactory: splashFactory,
+ overlayColor: overlayColor,
+ tabs: <Widget>[
+ Container(width: 100, height: 100, color: Colors.green),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ await tester.pumpAndSettle(); // theme animation
+ expect(tester.widget<InkWell>(find.byType(InkWell)).splashFactory, splashFactory);
+ expect(tester.widget<InkWell>(find.byType(InkWell)).overlayColor, overlayColor);
+ });
}
class KeepAliveInk extends StatefulWidget {