mirror of
https://github.com/flutter/flutter.git
synced 2026-02-12 05:42:27 +08:00
Move ListTileTheme and its tests into separate classes and add visualDensity to the ListTileTheme (#100622)
This commit is contained in:
parent
ba53b55949
commit
321ffa2ead
@ -94,6 +94,7 @@ export 'src/material/input_border.dart';
|
||||
export 'src/material/input_date_picker_form_field.dart';
|
||||
export 'src/material/input_decorator.dart';
|
||||
export 'src/material/list_tile.dart';
|
||||
export 'src/material/list_tile_theme.dart';
|
||||
export 'src/material/material.dart';
|
||||
export 'src/material/material_button.dart';
|
||||
export 'src/material/material_localizations.dart';
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'checkbox.dart';
|
||||
import 'list_tile.dart';
|
||||
import 'list_tile_theme.dart';
|
||||
import 'theme.dart';
|
||||
import 'theme_data.dart';
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import 'colors.dart';
|
||||
import 'debug.dart';
|
||||
import 'drawer_theme.dart';
|
||||
import 'list_tile.dart';
|
||||
import 'list_tile_theme.dart';
|
||||
import 'material.dart';
|
||||
import 'material_localizations.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
@ -9,6 +9,7 @@ import 'colors.dart';
|
||||
import 'expansion_tile_theme.dart';
|
||||
import 'icons.dart';
|
||||
import 'list_tile.dart';
|
||||
import 'list_tile_theme.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
const Duration _kExpand = Duration(milliseconds: 200);
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui' show lerpDouble;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
@ -15,6 +14,7 @@ import 'debug.dart';
|
||||
import 'divider.dart';
|
||||
import 'ink_decoration.dart';
|
||||
import 'ink_well.dart';
|
||||
import 'list_tile_theme.dart';
|
||||
import 'material_state.dart';
|
||||
import 'theme.dart';
|
||||
import 'theme_data.dart';
|
||||
@ -57,480 +57,6 @@ enum ListTileControlAffinity {
|
||||
platform,
|
||||
}
|
||||
|
||||
/// Used with [ListTileTheme] to define default property values for
|
||||
/// descendant [ListTile] widgets, as well as classes that build
|
||||
/// [ListTile]s, like [CheckboxListTile], [RadioListTile], and
|
||||
/// [SwitchListTile].
|
||||
///
|
||||
/// Descendant widgets obtain the current [ListTileThemeData] object
|
||||
/// using `ListTileTheme.of(context)`. Instances of
|
||||
/// [ListTileThemeData] can be customized with
|
||||
/// [ListTileThemeData.copyWith].
|
||||
///
|
||||
/// A [ListTileThemeData] is often specified as part of the
|
||||
/// overall [Theme] with [ThemeData.listTileTheme].
|
||||
///
|
||||
/// All [ListTileThemeData] properties are `null` by default.
|
||||
/// When a theme property is null, the [ListTile] will provide its own
|
||||
/// default based on the overall [Theme]'s textTheme and
|
||||
/// colorScheme. See the individual [ListTile] properties for details.
|
||||
///
|
||||
/// The [Drawer] widget specifies a list tile theme for its children that
|
||||
/// defines [style] to be [ListTileStyle.drawer].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ThemeData], which describes the overall theme information for the
|
||||
/// application.
|
||||
@immutable
|
||||
class ListTileThemeData with Diagnosticable {
|
||||
/// Creates a [ListTileThemeData].
|
||||
const ListTileThemeData ({
|
||||
this.dense,
|
||||
this.shape,
|
||||
this.style,
|
||||
this.selectedColor,
|
||||
this.iconColor,
|
||||
this.textColor,
|
||||
this.contentPadding,
|
||||
this.tileColor,
|
||||
this.selectedTileColor,
|
||||
this.horizontalTitleGap,
|
||||
this.minVerticalPadding,
|
||||
this.minLeadingWidth,
|
||||
this.enableFeedback,
|
||||
this.mouseCursor,
|
||||
});
|
||||
|
||||
/// Overrides the default value of [ListTile.dense].
|
||||
final bool? dense;
|
||||
|
||||
/// Overrides the default value of [ListTile.shape].
|
||||
final ShapeBorder? shape;
|
||||
|
||||
/// Overrides the default value of [ListTile.style].
|
||||
final ListTileStyle? style;
|
||||
|
||||
/// Overrides the default value of [ListTile.selectedColor].
|
||||
final Color? selectedColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.iconColor].
|
||||
final Color? iconColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.textColor].
|
||||
final Color? textColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.contentPadding].
|
||||
final EdgeInsetsGeometry? contentPadding;
|
||||
|
||||
/// Overrides the default value of [ListTile.tileColor].
|
||||
final Color? tileColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.selectedTileColor].
|
||||
final Color? selectedTileColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.horizontalTitleGap].
|
||||
final double? horizontalTitleGap;
|
||||
|
||||
/// Overrides the default value of [ListTile.minVerticalPadding].
|
||||
final double? minVerticalPadding;
|
||||
|
||||
/// Overrides the default value of [ListTile.minLeadingWidth].
|
||||
final double? minLeadingWidth;
|
||||
|
||||
/// Overrides the default value of [ListTile.enableFeedback].
|
||||
final bool? enableFeedback;
|
||||
|
||||
/// If specified, overrides the default value of [ListTile.mouseCursor].
|
||||
final MaterialStateProperty<MouseCursor?>? mouseCursor;
|
||||
|
||||
/// Creates a copy of this object with the given fields replaced with the
|
||||
/// new values.
|
||||
ListTileThemeData copyWith({
|
||||
bool? dense,
|
||||
ShapeBorder? shape,
|
||||
ListTileStyle? style,
|
||||
Color? selectedColor,
|
||||
Color? iconColor,
|
||||
Color? textColor,
|
||||
EdgeInsetsGeometry? contentPadding,
|
||||
Color? tileColor,
|
||||
Color? selectedTileColor,
|
||||
double? horizontalTitleGap,
|
||||
double? minVerticalPadding,
|
||||
double? minLeadingWidth,
|
||||
bool? enableFeedback,
|
||||
MaterialStateProperty<MouseCursor?>? mouseCursor,
|
||||
}) {
|
||||
return ListTileThemeData(
|
||||
dense: dense ?? this.dense,
|
||||
shape: shape ?? this.shape,
|
||||
style: style ?? this.style,
|
||||
selectedColor: selectedColor ?? this.selectedColor,
|
||||
iconColor: iconColor ?? this.iconColor,
|
||||
textColor: textColor ?? this.textColor,
|
||||
contentPadding: contentPadding ?? this.contentPadding,
|
||||
tileColor: tileColor ?? this.tileColor,
|
||||
selectedTileColor: selectedTileColor ?? this.selectedTileColor,
|
||||
horizontalTitleGap: horizontalTitleGap ?? this.horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding ?? this.minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth ?? this.minLeadingWidth,
|
||||
enableFeedback: enableFeedback ?? this.enableFeedback,
|
||||
mouseCursor: mouseCursor ?? this.mouseCursor,
|
||||
);
|
||||
}
|
||||
|
||||
/// Linearly interpolate between ListTileThemeData objects.
|
||||
static ListTileThemeData? lerp(ListTileThemeData? a, ListTileThemeData? b, double t) {
|
||||
assert (t != null);
|
||||
if (a == null && b == null)
|
||||
return null;
|
||||
return ListTileThemeData(
|
||||
dense: t < 0.5 ? a?.dense : b?.dense,
|
||||
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
|
||||
style: t < 0.5 ? a?.style : b?.style,
|
||||
selectedColor: Color.lerp(a?.selectedColor, b?.selectedColor, t),
|
||||
iconColor: Color.lerp(a?.iconColor, b?.iconColor, t),
|
||||
textColor: Color.lerp(a?.textColor, b?.textColor, t),
|
||||
contentPadding: EdgeInsetsGeometry.lerp(a?.contentPadding, b?.contentPadding, t),
|
||||
tileColor: Color.lerp(a?.tileColor, b?.tileColor, t),
|
||||
selectedTileColor: Color.lerp(a?.selectedTileColor, b?.selectedTileColor, t),
|
||||
horizontalTitleGap: lerpDouble(a?.horizontalTitleGap, b?.horizontalTitleGap, t),
|
||||
minVerticalPadding: lerpDouble(a?.minVerticalPadding, b?.minVerticalPadding, t),
|
||||
minLeadingWidth: lerpDouble(a?.minLeadingWidth, b?.minLeadingWidth, t),
|
||||
enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback,
|
||||
mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
dense,
|
||||
shape,
|
||||
style,
|
||||
selectedColor,
|
||||
iconColor,
|
||||
textColor,
|
||||
contentPadding,
|
||||
tileColor,
|
||||
selectedTileColor,
|
||||
horizontalTitleGap,
|
||||
minVerticalPadding,
|
||||
minLeadingWidth,
|
||||
enableFeedback,
|
||||
mouseCursor,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (other.runtimeType != runtimeType)
|
||||
return false;
|
||||
return other is ListTileThemeData
|
||||
&& other.dense == dense
|
||||
&& other.shape == shape
|
||||
&& other.style == style
|
||||
&& other.selectedColor == selectedColor
|
||||
&& other.iconColor == iconColor
|
||||
&& other.textColor == textColor
|
||||
&& other.contentPadding == contentPadding
|
||||
&& other.tileColor == tileColor
|
||||
&& other.selectedTileColor == selectedTileColor
|
||||
&& other.horizontalTitleGap == horizontalTitleGap
|
||||
&& other.minVerticalPadding == minVerticalPadding
|
||||
&& other.minLeadingWidth == minLeadingWidth
|
||||
&& other.enableFeedback == enableFeedback
|
||||
&& other.mouseCursor == mouseCursor;
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<bool>('dense', dense, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
|
||||
properties.add(EnumProperty<ListTileStyle>('style', style, defaultValue: null));
|
||||
properties.add(ColorProperty('selectedColor', selectedColor, defaultValue: null));
|
||||
properties.add(ColorProperty('iconColor', iconColor, defaultValue: null));
|
||||
properties.add(ColorProperty('textColor', textColor, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('contentPadding', contentPadding, defaultValue: null));
|
||||
properties.add(ColorProperty('tileColor', tileColor, defaultValue: null));
|
||||
properties.add(ColorProperty('selectedTileColor', selectedTileColor, defaultValue: null));
|
||||
properties.add(DoubleProperty('horizontalTitleGap', horizontalTitleGap, defaultValue: null));
|
||||
properties.add(DoubleProperty('minVerticalPadding', minVerticalPadding, defaultValue: null));
|
||||
properties.add(DoubleProperty('minLeadingWidth', minLeadingWidth, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null));
|
||||
}
|
||||
}
|
||||
|
||||
/// An inherited widget that defines color and style parameters for [ListTile]s
|
||||
/// in this widget's subtree.
|
||||
///
|
||||
/// Values specified here are used for [ListTile] properties that are not given
|
||||
/// an explicit non-null value.
|
||||
///
|
||||
/// The [Drawer] widget specifies a tile theme for its children which sets
|
||||
/// [style] to [ListTileStyle.drawer].
|
||||
class ListTileTheme extends InheritedTheme {
|
||||
/// Creates a list tile theme that defines the color and style parameters for
|
||||
/// descendant [ListTile]s.
|
||||
///
|
||||
/// Only the [data] parameter should be used. The other parameters are
|
||||
/// redundant (are now obsolete) and will be deprecated in a future update.
|
||||
const ListTileTheme({
|
||||
Key? key,
|
||||
ListTileThemeData? data,
|
||||
bool? dense,
|
||||
ShapeBorder? shape,
|
||||
ListTileStyle? style,
|
||||
Color? selectedColor,
|
||||
Color? iconColor,
|
||||
Color? textColor,
|
||||
EdgeInsetsGeometry? contentPadding,
|
||||
Color? tileColor,
|
||||
Color? selectedTileColor,
|
||||
bool? enableFeedback,
|
||||
MaterialStateProperty<MouseCursor?>? mouseCursor,
|
||||
double? horizontalTitleGap,
|
||||
double? minVerticalPadding,
|
||||
double? minLeadingWidth,
|
||||
required Widget child,
|
||||
}) : assert(
|
||||
data == null ||
|
||||
(shape ??
|
||||
selectedColor ??
|
||||
iconColor ??
|
||||
textColor ??
|
||||
contentPadding ??
|
||||
tileColor ??
|
||||
selectedTileColor ??
|
||||
enableFeedback ??
|
||||
mouseCursor ??
|
||||
horizontalTitleGap ??
|
||||
minVerticalPadding ??
|
||||
minLeadingWidth) == null),
|
||||
_data = data,
|
||||
_dense = dense,
|
||||
_shape = shape,
|
||||
_style = style,
|
||||
_selectedColor = selectedColor,
|
||||
_iconColor = iconColor,
|
||||
_textColor = textColor,
|
||||
_contentPadding = contentPadding,
|
||||
_tileColor = tileColor,
|
||||
_selectedTileColor = selectedTileColor,
|
||||
_enableFeedback = enableFeedback,
|
||||
_mouseCursor = mouseCursor,
|
||||
_horizontalTitleGap = horizontalTitleGap,
|
||||
_minVerticalPadding = minVerticalPadding,
|
||||
_minLeadingWidth = minLeadingWidth,
|
||||
super(key: key, child: child);
|
||||
|
||||
final ListTileThemeData? _data;
|
||||
final bool? _dense;
|
||||
final ShapeBorder? _shape;
|
||||
final ListTileStyle? _style;
|
||||
final Color? _selectedColor;
|
||||
final Color? _iconColor;
|
||||
final Color? _textColor;
|
||||
final EdgeInsetsGeometry? _contentPadding;
|
||||
final Color? _tileColor;
|
||||
final Color? _selectedTileColor;
|
||||
final double? _horizontalTitleGap;
|
||||
final double? _minVerticalPadding;
|
||||
final double? _minLeadingWidth;
|
||||
final bool? _enableFeedback;
|
||||
final MaterialStateProperty<MouseCursor?>? _mouseCursor;
|
||||
|
||||
/// The configuration of this theme.
|
||||
ListTileThemeData get data {
|
||||
return _data ?? ListTileThemeData(
|
||||
dense: _dense,
|
||||
shape: _shape,
|
||||
style: _style,
|
||||
selectedColor: _selectedColor,
|
||||
iconColor: _iconColor,
|
||||
textColor: _textColor,
|
||||
contentPadding: _contentPadding,
|
||||
tileColor: _tileColor,
|
||||
selectedTileColor: _selectedTileColor,
|
||||
enableFeedback: _enableFeedback,
|
||||
mouseCursor: _mouseCursor,
|
||||
horizontalTitleGap: _horizontalTitleGap,
|
||||
minVerticalPadding: _minVerticalPadding,
|
||||
minLeadingWidth: _minLeadingWidth,
|
||||
);
|
||||
}
|
||||
|
||||
/// Overrides the default value of [ListTile.dense].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.dense] property instead.
|
||||
bool? get dense => _data != null ? _data!.dense : _dense;
|
||||
|
||||
/// Overrides the default value of [ListTile.shape].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.shape] property instead.
|
||||
ShapeBorder? get shape => _data != null ? _data!.shape : _shape;
|
||||
|
||||
/// Overrides the default value of [ListTile.style].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.style] property instead.
|
||||
ListTileStyle? get style => _data != null ? _data!.style : _style;
|
||||
|
||||
/// Overrides the default value of [ListTile.selectedColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.selectedColor] property instead.
|
||||
Color? get selectedColor => _data != null ? _data!.selectedColor : _selectedColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.iconColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.iconColor] property instead.
|
||||
Color? get iconColor => _data != null ? _data!.iconColor : _iconColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.textColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.textColor] property instead.
|
||||
Color? get textColor => _data != null ? _data!.textColor : _textColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.contentPadding].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.contentPadding] property instead.
|
||||
EdgeInsetsGeometry? get contentPadding => _data != null ? _data!.contentPadding : _contentPadding;
|
||||
|
||||
/// Overrides the default value of [ListTile.tileColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.tileColor] property instead.
|
||||
Color? get tileColor => _data != null ? _data!.tileColor : _tileColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.selectedTileColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.selectedTileColor] property instead.
|
||||
Color? get selectedTileColor => _data != null ? _data!.selectedTileColor : _selectedTileColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.horizontalTitleGap].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.horizontalTitleGap] property instead.
|
||||
double? get horizontalTitleGap => _data != null ? _data!.horizontalTitleGap : _horizontalTitleGap;
|
||||
|
||||
/// Overrides the default value of [ListTile.minVerticalPadding].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.minVerticalPadding] property instead.
|
||||
double? get minVerticalPadding => _data != null ? _data!.minVerticalPadding : _minVerticalPadding;
|
||||
|
||||
/// Overrides the default value of [ListTile.minLeadingWidth].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.minLeadingWidth] property instead.
|
||||
double? get minLeadingWidth => _data != null ? _data!.minLeadingWidth : _minLeadingWidth;
|
||||
|
||||
/// Overrides the default value of [ListTile.enableFeedback].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.enableFeedback] property instead.
|
||||
bool? get enableFeedback => _data != null ? _data!.enableFeedback : _enableFeedback;
|
||||
|
||||
/// The [data] property of the closest instance of this class that
|
||||
/// encloses the given context.
|
||||
///
|
||||
/// If there is no enclosing [ListTileTheme] widget, then
|
||||
/// [ThemeData.listTileTheme] is used (see [Theme.of]).
|
||||
///
|
||||
/// Typical usage is as follows:
|
||||
///
|
||||
/// ```dart
|
||||
/// ListTileThemeData theme = ListTileTheme.of(context);
|
||||
/// ```
|
||||
static ListTileThemeData of(BuildContext context) {
|
||||
final ListTileTheme? result = context.dependOnInheritedWidgetOfExactType<ListTileTheme>();
|
||||
return result?.data ?? Theme.of(context).listTileTheme;
|
||||
}
|
||||
|
||||
/// Creates a list tile theme that controls the color and style parameters for
|
||||
/// [ListTile]s, and merges in the current list tile theme, if any.
|
||||
///
|
||||
/// The [child] argument must not be null.
|
||||
static Widget merge({
|
||||
Key? key,
|
||||
bool? dense,
|
||||
ShapeBorder? shape,
|
||||
ListTileStyle? style,
|
||||
Color? selectedColor,
|
||||
Color? iconColor,
|
||||
Color? textColor,
|
||||
EdgeInsetsGeometry? contentPadding,
|
||||
Color? tileColor,
|
||||
Color? selectedTileColor,
|
||||
bool? enableFeedback,
|
||||
double? horizontalTitleGap,
|
||||
double? minVerticalPadding,
|
||||
double? minLeadingWidth,
|
||||
required Widget child,
|
||||
}) {
|
||||
assert(child != null);
|
||||
return Builder(
|
||||
builder: (BuildContext context) {
|
||||
final ListTileThemeData parent = ListTileTheme.of(context);
|
||||
return ListTileTheme(
|
||||
key: key,
|
||||
data: ListTileThemeData(
|
||||
dense: dense ?? parent.dense,
|
||||
shape: shape ?? parent.shape,
|
||||
style: style ?? parent.style,
|
||||
selectedColor: selectedColor ?? parent.selectedColor,
|
||||
iconColor: iconColor ?? parent.iconColor,
|
||||
textColor: textColor ?? parent.textColor,
|
||||
contentPadding: contentPadding ?? parent.contentPadding,
|
||||
tileColor: tileColor ?? parent.tileColor,
|
||||
selectedTileColor: selectedTileColor ?? parent.selectedTileColor,
|
||||
enableFeedback: enableFeedback ?? parent.enableFeedback,
|
||||
horizontalTitleGap: horizontalTitleGap ?? parent.horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding ?? parent.minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth ?? parent.minLeadingWidth,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget wrap(BuildContext context, Widget child) {
|
||||
return ListTileTheme(
|
||||
data: ListTileThemeData(
|
||||
dense: dense,
|
||||
shape: shape,
|
||||
style: style,
|
||||
selectedColor: selectedColor,
|
||||
iconColor: iconColor,
|
||||
textColor: textColor,
|
||||
contentPadding: contentPadding,
|
||||
tileColor: tileColor,
|
||||
selectedTileColor: selectedTileColor,
|
||||
enableFeedback: enableFeedback,
|
||||
horizontalTitleGap: horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ListTileTheme oldWidget) => data != oldWidget.data;
|
||||
}
|
||||
|
||||
/// A single fixed-height row that typically contains some text as well as
|
||||
/// a leading or trailing icon.
|
||||
///
|
||||
@ -1262,7 +788,7 @@ class ListTile extends StatelessWidget {
|
||||
subtitle: subtitleText,
|
||||
trailing: trailingIcon,
|
||||
isDense: _isDenseLayout(theme, tileTheme),
|
||||
visualDensity: visualDensity ?? theme.visualDensity,
|
||||
visualDensity: visualDensity ?? tileTheme.visualDensity ?? theme.visualDensity,
|
||||
isThreeLine: isThreeLine,
|
||||
textDirection: textDirection,
|
||||
titleBaselineType: titleStyle.textBaseline!,
|
||||
|
||||
499
packages/flutter/lib/src/material/list_tile_theme.dart
Normal file
499
packages/flutter/lib/src/material/list_tile_theme.dart
Normal file
@ -0,0 +1,499 @@
|
||||
// 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' show lerpDouble;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'list_tile.dart';
|
||||
import 'material_state.dart';
|
||||
import 'theme.dart';
|
||||
import 'theme_data.dart';
|
||||
|
||||
/// Used with [ListTileTheme] to define default property values for
|
||||
/// descendant [ListTile] widgets, as well as classes that build
|
||||
/// [ListTile]s, like [CheckboxListTile], [RadioListTile], and
|
||||
/// [SwitchListTile].
|
||||
///
|
||||
/// Descendant widgets obtain the current [ListTileThemeData] object
|
||||
/// using `ListTileTheme.of(context)`. Instances of
|
||||
/// [ListTileThemeData] can be customized with
|
||||
/// [ListTileThemeData.copyWith].
|
||||
///
|
||||
/// A [ListTileThemeData] is often specified as part of the
|
||||
/// overall [Theme] with [ThemeData.listTileTheme].
|
||||
///
|
||||
/// All [ListTileThemeData] properties are `null` by default.
|
||||
/// When a theme property is null, the [ListTile] will provide its own
|
||||
/// default based on the overall [Theme]'s textTheme and
|
||||
/// colorScheme. See the individual [ListTile] properties for details.
|
||||
///
|
||||
/// The [Drawer] widget specifies a list tile theme for its children that
|
||||
/// defines [style] to be [ListTileStyle.drawer].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ThemeData], which describes the overall theme information for the
|
||||
/// application.
|
||||
@immutable
|
||||
class ListTileThemeData with Diagnosticable {
|
||||
/// Creates a [ListTileThemeData].
|
||||
const ListTileThemeData ({
|
||||
this.dense,
|
||||
this.shape,
|
||||
this.style,
|
||||
this.selectedColor,
|
||||
this.iconColor,
|
||||
this.textColor,
|
||||
this.contentPadding,
|
||||
this.tileColor,
|
||||
this.selectedTileColor,
|
||||
this.horizontalTitleGap,
|
||||
this.minVerticalPadding,
|
||||
this.minLeadingWidth,
|
||||
this.enableFeedback,
|
||||
this.mouseCursor,
|
||||
this.visualDensity,
|
||||
});
|
||||
|
||||
/// Overrides the default value of [ListTile.dense].
|
||||
final bool? dense;
|
||||
|
||||
/// Overrides the default value of [ListTile.shape].
|
||||
final ShapeBorder? shape;
|
||||
|
||||
/// Overrides the default value of [ListTile.style].
|
||||
final ListTileStyle? style;
|
||||
|
||||
/// Overrides the default value of [ListTile.selectedColor].
|
||||
final Color? selectedColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.iconColor].
|
||||
final Color? iconColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.textColor].
|
||||
final Color? textColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.contentPadding].
|
||||
final EdgeInsetsGeometry? contentPadding;
|
||||
|
||||
/// Overrides the default value of [ListTile.tileColor].
|
||||
final Color? tileColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.selectedTileColor].
|
||||
final Color? selectedTileColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.horizontalTitleGap].
|
||||
final double? horizontalTitleGap;
|
||||
|
||||
/// Overrides the default value of [ListTile.minVerticalPadding].
|
||||
final double? minVerticalPadding;
|
||||
|
||||
/// Overrides the default value of [ListTile.minLeadingWidth].
|
||||
final double? minLeadingWidth;
|
||||
|
||||
/// Overrides the default value of [ListTile.enableFeedback].
|
||||
final bool? enableFeedback;
|
||||
|
||||
/// If specified, overrides the default value of [ListTile.mouseCursor].
|
||||
final MaterialStateProperty<MouseCursor?>? mouseCursor;
|
||||
|
||||
/// If specified, overrides the default value of [ListTile.visualDensity].
|
||||
final VisualDensity? visualDensity;
|
||||
|
||||
/// Creates a copy of this object with the given fields replaced with the
|
||||
/// new values.
|
||||
ListTileThemeData copyWith({
|
||||
bool? dense,
|
||||
ShapeBorder? shape,
|
||||
ListTileStyle? style,
|
||||
Color? selectedColor,
|
||||
Color? iconColor,
|
||||
Color? textColor,
|
||||
EdgeInsetsGeometry? contentPadding,
|
||||
Color? tileColor,
|
||||
Color? selectedTileColor,
|
||||
double? horizontalTitleGap,
|
||||
double? minVerticalPadding,
|
||||
double? minLeadingWidth,
|
||||
bool? enableFeedback,
|
||||
MaterialStateProperty<MouseCursor?>? mouseCursor,
|
||||
bool? isThreeLine,
|
||||
VisualDensity? visualDensity,
|
||||
}) {
|
||||
return ListTileThemeData(
|
||||
dense: dense ?? this.dense,
|
||||
shape: shape ?? this.shape,
|
||||
style: style ?? this.style,
|
||||
selectedColor: selectedColor ?? this.selectedColor,
|
||||
iconColor: iconColor ?? this.iconColor,
|
||||
textColor: textColor ?? this.textColor,
|
||||
contentPadding: contentPadding ?? this.contentPadding,
|
||||
tileColor: tileColor ?? this.tileColor,
|
||||
selectedTileColor: selectedTileColor ?? this.selectedTileColor,
|
||||
horizontalTitleGap: horizontalTitleGap ?? this.horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding ?? this.minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth ?? this.minLeadingWidth,
|
||||
enableFeedback: enableFeedback ?? this.enableFeedback,
|
||||
mouseCursor: mouseCursor ?? this.mouseCursor,
|
||||
visualDensity: visualDensity ?? this.visualDensity,
|
||||
);
|
||||
}
|
||||
|
||||
/// Linearly interpolate between ListTileThemeData objects.
|
||||
static ListTileThemeData? lerp(ListTileThemeData? a, ListTileThemeData? b, double t) {
|
||||
assert (t != null);
|
||||
if (a == null && b == null)
|
||||
return null;
|
||||
return ListTileThemeData(
|
||||
dense: t < 0.5 ? a?.dense : b?.dense,
|
||||
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
|
||||
style: t < 0.5 ? a?.style : b?.style,
|
||||
selectedColor: Color.lerp(a?.selectedColor, b?.selectedColor, t),
|
||||
iconColor: Color.lerp(a?.iconColor, b?.iconColor, t),
|
||||
textColor: Color.lerp(a?.textColor, b?.textColor, t),
|
||||
contentPadding: EdgeInsetsGeometry.lerp(a?.contentPadding, b?.contentPadding, t),
|
||||
tileColor: Color.lerp(a?.tileColor, b?.tileColor, t),
|
||||
selectedTileColor: Color.lerp(a?.selectedTileColor, b?.selectedTileColor, t),
|
||||
horizontalTitleGap: lerpDouble(a?.horizontalTitleGap, b?.horizontalTitleGap, t),
|
||||
minVerticalPadding: lerpDouble(a?.minVerticalPadding, b?.minVerticalPadding, t),
|
||||
minLeadingWidth: lerpDouble(a?.minLeadingWidth, b?.minLeadingWidth, t),
|
||||
enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback,
|
||||
mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor,
|
||||
visualDensity: t < 0.5 ? a?.visualDensity : b?.visualDensity,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
dense,
|
||||
shape,
|
||||
style,
|
||||
selectedColor,
|
||||
iconColor,
|
||||
textColor,
|
||||
contentPadding,
|
||||
tileColor,
|
||||
selectedTileColor,
|
||||
horizontalTitleGap,
|
||||
minVerticalPadding,
|
||||
minLeadingWidth,
|
||||
enableFeedback,
|
||||
mouseCursor,
|
||||
visualDensity,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (other.runtimeType != runtimeType)
|
||||
return false;
|
||||
return other is ListTileThemeData
|
||||
&& other.dense == dense
|
||||
&& other.shape == shape
|
||||
&& other.style == style
|
||||
&& other.selectedColor == selectedColor
|
||||
&& other.iconColor == iconColor
|
||||
&& other.textColor == textColor
|
||||
&& other.contentPadding == contentPadding
|
||||
&& other.tileColor == tileColor
|
||||
&& other.selectedTileColor == selectedTileColor
|
||||
&& other.horizontalTitleGap == horizontalTitleGap
|
||||
&& other.minVerticalPadding == minVerticalPadding
|
||||
&& other.minLeadingWidth == minLeadingWidth
|
||||
&& other.enableFeedback == enableFeedback
|
||||
&& other.mouseCursor == mouseCursor
|
||||
&& other.visualDensity == visualDensity;
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<bool>('dense', dense, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
|
||||
properties.add(EnumProperty<ListTileStyle>('style', style, defaultValue: null));
|
||||
properties.add(ColorProperty('selectedColor', selectedColor, defaultValue: null));
|
||||
properties.add(ColorProperty('iconColor', iconColor, defaultValue: null));
|
||||
properties.add(ColorProperty('textColor', textColor, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('contentPadding', contentPadding, defaultValue: null));
|
||||
properties.add(ColorProperty('tileColor', tileColor, defaultValue: null));
|
||||
properties.add(ColorProperty('selectedTileColor', selectedTileColor, defaultValue: null));
|
||||
properties.add(DoubleProperty('horizontalTitleGap', horizontalTitleGap, defaultValue: null));
|
||||
properties.add(DoubleProperty('minVerticalPadding', minVerticalPadding, defaultValue: null));
|
||||
properties.add(DoubleProperty('minLeadingWidth', minLeadingWidth, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<VisualDensity>('visualDensity', visualDensity, defaultValue: null));
|
||||
}
|
||||
}
|
||||
|
||||
/// An inherited widget that defines color and style parameters for [ListTile]s
|
||||
/// in this widget's subtree.
|
||||
///
|
||||
/// Values specified here are used for [ListTile] properties that are not given
|
||||
/// an explicit non-null value.
|
||||
///
|
||||
/// The [Drawer] widget specifies a tile theme for its children which sets
|
||||
/// [style] to [ListTileStyle.drawer].
|
||||
class ListTileTheme extends InheritedTheme {
|
||||
/// Creates a list tile theme that defines the color and style parameters for
|
||||
/// descendant [ListTile]s.
|
||||
///
|
||||
/// Only the [data] parameter should be used. The other parameters are
|
||||
/// redundant (are now obsolete) and will be deprecated in a future update.
|
||||
const ListTileTheme({
|
||||
Key? key,
|
||||
ListTileThemeData? data,
|
||||
bool? dense,
|
||||
ShapeBorder? shape,
|
||||
ListTileStyle? style,
|
||||
Color? selectedColor,
|
||||
Color? iconColor,
|
||||
Color? textColor,
|
||||
EdgeInsetsGeometry? contentPadding,
|
||||
Color? tileColor,
|
||||
Color? selectedTileColor,
|
||||
bool? enableFeedback,
|
||||
MaterialStateProperty<MouseCursor?>? mouseCursor,
|
||||
double? horizontalTitleGap,
|
||||
double? minVerticalPadding,
|
||||
double? minLeadingWidth,
|
||||
required Widget child,
|
||||
}) : assert(
|
||||
data == null ||
|
||||
(shape ??
|
||||
selectedColor ??
|
||||
iconColor ??
|
||||
textColor ??
|
||||
contentPadding ??
|
||||
tileColor ??
|
||||
selectedTileColor ??
|
||||
enableFeedback ??
|
||||
mouseCursor ??
|
||||
horizontalTitleGap ??
|
||||
minVerticalPadding ??
|
||||
minLeadingWidth) == null),
|
||||
_data = data,
|
||||
_dense = dense,
|
||||
_shape = shape,
|
||||
_style = style,
|
||||
_selectedColor = selectedColor,
|
||||
_iconColor = iconColor,
|
||||
_textColor = textColor,
|
||||
_contentPadding = contentPadding,
|
||||
_tileColor = tileColor,
|
||||
_selectedTileColor = selectedTileColor,
|
||||
_enableFeedback = enableFeedback,
|
||||
_mouseCursor = mouseCursor,
|
||||
_horizontalTitleGap = horizontalTitleGap,
|
||||
_minVerticalPadding = minVerticalPadding,
|
||||
_minLeadingWidth = minLeadingWidth,
|
||||
super(key: key, child: child);
|
||||
|
||||
final ListTileThemeData? _data;
|
||||
final bool? _dense;
|
||||
final ShapeBorder? _shape;
|
||||
final ListTileStyle? _style;
|
||||
final Color? _selectedColor;
|
||||
final Color? _iconColor;
|
||||
final Color? _textColor;
|
||||
final EdgeInsetsGeometry? _contentPadding;
|
||||
final Color? _tileColor;
|
||||
final Color? _selectedTileColor;
|
||||
final double? _horizontalTitleGap;
|
||||
final double? _minVerticalPadding;
|
||||
final double? _minLeadingWidth;
|
||||
final bool? _enableFeedback;
|
||||
final MaterialStateProperty<MouseCursor?>? _mouseCursor;
|
||||
|
||||
/// The configuration of this theme.
|
||||
ListTileThemeData get data {
|
||||
return _data ?? ListTileThemeData(
|
||||
dense: _dense,
|
||||
shape: _shape,
|
||||
style: _style,
|
||||
selectedColor: _selectedColor,
|
||||
iconColor: _iconColor,
|
||||
textColor: _textColor,
|
||||
contentPadding: _contentPadding,
|
||||
tileColor: _tileColor,
|
||||
selectedTileColor: _selectedTileColor,
|
||||
enableFeedback: _enableFeedback,
|
||||
mouseCursor: _mouseCursor,
|
||||
horizontalTitleGap: _horizontalTitleGap,
|
||||
minVerticalPadding: _minVerticalPadding,
|
||||
minLeadingWidth: _minLeadingWidth,
|
||||
);
|
||||
}
|
||||
|
||||
/// Overrides the default value of [ListTile.dense].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.dense] property instead.
|
||||
bool? get dense => _data != null ? _data!.dense : _dense;
|
||||
|
||||
/// Overrides the default value of [ListTile.shape].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.shape] property instead.
|
||||
ShapeBorder? get shape => _data != null ? _data!.shape : _shape;
|
||||
|
||||
/// Overrides the default value of [ListTile.style].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.style] property instead.
|
||||
ListTileStyle? get style => _data != null ? _data!.style : _style;
|
||||
|
||||
/// Overrides the default value of [ListTile.selectedColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.selectedColor] property instead.
|
||||
Color? get selectedColor => _data != null ? _data!.selectedColor : _selectedColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.iconColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.iconColor] property instead.
|
||||
Color? get iconColor => _data != null ? _data!.iconColor : _iconColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.textColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.textColor] property instead.
|
||||
Color? get textColor => _data != null ? _data!.textColor : _textColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.contentPadding].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.contentPadding] property instead.
|
||||
EdgeInsetsGeometry? get contentPadding => _data != null ? _data!.contentPadding : _contentPadding;
|
||||
|
||||
/// Overrides the default value of [ListTile.tileColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.tileColor] property instead.
|
||||
Color? get tileColor => _data != null ? _data!.tileColor : _tileColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.selectedTileColor].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.selectedTileColor] property instead.
|
||||
Color? get selectedTileColor => _data != null ? _data!.selectedTileColor : _selectedTileColor;
|
||||
|
||||
/// Overrides the default value of [ListTile.horizontalTitleGap].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.horizontalTitleGap] property instead.
|
||||
double? get horizontalTitleGap => _data != null ? _data!.horizontalTitleGap : _horizontalTitleGap;
|
||||
|
||||
/// Overrides the default value of [ListTile.minVerticalPadding].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.minVerticalPadding] property instead.
|
||||
double? get minVerticalPadding => _data != null ? _data!.minVerticalPadding : _minVerticalPadding;
|
||||
|
||||
/// Overrides the default value of [ListTile.minLeadingWidth].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.minLeadingWidth] property instead.
|
||||
double? get minLeadingWidth => _data != null ? _data!.minLeadingWidth : _minLeadingWidth;
|
||||
|
||||
/// Overrides the default value of [ListTile.enableFeedback].
|
||||
///
|
||||
/// This property is obsolete: please use the [data]
|
||||
/// [ListTileThemeData.enableFeedback] property instead.
|
||||
bool? get enableFeedback => _data != null ? _data!.enableFeedback : _enableFeedback;
|
||||
|
||||
/// The [data] property of the closest instance of this class that
|
||||
/// encloses the given context.
|
||||
///
|
||||
/// If there is no enclosing [ListTileTheme] widget, then
|
||||
/// [ThemeData.listTileTheme] is used (see [Theme.of]).
|
||||
///
|
||||
/// Typical usage is as follows:
|
||||
///
|
||||
/// ```dart
|
||||
/// ListTileThemeData theme = ListTileTheme.of(context);
|
||||
/// ```
|
||||
static ListTileThemeData of(BuildContext context) {
|
||||
final ListTileTheme? result = context.dependOnInheritedWidgetOfExactType<ListTileTheme>();
|
||||
return result?.data ?? Theme.of(context).listTileTheme;
|
||||
}
|
||||
|
||||
/// Creates a list tile theme that controls the color and style parameters for
|
||||
/// [ListTile]s, and merges in the current list tile theme, if any.
|
||||
///
|
||||
/// The [child] argument must not be null.
|
||||
static Widget merge({
|
||||
Key? key,
|
||||
bool? dense,
|
||||
ShapeBorder? shape,
|
||||
ListTileStyle? style,
|
||||
Color? selectedColor,
|
||||
Color? iconColor,
|
||||
Color? textColor,
|
||||
EdgeInsetsGeometry? contentPadding,
|
||||
Color? tileColor,
|
||||
Color? selectedTileColor,
|
||||
bool? enableFeedback,
|
||||
double? horizontalTitleGap,
|
||||
double? minVerticalPadding,
|
||||
double? minLeadingWidth,
|
||||
required Widget child,
|
||||
}) {
|
||||
assert(child != null);
|
||||
return Builder(
|
||||
builder: (BuildContext context) {
|
||||
final ListTileThemeData parent = ListTileTheme.of(context);
|
||||
return ListTileTheme(
|
||||
key: key,
|
||||
data: ListTileThemeData(
|
||||
dense: dense ?? parent.dense,
|
||||
shape: shape ?? parent.shape,
|
||||
style: style ?? parent.style,
|
||||
selectedColor: selectedColor ?? parent.selectedColor,
|
||||
iconColor: iconColor ?? parent.iconColor,
|
||||
textColor: textColor ?? parent.textColor,
|
||||
contentPadding: contentPadding ?? parent.contentPadding,
|
||||
tileColor: tileColor ?? parent.tileColor,
|
||||
selectedTileColor: selectedTileColor ?? parent.selectedTileColor,
|
||||
enableFeedback: enableFeedback ?? parent.enableFeedback,
|
||||
horizontalTitleGap: horizontalTitleGap ?? parent.horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding ?? parent.minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth ?? parent.minLeadingWidth,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget wrap(BuildContext context, Widget child) {
|
||||
return ListTileTheme(
|
||||
data: ListTileThemeData(
|
||||
dense: dense,
|
||||
shape: shape,
|
||||
style: style,
|
||||
selectedColor: selectedColor,
|
||||
iconColor: iconColor,
|
||||
textColor: textColor,
|
||||
contentPadding: contentPadding,
|
||||
tileColor: tileColor,
|
||||
selectedTileColor: selectedTileColor,
|
||||
enableFeedback: enableFeedback,
|
||||
horizontalTitleGap: horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ListTileTheme oldWidget) => data != oldWidget.data;
|
||||
}
|
||||
@ -5,6 +5,7 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'list_tile.dart';
|
||||
import 'list_tile_theme.dart';
|
||||
import 'radio.dart';
|
||||
import 'theme.dart';
|
||||
import 'theme_data.dart';
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'list_tile.dart';
|
||||
import 'list_tile_theme.dart';
|
||||
import 'switch.dart';
|
||||
import 'theme.dart';
|
||||
import 'theme_data.dart';
|
||||
|
||||
@ -31,6 +31,7 @@ import 'ink_splash.dart';
|
||||
import 'ink_well.dart' show InteractiveInkFeatureFactory;
|
||||
import 'input_decorator.dart';
|
||||
import 'list_tile.dart';
|
||||
import 'list_tile_theme.dart';
|
||||
import 'navigation_bar_theme.dart';
|
||||
import 'navigation_rail_theme.dart';
|
||||
import 'outlined_button_theme.dart';
|
||||
|
||||
@ -51,133 +51,6 @@ class TestTextState extends State<TestText> {
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('ListTileThemeData copyWith, ==, hashCode basics', () {
|
||||
expect(const ListTileThemeData(), const ListTileThemeData().copyWith());
|
||||
expect(const ListTileThemeData().hashCode, const ListTileThemeData().copyWith().hashCode);
|
||||
});
|
||||
|
||||
test('ListTileThemeData defaults', () {
|
||||
const ListTileThemeData themeData = ListTileThemeData();
|
||||
expect(themeData.dense, null);
|
||||
expect(themeData.shape, null);
|
||||
expect(themeData.style, null);
|
||||
expect(themeData.selectedColor, null);
|
||||
expect(themeData.iconColor, null);
|
||||
expect(themeData.textColor, null);
|
||||
expect(themeData.contentPadding, null);
|
||||
expect(themeData.tileColor, null);
|
||||
expect(themeData.selectedTileColor, null);
|
||||
expect(themeData.horizontalTitleGap, null);
|
||||
expect(themeData.minVerticalPadding, null);
|
||||
expect(themeData.minLeadingWidth, null);
|
||||
expect(themeData.enableFeedback, null);
|
||||
expect(themeData.mouseCursor, null);
|
||||
});
|
||||
|
||||
testWidgets('Default ListTileThemeData debugFillProperties', (WidgetTester tester) async {
|
||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||
const ListTileThemeData().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('ListTileThemeData implements debugFillProperties', (WidgetTester tester) async {
|
||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||
const ListTileThemeData(
|
||||
dense: true,
|
||||
shape: StadiumBorder(),
|
||||
style: ListTileStyle.drawer,
|
||||
selectedColor: Color(0x00000001),
|
||||
iconColor: Color(0x00000002),
|
||||
textColor: Color(0x00000003),
|
||||
contentPadding: EdgeInsets.all(100),
|
||||
tileColor: Color(0x00000004),
|
||||
selectedTileColor: Color(0x00000005),
|
||||
horizontalTitleGap: 200,
|
||||
minVerticalPadding: 300,
|
||||
minLeadingWidth: 400,
|
||||
enableFeedback: true,
|
||||
mouseCursor: MaterialStateMouseCursor.clickable,
|
||||
).debugFillProperties(builder);
|
||||
|
||||
final List<String> description = builder.properties
|
||||
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||
.map((DiagnosticsNode node) => node.toString())
|
||||
.toList();
|
||||
|
||||
expect(description, <String>[
|
||||
'dense: true',
|
||||
'shape: StadiumBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none))',
|
||||
'style: drawer',
|
||||
'selectedColor: Color(0x00000001)',
|
||||
'iconColor: Color(0x00000002)',
|
||||
'textColor: Color(0x00000003)',
|
||||
'contentPadding: EdgeInsets.all(100.0)',
|
||||
'tileColor: Color(0x00000004)',
|
||||
'selectedTileColor: Color(0x00000005)',
|
||||
'horizontalTitleGap: 200.0',
|
||||
'minVerticalPadding: 300.0',
|
||||
'minLeadingWidth: 400.0',
|
||||
'enableFeedback: true',
|
||||
'mouseCursor: MaterialStateMouseCursor(clickable)',
|
||||
]);
|
||||
});
|
||||
|
||||
testWidgets('ListTileTheme backwards compatibility constructor', (WidgetTester tester) async {
|
||||
late ListTileThemeData theme;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: ListTileTheme(
|
||||
dense: true,
|
||||
shape: const StadiumBorder(),
|
||||
style: ListTileStyle.drawer,
|
||||
selectedColor: const Color(0x00000001),
|
||||
iconColor: const Color(0x00000002),
|
||||
textColor: const Color(0x00000003),
|
||||
contentPadding: const EdgeInsets.all(100),
|
||||
tileColor: const Color(0x00000004),
|
||||
selectedTileColor: const Color(0x00000005),
|
||||
horizontalTitleGap: 200,
|
||||
minVerticalPadding: 300,
|
||||
minLeadingWidth: 400,
|
||||
enableFeedback: true,
|
||||
mouseCursor: MaterialStateMouseCursor.clickable,
|
||||
child: Center(
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
theme = ListTileTheme.of(context);
|
||||
return const Placeholder();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(theme.dense, true);
|
||||
expect(theme.shape, const StadiumBorder());
|
||||
expect(theme.style, ListTileStyle.drawer);
|
||||
expect(theme.selectedColor, const Color(0x00000001));
|
||||
expect(theme.iconColor, const Color(0x00000002));
|
||||
expect(theme.textColor, const Color(0x00000003));
|
||||
expect(theme.contentPadding, const EdgeInsets.all(100));
|
||||
expect(theme.tileColor, const Color(0x00000004));
|
||||
expect(theme.selectedTileColor, const Color(0x00000005));
|
||||
expect(theme.horizontalTitleGap, 200);
|
||||
expect(theme.minVerticalPadding, 300);
|
||||
expect(theme.minLeadingWidth, 400);
|
||||
expect(theme.enableFeedback, true);
|
||||
expect(theme.mouseCursor, MaterialStateMouseCursor.clickable);
|
||||
});
|
||||
|
||||
testWidgets('ListTile geometry (LTR)', (WidgetTester tester) async {
|
||||
// See https://material.io/go/design-lists
|
||||
|
||||
@ -403,126 +276,6 @@ void main() {
|
||||
expect(callCount, 1);
|
||||
});
|
||||
|
||||
testWidgets('ListTileTheme', (WidgetTester tester) async {
|
||||
final Key titleKey = UniqueKey();
|
||||
final Key subtitleKey = UniqueKey();
|
||||
final Key leadingKey = UniqueKey();
|
||||
final Key trailingKey = UniqueKey();
|
||||
late ThemeData theme;
|
||||
|
||||
Widget buildFrame({
|
||||
bool enabled = true,
|
||||
bool dense = false,
|
||||
bool selected = false,
|
||||
ShapeBorder? shape,
|
||||
Color? selectedColor,
|
||||
Color? iconColor,
|
||||
Color? textColor,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: ListTileTheme(
|
||||
data: ListTileThemeData(
|
||||
dense: dense,
|
||||
shape: shape,
|
||||
selectedColor: selectedColor,
|
||||
iconColor: iconColor,
|
||||
textColor: textColor,
|
||||
mouseCursor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return SystemMouseCursors.forbidden;
|
||||
}
|
||||
|
||||
return SystemMouseCursors.click;
|
||||
}),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
theme = Theme.of(context);
|
||||
return ListTile(
|
||||
enabled: enabled,
|
||||
selected: selected,
|
||||
leading: TestIcon(key: leadingKey),
|
||||
trailing: TestIcon(key: trailingKey),
|
||||
title: TestText('title', key: titleKey),
|
||||
subtitle: TestText('subtitle', key: subtitleKey),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const Color green = Color(0xFF00FF00);
|
||||
const Color red = Color(0xFFFF0000);
|
||||
const ShapeBorder roundedShape = RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(4.0)),
|
||||
);
|
||||
|
||||
Color iconColor(Key key) => tester.state<TestIconState>(find.byKey(key)).iconTheme.color!;
|
||||
Color textColor(Key key) => tester.state<TestTextState>(find.byKey(key)).textStyle.color!;
|
||||
ShapeBorder inkWellBorder() => tester.widget<InkWell>(find.descendant(of: find.byType(ListTile), matching: find.byType(InkWell))).customBorder!;
|
||||
|
||||
// A selected ListTile's leading, trailing, and text get the primary color by default
|
||||
await tester.pumpWidget(buildFrame(selected: true));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), theme.primaryColor);
|
||||
expect(iconColor(trailingKey), theme.primaryColor);
|
||||
expect(textColor(titleKey), theme.primaryColor);
|
||||
expect(textColor(subtitleKey), theme.primaryColor);
|
||||
|
||||
// A selected ListTile's leading, trailing, and text get the ListTileTheme's selectedColor
|
||||
await tester.pumpWidget(buildFrame(selected: true, selectedColor: green));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), green);
|
||||
expect(iconColor(trailingKey), green);
|
||||
expect(textColor(titleKey), green);
|
||||
expect(textColor(subtitleKey), green);
|
||||
|
||||
// An unselected ListTile's leading and trailing get the ListTileTheme's iconColor
|
||||
// An unselected ListTile's title texts get the ListTileTheme's textColor
|
||||
await tester.pumpWidget(buildFrame(iconColor: red, textColor: green));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), red);
|
||||
expect(iconColor(trailingKey), red);
|
||||
expect(textColor(titleKey), green);
|
||||
expect(textColor(subtitleKey), green);
|
||||
|
||||
// If the item is disabled it's rendered with the theme's disabled color.
|
||||
await tester.pumpWidget(buildFrame(enabled: false));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), theme.disabledColor);
|
||||
expect(iconColor(trailingKey), theme.disabledColor);
|
||||
expect(textColor(titleKey), theme.disabledColor);
|
||||
expect(textColor(subtitleKey), theme.disabledColor);
|
||||
|
||||
// If the item is disabled it's rendered with the theme's disabled color.
|
||||
// Even if it's selected.
|
||||
await tester.pumpWidget(buildFrame(enabled: false, selected: true));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), theme.disabledColor);
|
||||
expect(iconColor(trailingKey), theme.disabledColor);
|
||||
expect(textColor(titleKey), theme.disabledColor);
|
||||
expect(textColor(subtitleKey), theme.disabledColor);
|
||||
|
||||
// A selected ListTile's InkWell gets the ListTileTheme's shape
|
||||
await tester.pumpWidget(buildFrame(selected: true, shape: roundedShape));
|
||||
expect(inkWellBorder(), roundedShape);
|
||||
|
||||
// Cursor updates when hovering disabled ListTile
|
||||
await tester.pumpWidget(buildFrame(enabled: false));
|
||||
final Offset listTile = tester.getCenter(find.byKey(titleKey));
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
await gesture.addPointer();
|
||||
addTearDown(gesture.removePointer);
|
||||
await gesture.moveTo(listTile);
|
||||
await tester.pumpAndSettle();
|
||||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.forbidden);
|
||||
});
|
||||
|
||||
testWidgets('ListTile semantics', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = SemanticsTester(tester);
|
||||
|
||||
@ -686,7 +439,7 @@ void main() {
|
||||
expect(right('L'), 790.0); // 800 - contentPadding.start
|
||||
});
|
||||
|
||||
testWidgets('ListTileTheme wide leading Widget', (WidgetTester tester) async {
|
||||
testWidgets('ListTile wide leading Widget', (WidgetTester tester) async {
|
||||
const Key leadingKey = ValueKey<String>('L');
|
||||
|
||||
Widget buildFrame(double leadingWidth, TextDirection textDirection) {
|
||||
@ -1870,88 +1623,6 @@ void main() {
|
||||
expect(find.byType(Material), paints..path(color: defaultColor));
|
||||
});
|
||||
|
||||
testWidgets("ListTile respects ListTileTheme's tileColor & selectedTileColor", (WidgetTester tester) async {
|
||||
late ListTileThemeData theme;
|
||||
bool isSelected = false;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: ListTileTheme(
|
||||
data: ListTileThemeData(
|
||||
tileColor: Colors.green.shade500,
|
||||
selectedTileColor: Colors.red.shade500,
|
||||
),
|
||||
child: Center(
|
||||
child: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
theme = ListTileTheme.of(context);
|
||||
return ListTile(
|
||||
selected: isSelected,
|
||||
onTap: () {
|
||||
setState(()=> isSelected = !isSelected);
|
||||
},
|
||||
title: const Text('Title'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: theme.tileColor));
|
||||
|
||||
// Tap on tile to change isSelected.
|
||||
await tester.tap(find.byType(ListTile));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Material), paints..path(color: theme.selectedTileColor));
|
||||
});
|
||||
|
||||
testWidgets("ListTileTheme's tileColor & selectedTileColor are overridden by ListTile properties", (WidgetTester tester) async {
|
||||
bool isSelected = false;
|
||||
final Color tileColor = Colors.green.shade500;
|
||||
final Color selectedTileColor = Colors.red.shade500;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: ListTileTheme(
|
||||
data: const ListTileThemeData(
|
||||
selectedTileColor: Colors.green,
|
||||
tileColor: Colors.red,
|
||||
),
|
||||
child: Center(
|
||||
child: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return ListTile(
|
||||
tileColor: tileColor,
|
||||
selectedTileColor: selectedTileColor,
|
||||
selected: isSelected,
|
||||
onTap: () {
|
||||
setState(()=> isSelected = !isSelected);
|
||||
},
|
||||
title: const Text('Title'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: tileColor));
|
||||
|
||||
// Tap on tile to change isSelected.
|
||||
await tester.tap(find.byType(ListTile));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||
});
|
||||
|
||||
testWidgets('ListTile layout at zero size', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/66636
|
||||
const Key key = Key('key');
|
||||
@ -2408,66 +2079,6 @@ void main() {
|
||||
expect(textColor(trailingKey), theme.disabledColor);
|
||||
});
|
||||
|
||||
testWidgets('ListTileTheme colors are applied to leading and trailing text widgets', (WidgetTester tester) async {
|
||||
final Key leadingKey = UniqueKey();
|
||||
final Key trailingKey = UniqueKey();
|
||||
|
||||
const Color selectedColor = Colors.orange;
|
||||
const Color defaultColor = Colors.black;
|
||||
|
||||
late ThemeData theme;
|
||||
Widget buildFrame({
|
||||
bool enabled = true,
|
||||
bool selected = false,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: ListTileTheme(
|
||||
data: const ListTileThemeData(
|
||||
selectedColor: selectedColor,
|
||||
textColor: defaultColor,
|
||||
),
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
theme = Theme.of(context);
|
||||
return ListTile(
|
||||
enabled: enabled,
|
||||
selected: selected,
|
||||
leading: TestText('leading', key: leadingKey),
|
||||
title: const TestText('title'),
|
||||
trailing: TestText('trailing', key: trailingKey),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Color textColor(Key key) => tester.state<TestTextState>(find.byKey(key)).textStyle.color!;
|
||||
|
||||
await tester.pumpWidget(buildFrame());
|
||||
// Enabled color should use ListTileTheme.textColor.
|
||||
expect(textColor(leadingKey), defaultColor);
|
||||
expect(textColor(trailingKey), defaultColor);
|
||||
|
||||
await tester.pumpWidget(buildFrame(selected: true));
|
||||
// Wait for text color to animate.
|
||||
await tester.pumpAndSettle();
|
||||
// Selected color should use ListTileTheme.selectedColor.
|
||||
expect(textColor(leadingKey), selectedColor);
|
||||
expect(textColor(trailingKey), selectedColor);
|
||||
|
||||
await tester.pumpWidget(buildFrame(enabled: false));
|
||||
// Wait for text color to animate.
|
||||
await tester.pumpAndSettle();
|
||||
// Disabled color should be ThemeData.disabledColor.
|
||||
expect(textColor(leadingKey), theme.disabledColor);
|
||||
expect(textColor(trailingKey), theme.disabledColor);
|
||||
});
|
||||
|
||||
testWidgets('selected, enabled ListTile default icon color, light and dark themes', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/pull/77004
|
||||
|
||||
|
||||
448
packages/flutter/test/material/list_tile_theme_test.dart
Normal file
448
packages/flutter/test/material/list_tile_theme_test.dart
Normal file
@ -0,0 +1,448 @@
|
||||
// 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/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../rendering/mock_canvas.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.add);
|
||||
}
|
||||
}
|
||||
|
||||
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('ListTileThemeData copyWith, ==, hashCode basics', () {
|
||||
expect(const ListTileThemeData(), const ListTileThemeData().copyWith());
|
||||
expect(const ListTileThemeData().hashCode, const ListTileThemeData().copyWith().hashCode);
|
||||
});
|
||||
|
||||
test('ListTileThemeData defaults', () {
|
||||
const ListTileThemeData themeData = ListTileThemeData();
|
||||
expect(themeData.dense, null);
|
||||
expect(themeData.shape, null);
|
||||
expect(themeData.style, null);
|
||||
expect(themeData.selectedColor, null);
|
||||
expect(themeData.iconColor, null);
|
||||
expect(themeData.textColor, null);
|
||||
expect(themeData.contentPadding, null);
|
||||
expect(themeData.tileColor, null);
|
||||
expect(themeData.selectedTileColor, null);
|
||||
expect(themeData.horizontalTitleGap, null);
|
||||
expect(themeData.minVerticalPadding, null);
|
||||
expect(themeData.minLeadingWidth, null);
|
||||
expect(themeData.enableFeedback, null);
|
||||
expect(themeData.mouseCursor, null);
|
||||
expect(themeData.visualDensity, null);
|
||||
});
|
||||
|
||||
testWidgets('Default ListTileThemeData debugFillProperties', (WidgetTester tester) async {
|
||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||
const ListTileThemeData().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('ListTileThemeData implements debugFillProperties', (WidgetTester tester) async {
|
||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||
const ListTileThemeData(
|
||||
dense: true,
|
||||
shape: StadiumBorder(),
|
||||
style: ListTileStyle.drawer,
|
||||
selectedColor: Color(0x00000001),
|
||||
iconColor: Color(0x00000002),
|
||||
textColor: Color(0x00000003),
|
||||
contentPadding: EdgeInsets.all(100),
|
||||
tileColor: Color(0x00000004),
|
||||
selectedTileColor: Color(0x00000005),
|
||||
horizontalTitleGap: 200,
|
||||
minVerticalPadding: 300,
|
||||
minLeadingWidth: 400,
|
||||
enableFeedback: true,
|
||||
mouseCursor: MaterialStateMouseCursor.clickable,
|
||||
visualDensity: VisualDensity.comfortable,
|
||||
).debugFillProperties(builder);
|
||||
|
||||
final List<String> description = builder.properties
|
||||
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||
.map((DiagnosticsNode node) => node.toString())
|
||||
.toList();
|
||||
|
||||
expect(description[0], 'dense: true');
|
||||
expect(description[1], 'shape: StadiumBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none))');
|
||||
expect(description[2], 'style: drawer');
|
||||
expect(description[3], 'selectedColor: Color(0x00000001)');
|
||||
expect(description[4], 'iconColor: Color(0x00000002)');
|
||||
expect(description[5], 'textColor: Color(0x00000003)');
|
||||
expect(description[6], 'contentPadding: EdgeInsets.all(100.0)');
|
||||
expect(description[7], 'tileColor: Color(0x00000004)');
|
||||
expect(description[8], 'selectedTileColor: Color(0x00000005)');
|
||||
expect(description[9], 'horizontalTitleGap: 200.0');
|
||||
expect(description[10], 'minVerticalPadding: 300.0');
|
||||
expect(description[11], 'minLeadingWidth: 400.0');
|
||||
expect(description[12], 'enableFeedback: true');
|
||||
expect(description[13], 'mouseCursor: MaterialStateMouseCursor(clickable)');
|
||||
expect(
|
||||
description[14],
|
||||
equalsIgnoringHashCodes('visualDensity: VisualDensity#00000(h: -1.0, v: -1.0)(horizontal: -1.0, vertical: -1.0)'),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('ListTileTheme backwards compatibility constructor', (WidgetTester tester) async {
|
||||
late ListTileThemeData theme;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: ListTileTheme(
|
||||
dense: true,
|
||||
shape: const StadiumBorder(),
|
||||
style: ListTileStyle.drawer,
|
||||
selectedColor: const Color(0x00000001),
|
||||
iconColor: const Color(0x00000002),
|
||||
textColor: const Color(0x00000003),
|
||||
contentPadding: const EdgeInsets.all(100),
|
||||
tileColor: const Color(0x00000004),
|
||||
selectedTileColor: const Color(0x00000005),
|
||||
horizontalTitleGap: 200,
|
||||
minVerticalPadding: 300,
|
||||
minLeadingWidth: 400,
|
||||
enableFeedback: true,
|
||||
mouseCursor: MaterialStateMouseCursor.clickable,
|
||||
child: Center(
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
theme = ListTileTheme.of(context);
|
||||
return const Placeholder();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(theme.dense, true);
|
||||
expect(theme.shape, const StadiumBorder());
|
||||
expect(theme.style, ListTileStyle.drawer);
|
||||
expect(theme.selectedColor, const Color(0x00000001));
|
||||
expect(theme.iconColor, const Color(0x00000002));
|
||||
expect(theme.textColor, const Color(0x00000003));
|
||||
expect(theme.contentPadding, const EdgeInsets.all(100));
|
||||
expect(theme.tileColor, const Color(0x00000004));
|
||||
expect(theme.selectedTileColor, const Color(0x00000005));
|
||||
expect(theme.horizontalTitleGap, 200);
|
||||
expect(theme.minVerticalPadding, 300);
|
||||
expect(theme.minLeadingWidth, 400);
|
||||
expect(theme.enableFeedback, true);
|
||||
expect(theme.mouseCursor, MaterialStateMouseCursor.clickable);
|
||||
});
|
||||
|
||||
testWidgets('ListTileTheme', (WidgetTester tester) async {
|
||||
final Key listTileKey = UniqueKey();
|
||||
final Key titleKey = UniqueKey();
|
||||
final Key subtitleKey = UniqueKey();
|
||||
final Key leadingKey = UniqueKey();
|
||||
final Key trailingKey = UniqueKey();
|
||||
late ThemeData theme;
|
||||
|
||||
Widget buildFrame({
|
||||
bool enabled = true,
|
||||
bool dense = false,
|
||||
bool selected = false,
|
||||
ShapeBorder? shape,
|
||||
Color? selectedColor,
|
||||
Color? iconColor,
|
||||
Color? textColor,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: ListTileTheme(
|
||||
data: ListTileThemeData(
|
||||
dense: dense,
|
||||
shape: shape,
|
||||
selectedColor: selectedColor,
|
||||
iconColor: iconColor,
|
||||
textColor: textColor,
|
||||
mouseCursor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return SystemMouseCursors.forbidden;
|
||||
}
|
||||
|
||||
return SystemMouseCursors.click;
|
||||
}),
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
theme = Theme.of(context);
|
||||
return ListTile(
|
||||
key: listTileKey,
|
||||
enabled: enabled,
|
||||
selected: selected,
|
||||
leading: TestIcon(key: leadingKey),
|
||||
trailing: TestIcon(key: trailingKey),
|
||||
title: TestText('title', key: titleKey),
|
||||
subtitle: TestText('subtitle', key: subtitleKey),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const Color green = Color(0xFF00FF00);
|
||||
const Color red = Color(0xFFFF0000);
|
||||
const ShapeBorder roundedShape = RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(4.0)),
|
||||
);
|
||||
|
||||
Color iconColor(Key key) => tester.state<TestIconState>(find.byKey(key)).iconTheme.color!;
|
||||
Color textColor(Key key) => tester.state<TestTextState>(find.byKey(key)).textStyle.color!;
|
||||
ShapeBorder inkWellBorder() => tester.widget<InkWell>(find.descendant(of: find.byType(ListTile), matching: find.byType(InkWell))).customBorder!;
|
||||
|
||||
// A selected ListTile's leading, trailing, and text get the primary color by default
|
||||
await tester.pumpWidget(buildFrame(selected: true));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), theme.primaryColor);
|
||||
expect(iconColor(trailingKey), theme.primaryColor);
|
||||
expect(textColor(titleKey), theme.primaryColor);
|
||||
expect(textColor(subtitleKey), theme.primaryColor);
|
||||
|
||||
// A selected ListTile's leading, trailing, and text get the ListTileTheme's selectedColor
|
||||
await tester.pumpWidget(buildFrame(selected: true, selectedColor: green));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), green);
|
||||
expect(iconColor(trailingKey), green);
|
||||
expect(textColor(titleKey), green);
|
||||
expect(textColor(subtitleKey), green);
|
||||
|
||||
// An unselected ListTile's leading and trailing get the ListTileTheme's iconColor
|
||||
// An unselected ListTile's title texts get the ListTileTheme's textColor
|
||||
await tester.pumpWidget(buildFrame(iconColor: red, textColor: green));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), red);
|
||||
expect(iconColor(trailingKey), red);
|
||||
expect(textColor(titleKey), green);
|
||||
expect(textColor(subtitleKey), green);
|
||||
|
||||
// If the item is disabled it's rendered with the theme's disabled color.
|
||||
await tester.pumpWidget(buildFrame(enabled: false));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), theme.disabledColor);
|
||||
expect(iconColor(trailingKey), theme.disabledColor);
|
||||
expect(textColor(titleKey), theme.disabledColor);
|
||||
expect(textColor(subtitleKey), theme.disabledColor);
|
||||
|
||||
// If the item is disabled it's rendered with the theme's disabled color.
|
||||
// Even if it's selected.
|
||||
await tester.pumpWidget(buildFrame(enabled: false, selected: true));
|
||||
await tester.pump(const Duration(milliseconds: 300)); // DefaultTextStyle changes animate
|
||||
expect(iconColor(leadingKey), theme.disabledColor);
|
||||
expect(iconColor(trailingKey), theme.disabledColor);
|
||||
expect(textColor(titleKey), theme.disabledColor);
|
||||
expect(textColor(subtitleKey), theme.disabledColor);
|
||||
|
||||
// A selected ListTile's InkWell gets the ListTileTheme's shape
|
||||
await tester.pumpWidget(buildFrame(selected: true, shape: roundedShape));
|
||||
expect(inkWellBorder(), roundedShape);
|
||||
|
||||
// Cursor updates when hovering disabled ListTile
|
||||
await tester.pumpWidget(buildFrame(enabled: false));
|
||||
final Offset listTile = tester.getCenter(find.byKey(titleKey));
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
await gesture.addPointer();
|
||||
addTearDown(gesture.removePointer);
|
||||
await gesture.moveTo(listTile);
|
||||
await tester.pumpAndSettle();
|
||||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.forbidden);
|
||||
|
||||
// VisualDensity is respected
|
||||
final RenderBox box = tester.renderObject(find.byKey(listTileKey));
|
||||
expect(box.size, equals(const Size(800, 64.0)));
|
||||
});
|
||||
|
||||
testWidgets('ListTileTheme colors are applied to leading and trailing text widgets', (WidgetTester tester) async {
|
||||
final Key leadingKey = UniqueKey();
|
||||
final Key trailingKey = UniqueKey();
|
||||
|
||||
const Color selectedColor = Colors.orange;
|
||||
const Color defaultColor = Colors.black;
|
||||
|
||||
late ThemeData theme;
|
||||
Widget buildFrame({
|
||||
bool enabled = true,
|
||||
bool selected = false,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: ListTileTheme(
|
||||
data: const ListTileThemeData(
|
||||
selectedColor: selectedColor,
|
||||
textColor: defaultColor,
|
||||
),
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
theme = Theme.of(context);
|
||||
return ListTile(
|
||||
enabled: enabled,
|
||||
selected: selected,
|
||||
leading: TestText('leading', key: leadingKey),
|
||||
title: const TestText('title'),
|
||||
trailing: TestText('trailing', key: trailingKey),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Color textColor(Key key) => tester.state<TestTextState>(find.byKey(key)).textStyle.color!;
|
||||
|
||||
await tester.pumpWidget(buildFrame());
|
||||
// Enabled color should use ListTileTheme.textColor.
|
||||
expect(textColor(leadingKey), defaultColor);
|
||||
expect(textColor(trailingKey), defaultColor);
|
||||
|
||||
await tester.pumpWidget(buildFrame(selected: true));
|
||||
// Wait for text color to animate.
|
||||
await tester.pumpAndSettle();
|
||||
// Selected color should use ListTileTheme.selectedColor.
|
||||
expect(textColor(leadingKey), selectedColor);
|
||||
expect(textColor(trailingKey), selectedColor);
|
||||
|
||||
await tester.pumpWidget(buildFrame(enabled: false));
|
||||
// Wait for text color to animate.
|
||||
await tester.pumpAndSettle();
|
||||
// Disabled color should be ThemeData.disabledColor.
|
||||
expect(textColor(leadingKey), theme.disabledColor);
|
||||
expect(textColor(trailingKey), theme.disabledColor);
|
||||
});
|
||||
|
||||
testWidgets("ListTile respects ListTileTheme's tileColor & selectedTileColor", (WidgetTester tester) async {
|
||||
late ListTileThemeData theme;
|
||||
bool isSelected = false;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: ListTileTheme(
|
||||
data: ListTileThemeData(
|
||||
tileColor: Colors.green.shade500,
|
||||
selectedTileColor: Colors.red.shade500,
|
||||
),
|
||||
child: Center(
|
||||
child: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
theme = ListTileTheme.of(context);
|
||||
return ListTile(
|
||||
selected: isSelected,
|
||||
onTap: () {
|
||||
setState(()=> isSelected = !isSelected);
|
||||
},
|
||||
title: const Text('Title'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: theme.tileColor));
|
||||
|
||||
// Tap on tile to change isSelected.
|
||||
await tester.tap(find.byType(ListTile));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Material), paints..path(color: theme.selectedTileColor));
|
||||
});
|
||||
|
||||
testWidgets("ListTileTheme's tileColor & selectedTileColor are overridden by ListTile properties", (WidgetTester tester) async {
|
||||
bool isSelected = false;
|
||||
final Color tileColor = Colors.green.shade500;
|
||||
final Color selectedTileColor = Colors.red.shade500;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: ListTileTheme(
|
||||
data: const ListTileThemeData(
|
||||
selectedTileColor: Colors.green,
|
||||
tileColor: Colors.red,
|
||||
),
|
||||
child: Center(
|
||||
child: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return ListTile(
|
||||
tileColor: tileColor,
|
||||
selectedTileColor: selectedTileColor,
|
||||
selected: isSelected,
|
||||
onTap: () {
|
||||
setState(()=> isSelected = !isSelected);
|
||||
},
|
||||
title: const Text('Title'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: tileColor));
|
||||
|
||||
// Tap on tile to change isSelected.
|
||||
await tester.tap(find.byType(ListTile));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user