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

This commit is contained in:
Hans Muller 2022-01-14 11:53:42 -08:00 committed by GitHub
parent 1612310513
commit bbc68cd207
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 151 additions and 8 deletions

View file

@ -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 @@ class TabBarTheme with Diagnosticable {
this.labelStyle,
this.unselectedLabelColor,
this.unselectedLabelStyle,
this.overlayColor,
this.splashFactory,
});
/// Default value for [TabBar.indicator].
@ -60,6 +64,12 @@ class TabBarTheme with Diagnosticable {
/// 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 @@ class TabBarTheme with Diagnosticable {
TextStyle? labelStyle,
Color? unselectedLabelColor,
TextStyle? unselectedLabelStyle,
MaterialStateProperty<Color?>? overlayColor,
InteractiveInkFeatureFactory? splashFactory,
}) {
return TabBarTheme(
indicator: indicator ?? this.indicator,
@ -79,6 +91,8 @@ class TabBarTheme with Diagnosticable {
labelStyle: labelStyle ?? this.labelStyle,
unselectedLabelColor: unselectedLabelColor ?? this.unselectedLabelColor,
unselectedLabelStyle: unselectedLabelStyle ?? this.unselectedLabelStyle,
overlayColor: overlayColor ?? this.overlayColor,
splashFactory: splashFactory ?? this.splashFactory,
);
}
@ -104,6 +118,8 @@ class TabBarTheme with Diagnosticable {
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 @@ class TabBarTheme with Diagnosticable {
labelStyle,
unselectedLabelColor,
unselectedLabelStyle,
overlayColor,
splashFactory,
);
}
@ -133,6 +151,42 @@ class TabBarTheme with Diagnosticable {
&& 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;
}
}

View file

@ -643,6 +643,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
this.enableFeedback,
this.onTap,
this.physics,
this.splashFactory,
}) : assert(tabs != null),
assert(isScrollable != null),
assert(dragStartBehavior != null),
@ -786,14 +787,11 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
/// [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 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
/// 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 @@ class _TabBarState extends State<TabBar> {
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(

View file

@ -54,6 +54,21 @@ RenderParagraph _iconRenderObject(WidgetTester tester, IconData icon) {
}
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));

View file

@ -4322,6 +4322,62 @@ void main() {
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 {