Unverified Commit bbc68cd2 authored by Hans Muller's avatar Hans Muller Committed by GitHub

Added TabBar.splashFactory, TabBarTheme.splashFactory,overlayColor (#96252)

parent 16123105
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'ink_well.dart';
import 'material_state.dart';
import 'tabs.dart'; import 'tabs.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -33,6 +35,8 @@ class TabBarTheme with Diagnosticable { ...@@ -33,6 +35,8 @@ class TabBarTheme with Diagnosticable {
this.labelStyle, this.labelStyle,
this.unselectedLabelColor, this.unselectedLabelColor,
this.unselectedLabelStyle, this.unselectedLabelStyle,
this.overlayColor,
this.splashFactory,
}); });
/// Default value for [TabBar.indicator]. /// Default value for [TabBar.indicator].
...@@ -60,6 +64,12 @@ class TabBarTheme with Diagnosticable { ...@@ -60,6 +64,12 @@ class TabBarTheme with Diagnosticable {
/// Default value for [TabBar.unselectedLabelStyle]. /// Default value for [TabBar.unselectedLabelStyle].
final TextStyle? 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 /// Creates a copy of this object but with the given fields replaced with the
/// new values. /// new values.
TabBarTheme copyWith({ TabBarTheme copyWith({
...@@ -70,6 +80,8 @@ class TabBarTheme with Diagnosticable { ...@@ -70,6 +80,8 @@ class TabBarTheme with Diagnosticable {
TextStyle? labelStyle, TextStyle? labelStyle,
Color? unselectedLabelColor, Color? unselectedLabelColor,
TextStyle? unselectedLabelStyle, TextStyle? unselectedLabelStyle,
MaterialStateProperty<Color?>? overlayColor,
InteractiveInkFeatureFactory? splashFactory,
}) { }) {
return TabBarTheme( return TabBarTheme(
indicator: indicator ?? this.indicator, indicator: indicator ?? this.indicator,
...@@ -79,6 +91,8 @@ class TabBarTheme with Diagnosticable { ...@@ -79,6 +91,8 @@ class TabBarTheme with Diagnosticable {
labelStyle: labelStyle ?? this.labelStyle, labelStyle: labelStyle ?? this.labelStyle,
unselectedLabelColor: unselectedLabelColor ?? this.unselectedLabelColor, unselectedLabelColor: unselectedLabelColor ?? this.unselectedLabelColor,
unselectedLabelStyle: unselectedLabelStyle ?? this.unselectedLabelStyle, unselectedLabelStyle: unselectedLabelStyle ?? this.unselectedLabelStyle,
overlayColor: overlayColor ?? this.overlayColor,
splashFactory: splashFactory ?? this.splashFactory,
); );
} }
...@@ -104,6 +118,8 @@ class TabBarTheme with Diagnosticable { ...@@ -104,6 +118,8 @@ class TabBarTheme with Diagnosticable {
labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t), labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t),
unselectedLabelColor: Color.lerp(a.unselectedLabelColor, b.unselectedLabelColor, t), unselectedLabelColor: Color.lerp(a.unselectedLabelColor, b.unselectedLabelColor, t),
unselectedLabelStyle: TextStyle.lerp(a.unselectedLabelStyle, b.unselectedLabelStyle, 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 @@ class TabBarTheme with Diagnosticable { ...@@ -117,6 +133,8 @@ class TabBarTheme with Diagnosticable {
labelStyle, labelStyle,
unselectedLabelColor, unselectedLabelColor,
unselectedLabelStyle, unselectedLabelStyle,
overlayColor,
splashFactory,
); );
} }
...@@ -133,6 +151,42 @@ class TabBarTheme with Diagnosticable { ...@@ -133,6 +151,42 @@ class TabBarTheme with Diagnosticable {
&& other.labelPadding == labelPadding && other.labelPadding == labelPadding
&& other.labelStyle == labelStyle && other.labelStyle == labelStyle
&& other.unselectedLabelColor == unselectedLabelColor && 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;
} }
} }
...@@ -643,6 +643,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -643,6 +643,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
this.enableFeedback, this.enableFeedback,
this.onTap, this.onTap,
this.physics, this.physics,
this.splashFactory,
}) : assert(tabs != null), }) : assert(tabs != null),
assert(isScrollable != null), assert(isScrollable != null),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
...@@ -786,14 +787,11 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -786,14 +787,11 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
/// [MaterialState.hovered], and [MaterialState.pressed]. /// [MaterialState.hovered], and [MaterialState.pressed].
/// ///
/// [MaterialState.pressed] triggers a ripple (an ink splash), per /// [MaterialState.pressed] triggers a ripple (an ink splash), per
/// the current Material Design spec. The [overlayColor] doesn't map /// the current Material Design spec.
/// 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
/// ///
/// If the overlay color is null or resolves to null, then the default values /// If the overlay color is null or resolves to null, then the default values
/// for [InkResponse.focusColor], [InkResponse.hoverColor], [InkResponse.splashColor] /// for [InkResponse.focusColor], [InkResponse.hoverColor], [InkResponse.splashColor],
/// will be used instead. /// and [InkResponse.highlightColor] will be used instead.
final MaterialStateProperty<Color?>? overlayColor; final MaterialStateProperty<Color?>? overlayColor;
/// {@macro flutter.widgets.scrollable.dragStartBehavior} /// {@macro flutter.widgets.scrollable.dragStartBehavior}
...@@ -832,6 +830,25 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -832,6 +830,25 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
/// Defaults to matching platform conventions. /// Defaults to matching platform conventions.
final ScrollPhysics? physics; 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. /// A size whose height depends on if the tabs have both icons and text.
/// ///
/// [AppBar] uses this size to compute its own preferred size. /// [AppBar] uses this size to compute its own preferred size.
...@@ -1187,7 +1204,8 @@ class _TabBarState extends State<TabBar> { ...@@ -1187,7 +1204,8 @@ class _TabBarState extends State<TabBar> {
mouseCursor: widget.mouseCursor ?? SystemMouseCursors.click, mouseCursor: widget.mouseCursor ?? SystemMouseCursors.click,
onTap: () { _handleTap(index); }, onTap: () { _handleTap(index); },
enableFeedback: widget.enableFeedback ?? true, enableFeedback: widget.enableFeedback ?? true,
overlayColor: widget.overlayColor, overlayColor: widget.overlayColor ?? tabBarTheme.overlayColor,
splashFactory: widget.splashFactory ?? tabBarTheme.splashFactory,
child: Padding( child: Padding(
padding: EdgeInsets.only(bottom: widget.indicatorWeight), padding: EdgeInsets.only(bottom: widget.indicatorWeight),
child: Stack( child: Stack(
......
...@@ -54,6 +54,21 @@ RenderParagraph _iconRenderObject(WidgetTester tester, IconData icon) { ...@@ -54,6 +54,21 @@ RenderParagraph _iconRenderObject(WidgetTester tester, IconData icon) {
} }
void main() { 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 { 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 // tests for the default label color and label styles when tabBarTheme and tabBar do not provide any
await tester.pumpWidget(_withTheme(null)); await tester.pumpWidget(_withTheme(null));
......
...@@ -4322,6 +4322,62 @@ void main() { ...@@ -4322,6 +4322,62 @@ void main() {
expect(controller3.index, 2); expect(controller3.index, 2);
expect(pageController.page, 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 { class KeepAliveInk extends StatefulWidget {
......
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