Add ExpansionTileTheme
(#98405)
This commit is contained in:
parent
2a2f973120
commit
60f039cb1c
@ -73,6 +73,7 @@ export 'src/material/elevation_overlay.dart';
|
||||
export 'src/material/expand_icon.dart';
|
||||
export 'src/material/expansion_panel.dart';
|
||||
export 'src/material/expansion_tile.dart';
|
||||
export 'src/material/expansion_tile_theme.dart';
|
||||
export 'src/material/feedback.dart';
|
||||
export 'src/material/flat_button.dart';
|
||||
export 'src/material/flexible_space_bar.dart';
|
||||
|
@ -6,6 +6,7 @@ import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'color_scheme.dart';
|
||||
import 'colors.dart';
|
||||
import 'expansion_tile_theme.dart';
|
||||
import 'icons.dart';
|
||||
import 'list_tile.dart';
|
||||
import 'theme.dart';
|
||||
@ -107,9 +108,25 @@ class ExpansionTile extends StatefulWidget {
|
||||
final List<Widget> children;
|
||||
|
||||
/// The color to display behind the sublist when expanded.
|
||||
///
|
||||
/// If this property is null then [ExpansionTileThemeData.backgroundColor] is used. If that
|
||||
/// is also null then Colors.transparent is used.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
|
||||
/// [ExpansionTileThemeData].
|
||||
final Color? backgroundColor;
|
||||
|
||||
/// When not null, defines the background color of tile when the sublist is collapsed.
|
||||
///
|
||||
/// If this property is null then [ExpansionTileThemeData.collapsedBackgroundColor] is used.
|
||||
/// If that is also null then Colors.transparent is used.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
|
||||
/// [ExpansionTileThemeData].
|
||||
final Color? collapsedBackgroundColor;
|
||||
|
||||
/// A widget to display after the title.
|
||||
@ -134,7 +151,13 @@ class ExpansionTile extends StatefulWidget {
|
||||
/// the [leading], [title], [subtitle] and [trailing] widgets. It does not inset
|
||||
/// the expanded [children] widgets.
|
||||
///
|
||||
/// When the value is null, the tile's padding is `EdgeInsets.symmetric(horizontal: 16.0)`.
|
||||
/// If this property is null then [ExpansionTileThemeData.tilePadding] is used. If that
|
||||
/// is also null then the tile's padding is `EdgeInsets.symmetric(horizontal: 16.0)`.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
|
||||
/// [ExpansionTileThemeData].
|
||||
final EdgeInsetsGeometry? tilePadding;
|
||||
|
||||
/// Specifies the alignment of [children], which are arranged in a column when
|
||||
@ -150,7 +173,13 @@ class ExpansionTile extends StatefulWidget {
|
||||
///
|
||||
/// The width of the column is the width of the widest child widget in [children].
|
||||
///
|
||||
/// When the value is null, the value of `expandedAlignment` is [Alignment.center].
|
||||
/// If this property is null then [ExpansionTileThemeData.expandedAlignment]is used. If that
|
||||
/// is also null then the value of `expandedAlignment` is [Alignment.center].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
|
||||
/// [ExpansionTileThemeData].
|
||||
final Alignment? expandedAlignment;
|
||||
|
||||
/// Specifies the alignment of each child within [children] when the tile is expanded.
|
||||
@ -171,12 +200,26 @@ class ExpansionTile extends StatefulWidget {
|
||||
|
||||
/// Specifies padding for [children].
|
||||
///
|
||||
/// When the value is null, the value of `childrenPadding` is [EdgeInsets.zero].
|
||||
/// If this property is null then [ExpansionTileThemeData.childrenPadding] is used. If that
|
||||
/// is also null then the value of `childrenPadding` is [EdgeInsets.zero].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
|
||||
/// [ExpansionTileThemeData].
|
||||
final EdgeInsetsGeometry? childrenPadding;
|
||||
|
||||
/// The icon color of tile's expansion arrow icon when the sublist is expanded.
|
||||
///
|
||||
/// Used to override to the [ListTileThemeData.iconColor].
|
||||
///
|
||||
/// If this property is null then [ExpansionTileThemeData.iconColor] is used. If that
|
||||
/// is also null then the value of [ListTileThemeData.iconColor] is used.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
|
||||
/// [ExpansionTileThemeData].
|
||||
final Color? iconColor;
|
||||
|
||||
/// The icon color of tile's expansion arrow icon when the sublist is collapsed.
|
||||
@ -188,11 +231,27 @@ class ExpansionTile extends StatefulWidget {
|
||||
/// The color of the tile's titles when the sublist is expanded.
|
||||
///
|
||||
/// Used to override to the [ListTileThemeData.textColor].
|
||||
///
|
||||
/// If this property is null then [ExpansionTileThemeData.textColor] is used. If that
|
||||
/// is also null then the value of [ListTileThemeData.textColor] is used.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
|
||||
/// [ExpansionTileThemeData].
|
||||
final Color? textColor;
|
||||
|
||||
/// The color of the tile's titles when the sublist is collapsed.
|
||||
///
|
||||
/// Used to override to the [ListTileThemeData.textColor].
|
||||
///
|
||||
/// If this property is null then [ExpansionTileThemeData.collapsedTextColor] is used. If that
|
||||
/// is also null then the value of [ListTileThemeData.textColor] is used.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
|
||||
/// [ExpansionTileThemeData].
|
||||
final Color? collapsedTextColor;
|
||||
|
||||
/// Typically used to force the expansion arrow icon to the tile's leading or trailing edge.
|
||||
@ -297,11 +356,12 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
}
|
||||
|
||||
Widget _buildChildren(BuildContext context, Widget? child) {
|
||||
final ExpansionTileThemeData expansionTileTheme = ExpansionTileTheme.of(context);
|
||||
final Color borderSideColor = _borderColor.value ?? Colors.transparent;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: _backgroundColor.value ?? Colors.transparent,
|
||||
color: _backgroundColor.value ?? expansionTileTheme.backgroundColor ?? Colors.transparent,
|
||||
border: Border(
|
||||
top: BorderSide(color: borderSideColor),
|
||||
bottom: BorderSide(color: borderSideColor),
|
||||
@ -311,11 +371,11 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
ListTileTheme.merge(
|
||||
iconColor: _iconColor.value,
|
||||
iconColor: _iconColor.value ?? expansionTileTheme.iconColor,
|
||||
textColor: _headerColor.value,
|
||||
child: ListTile(
|
||||
onTap: _handleTap,
|
||||
contentPadding: widget.tilePadding,
|
||||
contentPadding: widget.tilePadding ?? expansionTileTheme.tilePadding,
|
||||
leading: widget.leading ?? _buildLeadingIcon(context),
|
||||
title: widget.title,
|
||||
subtitle: widget.subtitle,
|
||||
@ -324,7 +384,9 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
),
|
||||
ClipRect(
|
||||
child: Align(
|
||||
alignment: widget.expandedAlignment ?? Alignment.center,
|
||||
alignment: widget.expandedAlignment
|
||||
?? expansionTileTheme.expandedAlignment
|
||||
?? Alignment.center,
|
||||
heightFactor: _heightFactor.value,
|
||||
child: child,
|
||||
),
|
||||
@ -337,22 +399,28 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final ExpansionTileThemeData expansionTileTheme = ExpansionTileTheme.of(context);
|
||||
final ColorScheme colorScheme = theme.colorScheme;
|
||||
_borderColorTween.end = theme.dividerColor;
|
||||
_headerColorTween
|
||||
..begin = widget.collapsedTextColor ?? theme.textTheme.subtitle1!.color
|
||||
..end = widget.textColor ?? colorScheme.primary;
|
||||
..begin = widget.collapsedTextColor
|
||||
?? expansionTileTheme.collapsedTextColor
|
||||
?? theme.textTheme.subtitle1!.color
|
||||
..end = widget.textColor ?? expansionTileTheme.textColor ?? colorScheme.primary;
|
||||
_iconColorTween
|
||||
..begin = widget.collapsedIconColor ?? theme.unselectedWidgetColor
|
||||
..end = widget.iconColor ?? colorScheme.primary;
|
||||
..begin = widget.collapsedIconColor
|
||||
?? expansionTileTheme.collapsedIconColor
|
||||
?? theme.unselectedWidgetColor
|
||||
..end = widget.iconColor ?? expansionTileTheme.iconColor ?? colorScheme.primary;
|
||||
_backgroundColorTween
|
||||
..begin = widget.collapsedBackgroundColor
|
||||
..end = widget.backgroundColor;
|
||||
..begin = widget.collapsedBackgroundColor ?? expansionTileTheme.collapsedBackgroundColor
|
||||
..end = widget.backgroundColor ?? expansionTileTheme.backgroundColor;
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ExpansionTileThemeData expansionTileTheme = ExpansionTileTheme.of(context);
|
||||
final bool closed = !_isExpanded && _controller.isDismissed;
|
||||
final bool shouldRemoveChildren = closed && !widget.maintainState;
|
||||
|
||||
@ -361,7 +429,7 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
child: TickerMode(
|
||||
enabled: !closed,
|
||||
child: Padding(
|
||||
padding: widget.childrenPadding ?? EdgeInsets.zero,
|
||||
padding: widget.childrenPadding ?? expansionTileTheme.childrenPadding ?? EdgeInsets.zero,
|
||||
child: Column(
|
||||
crossAxisAlignment: widget.expandedCrossAxisAlignment ?? CrossAxisAlignment.center,
|
||||
children: widget.children,
|
||||
|
217
packages/flutter/lib/src/material/expansion_tile_theme.dart
Normal file
217
packages/flutter/lib/src/material/expansion_tile_theme.dart
Normal file
@ -0,0 +1,217 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'theme.dart';
|
||||
|
||||
/// Used with [ExpansionTileTheme] to define default property values for
|
||||
/// descendant [ExpansionTile] widgets.
|
||||
///
|
||||
/// Descendant widgets obtain the current [ExpansionTileThemeData] object
|
||||
/// using `ExpansionTileTheme.of(context)`. Instances of
|
||||
/// [ExpansionTileThemeData] can be customized with
|
||||
/// [ExpansionTileThemeData.copyWith].
|
||||
///
|
||||
/// A [ExpansionTileThemeData] is often specified as part of the
|
||||
/// overall [Theme] with [ThemeData.expansionTileTheme].
|
||||
///
|
||||
/// All [ExpansionTileThemeData] properties are `null` by default.
|
||||
/// When a theme property is null, the [ExpansionTile] will provide its own
|
||||
/// default based on the overall [Theme]'s textTheme and
|
||||
/// colorScheme. See the individual [ExpansionTile] properties for details.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ThemeData], which describes the overall theme information for the
|
||||
/// application.
|
||||
/// * [ExpansionTileTheme] which overrides the default [ExpansionTileTheme]
|
||||
/// of its [ExpansionTile] descendants.
|
||||
/// * [ThemeData.textTheme], text with a color that contrasts with the card
|
||||
/// and canvas colors.
|
||||
/// * [ThemeData.colorScheme], the thirteen colors that most Material widget
|
||||
/// default colors are based on.
|
||||
@immutable
|
||||
class ExpansionTileThemeData with Diagnosticable {
|
||||
/// Creates a [ExpansionTileThemeData].
|
||||
const ExpansionTileThemeData ({
|
||||
this.backgroundColor,
|
||||
this.collapsedBackgroundColor,
|
||||
this.tilePadding,
|
||||
this.expandedAlignment,
|
||||
this.childrenPadding,
|
||||
this.iconColor,
|
||||
this.collapsedIconColor,
|
||||
this.textColor,
|
||||
this.collapsedTextColor,
|
||||
});
|
||||
|
||||
/// Overrides the default value of [ExpansionTile.backgroundColor].
|
||||
final Color? backgroundColor;
|
||||
|
||||
/// Overrides the default value of [ExpansionTile.collapsedBackgroundColor].
|
||||
final Color? collapsedBackgroundColor;
|
||||
|
||||
/// Overrides the default value of [ExpansionTile.tilePadding].
|
||||
final EdgeInsetsGeometry? tilePadding;
|
||||
|
||||
/// Overrides the default value of [ExpansionTile.expandedAlignment].
|
||||
final AlignmentGeometry? expandedAlignment;
|
||||
|
||||
/// Overrides the default value of [ExpansionTile.childrenPadding].
|
||||
final EdgeInsetsGeometry? childrenPadding;
|
||||
|
||||
/// Overrides the default value of [ExpansionTile.iconColor].
|
||||
final Color? iconColor;
|
||||
|
||||
/// Overrides the default value of [ExpansionTile.collapsedIconColor].
|
||||
final Color? collapsedIconColor;
|
||||
|
||||
/// Overrides the default value of [ExpansionTile.textColor].
|
||||
final Color? textColor;
|
||||
|
||||
/// Overrides the default value of [ExpansionTile.collapsedTextColor].
|
||||
final Color? collapsedTextColor;
|
||||
|
||||
/// Creates a copy of this object with the given fields replaced with the
|
||||
/// new values.
|
||||
ExpansionTileThemeData copyWith({
|
||||
Color? backgroundColor,
|
||||
Color? collapsedBackgroundColor,
|
||||
EdgeInsetsGeometry? tilePadding,
|
||||
AlignmentGeometry? expandedAlignment,
|
||||
EdgeInsetsGeometry? childrenPadding,
|
||||
Color? iconColor,
|
||||
Color? collapsedIconColor,
|
||||
Color? textColor,
|
||||
Color? collapsedTextColor,
|
||||
}) {
|
||||
return ExpansionTileThemeData(
|
||||
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||
collapsedBackgroundColor: collapsedBackgroundColor ?? this.collapsedBackgroundColor,
|
||||
tilePadding: tilePadding ?? this.tilePadding,
|
||||
expandedAlignment: expandedAlignment ?? this.expandedAlignment,
|
||||
childrenPadding: childrenPadding ?? this.childrenPadding,
|
||||
iconColor: iconColor ?? this.iconColor,
|
||||
collapsedIconColor: collapsedIconColor ?? this.collapsedIconColor,
|
||||
textColor: textColor ?? this.textColor,
|
||||
collapsedTextColor: collapsedTextColor ?? this.collapsedTextColor,
|
||||
);
|
||||
}
|
||||
|
||||
/// Linearly interpolate between ExpansionTileThemeData objects.
|
||||
static ExpansionTileThemeData? lerp(ExpansionTileThemeData? a, ExpansionTileThemeData? b, double t) {
|
||||
assert (t != null);
|
||||
if (a == null && b == null)
|
||||
return null;
|
||||
return ExpansionTileThemeData(
|
||||
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
|
||||
collapsedBackgroundColor: Color.lerp(a?.collapsedBackgroundColor, b?.collapsedBackgroundColor, t),
|
||||
tilePadding: EdgeInsetsGeometry.lerp(a?.tilePadding, b?.tilePadding, t),
|
||||
expandedAlignment: AlignmentGeometry.lerp(a?.expandedAlignment, b?.expandedAlignment, t),
|
||||
childrenPadding: EdgeInsetsGeometry.lerp(a?.childrenPadding, b?.childrenPadding, t),
|
||||
iconColor: Color.lerp(a?.iconColor, b?.iconColor, t),
|
||||
collapsedIconColor: Color.lerp(a?.collapsedIconColor, b?.collapsedIconColor, t),
|
||||
textColor: Color.lerp(a?.textColor, b?.textColor, t),
|
||||
collapsedTextColor: Color.lerp(a?.collapsedTextColor, b?.collapsedTextColor, t),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashValues(
|
||||
backgroundColor,
|
||||
collapsedBackgroundColor,
|
||||
tilePadding,
|
||||
expandedAlignment,
|
||||
childrenPadding,
|
||||
iconColor,
|
||||
collapsedIconColor,
|
||||
textColor,
|
||||
collapsedTextColor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (other.runtimeType != runtimeType)
|
||||
return false;
|
||||
return other is ExpansionTileThemeData
|
||||
&& other.backgroundColor == backgroundColor
|
||||
&& other.collapsedBackgroundColor == collapsedBackgroundColor
|
||||
&& other.tilePadding == tilePadding
|
||||
&& other.expandedAlignment == expandedAlignment
|
||||
&& other.childrenPadding == childrenPadding
|
||||
&& other.iconColor == iconColor
|
||||
&& other.collapsedIconColor == collapsedIconColor
|
||||
&& other.textColor == textColor
|
||||
&& other.collapsedTextColor == collapsedTextColor;
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
|
||||
properties.add(ColorProperty('collapsedBackgroundColor', collapsedBackgroundColor, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('tilePadding', tilePadding, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<AlignmentGeometry>('expandedAlignment', expandedAlignment, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('childrenPadding', childrenPadding, defaultValue: null));
|
||||
properties.add(ColorProperty('iconColor', iconColor, defaultValue: null));
|
||||
properties.add(ColorProperty('collapsedIconColor', collapsedIconColor, defaultValue: null));
|
||||
properties.add(ColorProperty('textColor', textColor, defaultValue: null));
|
||||
properties.add(ColorProperty('collapsedTextColor', collapsedTextColor, defaultValue: null));
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides the default [ExpansionTileTheme] of its [ExpansionTile] descendants.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ExpansionTileThemeData], which is used to configure this theme.
|
||||
/// * [ThemeData.expansionTileTheme], which can be used to override the default
|
||||
/// [ExpansionTileTheme] for [ExpansionTile]s below the overall [Theme].
|
||||
class ExpansionTileTheme extends InheritedTheme {
|
||||
/// Applies the given theme [data] to [child].
|
||||
///
|
||||
/// The [data] and [child] arguments must not be null.
|
||||
const ExpansionTileTheme({
|
||||
Key? key,
|
||||
required this.data,
|
||||
required Widget child,
|
||||
}) : assert(child != null),
|
||||
assert(data != null),
|
||||
super(key: key, child: child);
|
||||
|
||||
/// Specifies color, alignment, and text style values for
|
||||
/// descendant [ExpansionTile] widgets.
|
||||
final ExpansionTileThemeData data;
|
||||
|
||||
/// The closest instance of this class that encloses the given context.
|
||||
///
|
||||
/// If there is no enclosing [ExpansionTileTheme] widget, then
|
||||
/// [ThemeData.expansionTileTheme] is used.
|
||||
///
|
||||
/// Typical usage is as follows:
|
||||
///
|
||||
/// ```dart
|
||||
/// ExpansionTileThemeData theme = ExpansionTileTheme.of(context);
|
||||
/// ```
|
||||
static ExpansionTileThemeData of(BuildContext context) {
|
||||
final ExpansionTileTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<ExpansionTileTheme>();
|
||||
return inheritedTheme?.data ?? Theme.of(context).expansionTileTheme;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget wrap(BuildContext context, Widget child) {
|
||||
return ExpansionTileTheme(data: data, child: child);
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ExpansionTileTheme oldWidget) => data != oldWidget.data;
|
||||
}
|
@ -24,6 +24,7 @@ import 'dialog_theme.dart';
|
||||
import 'divider_theme.dart';
|
||||
import 'drawer_theme.dart';
|
||||
import 'elevated_button_theme.dart';
|
||||
import 'expansion_tile_theme.dart';
|
||||
import 'floating_action_button_theme.dart';
|
||||
import 'ink_splash.dart';
|
||||
import 'ink_well.dart' show InteractiveInkFeatureFactory;
|
||||
@ -321,6 +322,7 @@ class ThemeData with Diagnosticable {
|
||||
TimePickerThemeData? timePickerTheme,
|
||||
ToggleButtonsThemeData? toggleButtonsTheme,
|
||||
TooltipThemeData? tooltipTheme,
|
||||
ExpansionTileThemeData? expansionTileTheme,
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
@Deprecated(
|
||||
'No longer used by the framework, please remove any reference to it. '
|
||||
@ -541,6 +543,7 @@ class ThemeData with Diagnosticable {
|
||||
timePickerTheme ??= const TimePickerThemeData();
|
||||
toggleButtonsTheme ??= const ToggleButtonsThemeData();
|
||||
tooltipTheme ??= const TooltipThemeData();
|
||||
expansionTileTheme ??= const ExpansionTileThemeData();
|
||||
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
useTextSelectionTheme ??= true;
|
||||
@ -630,6 +633,7 @@ class ThemeData with Diagnosticable {
|
||||
timePickerTheme: timePickerTheme,
|
||||
toggleButtonsTheme: toggleButtonsTheme,
|
||||
tooltipTheme: tooltipTheme,
|
||||
expansionTileTheme: expansionTileTheme,
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
useTextSelectionTheme: useTextSelectionTheme,
|
||||
textSelectionColor: textSelectionColor,
|
||||
@ -735,6 +739,7 @@ class ThemeData with Diagnosticable {
|
||||
required this.timePickerTheme,
|
||||
required this.toggleButtonsTheme,
|
||||
required this.tooltipTheme,
|
||||
required this.expansionTileTheme,
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
@Deprecated(
|
||||
'No longer used by the framework, please remove any reference to it. '
|
||||
@ -873,6 +878,7 @@ class ThemeData with Diagnosticable {
|
||||
assert(timePickerTheme != null),
|
||||
assert(toggleButtonsTheme != null),
|
||||
assert(tooltipTheme != null),
|
||||
assert(expansionTileTheme != null),
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
assert(useTextSelectionTheme != null),
|
||||
assert(textSelectionColor != null),
|
||||
@ -1409,6 +1415,9 @@ class ThemeData with Diagnosticable {
|
||||
/// This is the value returned from [TooltipTheme.of].
|
||||
final TooltipThemeData tooltipTheme;
|
||||
|
||||
/// A theme for customizing the visual properties of [ExpansionTile]s.
|
||||
final ExpansionTileThemeData expansionTileTheme;
|
||||
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
|
||||
/// A temporary flag that was used to opt-in to the new [TextSelectionTheme]
|
||||
@ -1634,6 +1643,7 @@ class ThemeData with Diagnosticable {
|
||||
TimePickerThemeData? timePickerTheme,
|
||||
ToggleButtonsThemeData? toggleButtonsTheme,
|
||||
TooltipThemeData? tooltipTheme,
|
||||
ExpansionTileThemeData? expansionTileTheme,
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
@Deprecated(
|
||||
'No longer used by the framework, please remove any reference to it. '
|
||||
@ -1777,6 +1787,7 @@ class ThemeData with Diagnosticable {
|
||||
timePickerTheme: timePickerTheme ?? this.timePickerTheme,
|
||||
toggleButtonsTheme: toggleButtonsTheme ?? this.toggleButtonsTheme,
|
||||
tooltipTheme: tooltipTheme ?? this.tooltipTheme,
|
||||
expansionTileTheme: expansionTileTheme ?? this.expansionTileTheme,
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
useTextSelectionTheme: useTextSelectionTheme ?? this.useTextSelectionTheme,
|
||||
textSelectionColor: textSelectionColor ?? this.textSelectionColor,
|
||||
@ -1946,6 +1957,7 @@ class ThemeData with Diagnosticable {
|
||||
timePickerTheme: TimePickerThemeData.lerp(a.timePickerTheme, b.timePickerTheme, t),
|
||||
toggleButtonsTheme: ToggleButtonsThemeData.lerp(a.toggleButtonsTheme, b.toggleButtonsTheme, t)!,
|
||||
tooltipTheme: TooltipThemeData.lerp(a.tooltipTheme, b.tooltipTheme, t)!,
|
||||
expansionTileTheme: ExpansionTileThemeData.lerp(a.expansionTileTheme, b.expansionTileTheme, t)!,
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
useTextSelectionTheme: t < 0.5 ? a.useTextSelectionTheme : b.useTextSelectionTheme,
|
||||
textSelectionColor: Color.lerp(a.textSelectionColor, b.textSelectionColor, t)!,
|
||||
@ -2045,6 +2057,7 @@ class ThemeData with Diagnosticable {
|
||||
other.timePickerTheme == timePickerTheme &&
|
||||
other.toggleButtonsTheme == toggleButtonsTheme &&
|
||||
other.tooltipTheme == tooltipTheme &&
|
||||
other.expansionTileTheme == expansionTileTheme &&
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
other.useTextSelectionTheme == useTextSelectionTheme &&
|
||||
other.textSelectionColor == textSelectionColor &&
|
||||
@ -2141,6 +2154,7 @@ class ThemeData with Diagnosticable {
|
||||
timePickerTheme,
|
||||
toggleButtonsTheme,
|
||||
tooltipTheme,
|
||||
expansionTileTheme,
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
useTextSelectionTheme,
|
||||
textSelectionColor,
|
||||
@ -2237,6 +2251,7 @@ class ThemeData with Diagnosticable {
|
||||
properties.add(DiagnosticsProperty<TimePickerThemeData>('timePickerTheme', timePickerTheme, defaultValue: defaultData.timePickerTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<ToggleButtonsThemeData>('toggleButtonsTheme', toggleButtonsTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<TooltipThemeData>('tooltipTheme', tooltipTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<ExpansionTileThemeData>('expansionTileTheme', expansionTileTheme, level: DiagnosticLevel.debug));
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
properties.add(DiagnosticsProperty<bool>('useTextSelectionTheme', useTextSelectionTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(ColorProperty('textSelectionColor', textSelectionColor, defaultValue: defaultData.textSelectionColor, level: DiagnosticLevel.debug));
|
||||
|
257
packages/flutter/test/material/expansion_tile_theme_test.dart
Normal file
257
packages/flutter/test/material/expansion_tile_theme_test.dart
Normal file
@ -0,0 +1,257 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
class TestIcon extends StatefulWidget {
|
||||
const TestIcon({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
TestIconState createState() => TestIconState();
|
||||
}
|
||||
|
||||
class TestIconState extends State<TestIcon> {
|
||||
late IconThemeData iconTheme;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
iconTheme = IconTheme.of(context);
|
||||
return const Icon(Icons.expand_more);
|
||||
}
|
||||
}
|
||||
class TestText extends StatefulWidget {
|
||||
const TestText(this.text, {Key? key}) : super(key: key);
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
TestTextState createState() => TestTextState();
|
||||
}
|
||||
|
||||
class TestTextState extends State<TestText> {
|
||||
late TextStyle textStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
textStyle = DefaultTextStyle.of(context).style;
|
||||
return Text(widget.text);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('ExpansionTileThemeData copyWith, ==, hashCode basics', () {
|
||||
expect(const ExpansionTileThemeData(), const ExpansionTileThemeData().copyWith());
|
||||
expect(const ExpansionTileThemeData().hashCode, const ExpansionTileThemeData().copyWith().hashCode);
|
||||
});
|
||||
|
||||
test('ExpansionTileThemeData defaults', () {
|
||||
const ExpansionTileThemeData theme = ExpansionTileThemeData();
|
||||
expect(theme.backgroundColor, null);
|
||||
expect(theme.collapsedBackgroundColor, null);
|
||||
expect(theme.tilePadding, null);
|
||||
expect(theme.expandedAlignment, null);
|
||||
expect(theme.childrenPadding, null);
|
||||
expect(theme.iconColor, null);
|
||||
expect(theme.collapsedIconColor, null);
|
||||
expect(theme.textColor, null);
|
||||
expect(theme.collapsedTextColor, null);
|
||||
});
|
||||
|
||||
testWidgets('Default ExpansionTileThemeData debugFillProperties', (WidgetTester tester) async {
|
||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||
const TooltipThemeData().debugFillProperties(builder);
|
||||
|
||||
final List<String> description = builder.properties
|
||||
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||
.map((DiagnosticsNode node) => node.toString())
|
||||
.toList();
|
||||
|
||||
expect(description, <String>[]);
|
||||
});
|
||||
|
||||
testWidgets('ExpansionTileThemeData implements debugFillProperties', (WidgetTester tester) async {
|
||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||
const ExpansionTileThemeData(
|
||||
backgroundColor: Color(0xff000000),
|
||||
collapsedBackgroundColor: Color(0xff6f83fc),
|
||||
tilePadding: EdgeInsets.all(20.0),
|
||||
expandedAlignment: Alignment.bottomCenter,
|
||||
childrenPadding: EdgeInsets.all(10.0),
|
||||
iconColor: Color(0xffa7c61c),
|
||||
collapsedIconColor: Color(0xffdd0b1f),
|
||||
textColor: Color(0xffffffff),
|
||||
collapsedTextColor: Color(0xff522bab),
|
||||
).debugFillProperties(builder);
|
||||
|
||||
final List<String> description = builder.properties
|
||||
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||
.map((DiagnosticsNode node) => node.toString())
|
||||
.toList();
|
||||
|
||||
expect(description, <String>[
|
||||
'backgroundColor: Color(0xff000000)',
|
||||
'collapsedBackgroundColor: Color(0xff6f83fc)',
|
||||
'tilePadding: EdgeInsets.all(20.0)',
|
||||
'expandedAlignment: Alignment.bottomCenter',
|
||||
'childrenPadding: EdgeInsets.all(10.0)',
|
||||
'iconColor: Color(0xffa7c61c)',
|
||||
'collapsedIconColor: Color(0xffdd0b1f)',
|
||||
'textColor: Color(0xffffffff)',
|
||||
'collapsedTextColor: Color(0xff522bab)',
|
||||
]);
|
||||
});
|
||||
|
||||
testWidgets('ExpansionTileTheme - collapsed', (WidgetTester tester) async {
|
||||
final Key tileKey = UniqueKey();
|
||||
final Key titleKey = UniqueKey();
|
||||
final Key iconKey = UniqueKey();
|
||||
const Color backgroundColor = Colors.orange;
|
||||
const Color collapsedBackgroundColor = Colors.red;
|
||||
const Color iconColor = Colors.green;
|
||||
const Color collapsedIconColor = Colors.blue;
|
||||
const Color textColor = Colors.black;
|
||||
const Color collapsedTextColor = Colors.white;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
expansionTileTheme: const ExpansionTileThemeData(
|
||||
backgroundColor: backgroundColor,
|
||||
collapsedBackgroundColor: collapsedBackgroundColor,
|
||||
tilePadding: EdgeInsets.fromLTRB(8, 12, 4, 10),
|
||||
expandedAlignment: Alignment.centerRight,
|
||||
childrenPadding: EdgeInsets.all(20.0),
|
||||
iconColor: iconColor,
|
||||
collapsedIconColor: collapsedIconColor,
|
||||
textColor: textColor,
|
||||
collapsedTextColor: collapsedTextColor,
|
||||
),
|
||||
),
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: ExpansionTile(
|
||||
key: tileKey,
|
||||
title: TestText('Collapsed Tile', key: titleKey),
|
||||
trailing: TestIcon(key: iconKey),
|
||||
children: const <Widget>[Text('Tile 1')],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final BoxDecoration boxDecoration = tester.firstWidget<Container>(find.descendant(
|
||||
of: find.byKey(tileKey),
|
||||
matching: find.byType(Container),
|
||||
)).decoration! as BoxDecoration;
|
||||
// Check the tile's collapsed background color when collapsedBackgroundColor is applied.
|
||||
expect(boxDecoration.color, collapsedBackgroundColor);
|
||||
|
||||
final Rect titleRect = tester.getRect(find.text('Collapsed Tile'));
|
||||
final Rect trailingRect = tester.getRect(find.byIcon(Icons.expand_more));
|
||||
final Rect listTileRect = tester.getRect(find.byType(ListTile));
|
||||
final Rect tallerWidget = titleRect.height > trailingRect.height ? titleRect : trailingRect;
|
||||
|
||||
// Check the positions of title and trailing Widgets, after padding is applied.
|
||||
expect(listTileRect.left, titleRect.left - 8);
|
||||
expect(listTileRect.right, trailingRect.right + 4);
|
||||
|
||||
// Calculate the remaining height of ListTile from the default height.
|
||||
final double remainingHeight = 56 - tallerWidget.height;
|
||||
expect(listTileRect.top, tallerWidget.top - remainingHeight / 2 - 12);
|
||||
expect(listTileRect.bottom, tallerWidget.bottom + remainingHeight / 2 + 10);
|
||||
|
||||
Color getIconColor() => tester.state<TestIconState>(find.byType(TestIcon)).iconTheme.color!;
|
||||
Color getTextColor() => tester.state<TestTextState>(find.byType(TestText)).textStyle.color!;
|
||||
|
||||
// Check the collapsed icon color when iconColor is applied.
|
||||
expect(getIconColor(), collapsedIconColor);
|
||||
// Check the collapsed text color when textColor is applied.
|
||||
expect(getTextColor(), collapsedTextColor);
|
||||
});
|
||||
|
||||
testWidgets('ExpansionTileTheme - expanded', (WidgetTester tester) async {
|
||||
final Key tileKey = UniqueKey();
|
||||
final Key titleKey = UniqueKey();
|
||||
final Key iconKey = UniqueKey();
|
||||
const Color backgroundColor = Colors.orange;
|
||||
const Color collapsedBackgroundColor = Colors.red;
|
||||
const Color iconColor = Colors.green;
|
||||
const Color collapsedIconColor = Colors.blue;
|
||||
const Color textColor = Colors.black;
|
||||
const Color collapsedTextColor = Colors.white;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
expansionTileTheme: const ExpansionTileThemeData(
|
||||
backgroundColor: backgroundColor,
|
||||
collapsedBackgroundColor: collapsedBackgroundColor,
|
||||
tilePadding: EdgeInsets.fromLTRB(8, 12, 4, 10),
|
||||
expandedAlignment: Alignment.centerRight,
|
||||
childrenPadding: EdgeInsets.all(20.0),
|
||||
iconColor: iconColor,
|
||||
collapsedIconColor: collapsedIconColor,
|
||||
textColor: textColor,
|
||||
collapsedTextColor: collapsedTextColor,
|
||||
),
|
||||
),
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: ExpansionTile(
|
||||
key: tileKey,
|
||||
initiallyExpanded: true,
|
||||
title: TestText('Expanded Tile', key: titleKey),
|
||||
trailing: TestIcon(key: iconKey),
|
||||
children: const <Widget>[Text('Tile 1')],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final BoxDecoration boxDecoration = tester.firstWidget<Container>(find.descendant(
|
||||
of: find.byKey(tileKey),
|
||||
matching: find.byType(Container),
|
||||
)).decoration! as BoxDecoration;
|
||||
// Check the tile's background color when backgroundColor is applied.
|
||||
expect(boxDecoration.color, backgroundColor);
|
||||
|
||||
final Rect titleRect = tester.getRect(find.text('Expanded Tile'));
|
||||
final Rect trailingRect = tester.getRect(find.byIcon(Icons.expand_more));
|
||||
final Rect listTileRect = tester.getRect(find.byType(ListTile));
|
||||
final Rect tallerWidget = titleRect.height > trailingRect.height ? titleRect : trailingRect;
|
||||
|
||||
// Check the positions of title and trailing Widgets, after padding is applied.
|
||||
expect(listTileRect.left, titleRect.left - 8);
|
||||
expect(listTileRect.right, trailingRect.right + 4);
|
||||
|
||||
// Calculate the remaining height of ListTile from the default height.
|
||||
final double remainingHeight = 56 - tallerWidget.height;
|
||||
expect(listTileRect.top, tallerWidget.top - remainingHeight / 2 - 12);
|
||||
expect(listTileRect.bottom, tallerWidget.bottom + remainingHeight / 2 + 10);
|
||||
|
||||
Color getIconColor() => tester.state<TestIconState>(find.byType(TestIcon)).iconTheme.color!;
|
||||
Color getTextColor() => tester.state<TestTextState>(find.byType(TestText)).textStyle.color!;
|
||||
|
||||
// Check the expanded icon color when iconColor is applied.
|
||||
expect(getIconColor(), iconColor);
|
||||
// Check the expanded text color when textColor is applied.
|
||||
expect(getTextColor(), textColor);
|
||||
|
||||
// Check the child position when expandedAlignment is applied.
|
||||
final Rect childRect = tester.getRect(find.text('Tile 1'));
|
||||
expect(childRect.right, 800 - 20);
|
||||
expect(childRect.left, 800 - childRect.width - 20);
|
||||
|
||||
// Check the child padding when childrenPadding is applied.
|
||||
final Rect paddingRect = tester.getRect(find.byType(Padding).last);
|
||||
expect(childRect.top, paddingRect.top + 20);
|
||||
expect(childRect.left, paddingRect.left + 20);
|
||||
expect(childRect.right, paddingRect.right - 20);
|
||||
expect(childRect.bottom, paddingRect.bottom - 20);
|
||||
});
|
||||
}
|
@ -430,6 +430,7 @@ void main() {
|
||||
sliderTheme: sliderTheme,
|
||||
tabBarTheme: const TabBarTheme(labelColor: Colors.black),
|
||||
tooltipTheme: const TooltipThemeData(height: 100),
|
||||
expansionTileTheme: const ExpansionTileThemeData(backgroundColor: Colors.black),
|
||||
cardTheme: const CardTheme(color: Colors.black),
|
||||
chipTheme: chipTheme,
|
||||
platform: TargetPlatform.iOS,
|
||||
@ -529,6 +530,7 @@ void main() {
|
||||
sliderTheme: otherSliderTheme,
|
||||
tabBarTheme: const TabBarTheme(labelColor: Colors.white),
|
||||
tooltipTheme: const TooltipThemeData(height: 100),
|
||||
expansionTileTheme: const ExpansionTileThemeData(backgroundColor: Colors.black),
|
||||
cardTheme: const CardTheme(color: Colors.white),
|
||||
chipTheme: otherChipTheme,
|
||||
platform: TargetPlatform.android,
|
||||
@ -841,6 +843,7 @@ void main() {
|
||||
'timePickerTheme',
|
||||
'toggleButtonsTheme',
|
||||
'tooltipTheme',
|
||||
'expansionTileTheme',
|
||||
// DEPRECATED (newest deprecations at the bottom)
|
||||
'useTextSelectionTheme',
|
||||
'textSelectionColor',
|
||||
|
Loading…
x
Reference in New Issue
Block a user