blob: 501a608dd5119ea64c2e2196427b76ec74f343e4 [file] [log] [blame]
// 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';
// Examples can assume:
// late BuildContext context;
/// 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({
super.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 super.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;
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;
}