Added TabBar.splashFactory, TabBarTheme.splashFactory,overlayColor (#96252)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user