blob: dd5352003e83fd6bb9d2953d214a9af6eb0c8d24 [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 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'input_decorator.dart';
import 'theme.dart';
/// Defines the visual properties of the widget displayed with [showTimePicker].
///
/// Descendant widgets obtain the current [TimePickerThemeData] object using
/// `TimePickerTheme.of(context)`. Instances of [TimePickerThemeData]
/// can be customized with [TimePickerThemeData.copyWith].
///
/// Typically a [TimePickerThemeData] is specified as part of the overall
/// [Theme] with [ThemeData.timePickerTheme].
///
/// All [TimePickerThemeData] properties are `null` by default. When null,
/// [showTimePicker] will provide its own defaults.
///
/// See also:
///
/// * [ThemeData], which describes the overall theme information for the
/// application.
/// * [TimePickerTheme], which describes the actual configuration of a time
/// picker theme.
@immutable
class TimePickerThemeData with Diagnosticable {
/// Creates a theme that can be used for [TimePickerTheme] or
/// [ThemeData.timePickerTheme].
const TimePickerThemeData({
this.backgroundColor,
this.hourMinuteTextColor,
this.hourMinuteColor,
this.dayPeriodTextColor,
this.dayPeriodColor,
this.dialHandColor,
this.dialBackgroundColor,
this.dialTextColor,
this.entryModeIconColor,
this.hourMinuteTextStyle,
this.dayPeriodTextStyle,
this.helpTextStyle,
this.shape,
this.hourMinuteShape,
this.dayPeriodShape,
this.dayPeriodBorderSide,
this.inputDecorationTheme,
});
/// The background color of a time picker.
///
/// If this is null, the time picker defaults to the overall theme's
/// [ColorScheme.background].
final Color? backgroundColor;
/// The color of the header text that represents hours and minutes.
///
/// If [hourMinuteTextColor] is a [MaterialStateColor], then the effective
/// text color can depend on the [MaterialState.selected] state, i.e. if the
/// text is selected or not.
///
/// By default the overall theme's [ColorScheme.primary] color is used when
/// the text is selected and [ColorScheme.onSurface] when it's not selected.
final Color? hourMinuteTextColor;
/// The background color of the hour and minutes header segments.
///
/// If [hourMinuteColor] is a [MaterialStateColor], then the effective
/// background color can depend on the [MaterialState.selected] state, i.e.
/// if the segment is selected or not.
///
/// By default, if the segment is selected, the overall theme's
/// `ColorScheme.primary.withOpacity(0.12)` is used when the overall theme's
/// brightness is [Brightness.light] and
/// `ColorScheme.primary.withOpacity(0.24)` is used when the overall theme's
/// brightness is [Brightness.dark].
/// If the segment is not selected, the overall theme's
/// `ColorScheme.onSurface.withOpacity(0.12)` is used.
final Color? hourMinuteColor;
/// The color of the day period text that represents AM/PM.
///
/// If [dayPeriodTextColor] is a [MaterialStateColor], then the effective
/// text color can depend on the [MaterialState.selected] state, i.e. if the
/// text is selected or not.
///
/// By default the overall theme's [ColorScheme.primary] color is used when
/// the text is selected and `ColorScheme.onSurface.withOpacity(0.60)` when
/// it's not selected.
final Color? dayPeriodTextColor;
/// The background color of the AM/PM toggle.
///
/// If [dayPeriodColor] is a [MaterialStateColor], then the effective
/// background color can depend on the [MaterialState.selected] state, i.e.
/// if the segment is selected or not.
///
/// By default, if the segment is selected, the overall theme's
/// `ColorScheme.primary.withOpacity(0.12)` is used when the overall theme's
/// brightness is [Brightness.light] and
/// `ColorScheme.primary.withOpacity(0.24)` is used when the overall theme's
/// brightness is [Brightness.dark].
/// If the segment is not selected, [Colors.transparent] is used to allow the
/// [Dialog]'s color to be used.
final Color? dayPeriodColor;
/// The color of the time picker dial's hand.
///
/// If this is null, the time picker defaults to the overall theme's
/// [ColorScheme.primary].
final Color? dialHandColor;
/// The background color of the time picker dial.
///
/// If this is null, the time picker defaults to the overall theme's
/// [ColorScheme.primary].
final Color? dialBackgroundColor;
/// The color of the dial text that represents specific hours and minutes.
///
/// If [dialTextColor] is a [MaterialStateColor], then the effective
/// text color can depend on the [MaterialState.selected] state, i.e. if the
/// text is selected or not.
///
/// By default the overall theme's `textTheme` color is used when the text is
/// selected and `accentTextTheme` color is used when it's not selected.
final Color? dialTextColor;
/// The color of the entry mode [IconButton].
///
/// If this is null, the time picker defaults to:
/// ```
/// Theme.of(context).colorScheme.onSurface.withOpacity(
/// Theme.of(context).colorScheme.brightness == Brightness.dark ? 1.0 : 0.6,
/// )
/// ```
final Color? entryModeIconColor;
/// Used to configure the [TextStyle]s for the hour/minute controls.
///
/// If this is null, the time picker defaults to the overall theme's
/// [TextTheme.headline3].
final TextStyle? hourMinuteTextStyle;
/// Used to configure the [TextStyle]s for the day period control.
///
/// If this is null, the time picker defaults to the overall theme's
/// [TextTheme.subtitle1].
final TextStyle? dayPeriodTextStyle;
/// Used to configure the [TextStyle]s for the helper text in the header.
///
/// If this is null, the time picker defaults to the overall theme's
/// [TextTheme.overline].
final TextStyle? helpTextStyle;
/// The shape of the [Dialog] that the time picker is presented in.
///
/// If this is null, the time picker defaults to
/// `RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)))`.
final ShapeBorder? shape;
/// The shape of the hour and minute controls that the time picker uses.
///
/// If this is null, the time picker defaults to
/// `RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)))`.
final ShapeBorder? hourMinuteShape;
/// The shape of the day period that the time picker uses.
///
/// If this is null, the time picker defaults to:
/// ```
/// RoundedRectangleBorder(
/// borderRadius: BorderRadius.all(Radius.circular(4.0)),
/// side: BorderSide(),
/// )
/// ```
final OutlinedBorder? dayPeriodShape;
/// The color and weight of the day period's outline.
///
/// If this is null, the time picker defaults to:
/// ```
/// BorderSide(
/// color: Color.alphaBlend(colorScheme.onBackground.withOpacity(0.38), colorScheme.surface),
/// )
/// ```
final BorderSide? dayPeriodBorderSide;
/// The input decoration theme for the [TextField]s in the time picker.
///
/// If this is null, the time picker provides its own defaults.
final InputDecorationTheme? inputDecorationTheme;
/// Creates a copy of this object with the given fields replaced with the
/// new values.
TimePickerThemeData copyWith({
Color? backgroundColor,
Color? hourMinuteTextColor,
Color? hourMinuteColor,
Color? dayPeriodTextColor,
Color? dayPeriodColor,
Color? dialHandColor,
Color? dialBackgroundColor,
Color? dialTextColor,
Color? entryModeIconColor,
TextStyle? hourMinuteTextStyle,
TextStyle? dayPeriodTextStyle,
TextStyle? helpTextStyle,
ShapeBorder? shape,
ShapeBorder? hourMinuteShape,
OutlinedBorder? dayPeriodShape,
BorderSide? dayPeriodBorderSide,
InputDecorationTheme? inputDecorationTheme,
}) {
return TimePickerThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
hourMinuteTextColor: hourMinuteTextColor ?? this.hourMinuteTextColor,
hourMinuteColor: hourMinuteColor ?? this.hourMinuteColor,
dayPeriodTextColor: dayPeriodTextColor ?? this.dayPeriodTextColor,
dayPeriodColor: dayPeriodColor ?? this.dayPeriodColor,
dialHandColor: dialHandColor ?? this.dialHandColor,
dialBackgroundColor: dialBackgroundColor ?? this.dialBackgroundColor,
dialTextColor: dialTextColor ?? this.dialTextColor,
entryModeIconColor: entryModeIconColor ?? this.entryModeIconColor,
hourMinuteTextStyle: hourMinuteTextStyle ?? this.hourMinuteTextStyle,
dayPeriodTextStyle: dayPeriodTextStyle ?? this.dayPeriodTextStyle,
helpTextStyle: helpTextStyle ?? this.helpTextStyle,
shape: shape ?? this.shape,
hourMinuteShape: hourMinuteShape ?? this.hourMinuteShape,
dayPeriodShape: dayPeriodShape ?? this.dayPeriodShape,
dayPeriodBorderSide: dayPeriodBorderSide ?? this.dayPeriodBorderSide,
inputDecorationTheme: inputDecorationTheme ?? this.inputDecorationTheme,
);
}
/// Linearly interpolate between two time picker themes.
///
/// The argument `t` must not be null.
///
/// {@macro dart.ui.shadow.lerp}
static TimePickerThemeData lerp(TimePickerThemeData? a, TimePickerThemeData? b, double t) {
assert(t != null);
// Workaround since BorderSide's lerp does not allow for null arguments.
BorderSide? lerpedBorderSide;
if (a?.dayPeriodBorderSide == null && b?.dayPeriodBorderSide == null) {
lerpedBorderSide = null;
} else if (a?.dayPeriodBorderSide == null) {
lerpedBorderSide = b?.dayPeriodBorderSide;
} else if (b?.dayPeriodBorderSide == null) {
lerpedBorderSide = a?.dayPeriodBorderSide;
} else {
lerpedBorderSide = BorderSide.lerp(a!.dayPeriodBorderSide!, b!.dayPeriodBorderSide!, t);
}
return TimePickerThemeData(
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
hourMinuteTextColor: Color.lerp(a?.hourMinuteTextColor, b?.hourMinuteTextColor, t),
hourMinuteColor: Color.lerp(a?.hourMinuteColor, b?.hourMinuteColor, t),
dayPeriodTextColor: Color.lerp(a?.dayPeriodTextColor, b?.dayPeriodTextColor, t),
dayPeriodColor: Color.lerp(a?.dayPeriodColor, b?.dayPeriodColor, t),
dialHandColor: Color.lerp(a?.dialHandColor, b?.dialHandColor, t),
dialBackgroundColor: Color.lerp(a?.dialBackgroundColor, b?.dialBackgroundColor, t),
dialTextColor: Color.lerp(a?.dialTextColor, b?.dialTextColor, t),
entryModeIconColor: Color.lerp(a?.entryModeIconColor, b?.entryModeIconColor, t),
hourMinuteTextStyle: TextStyle.lerp(a?.hourMinuteTextStyle, b?.hourMinuteTextStyle, t),
dayPeriodTextStyle: TextStyle.lerp(a?.dayPeriodTextStyle, b?.dayPeriodTextStyle, t),
helpTextStyle: TextStyle.lerp(a?.helpTextStyle, b?.helpTextStyle, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
hourMinuteShape: ShapeBorder.lerp(a?.hourMinuteShape, b?.hourMinuteShape, t),
dayPeriodShape: ShapeBorder.lerp(a?.dayPeriodShape, b?.dayPeriodShape, t) as OutlinedBorder?,
dayPeriodBorderSide: lerpedBorderSide,
inputDecorationTheme: t < 0.5 ? a?.inputDecorationTheme : b?.inputDecorationTheme,
);
}
@override
int get hashCode {
return hashValues(
backgroundColor,
hourMinuteTextColor,
hourMinuteColor,
dayPeriodTextColor,
dayPeriodColor,
dialHandColor,
dialBackgroundColor,
dialTextColor,
entryModeIconColor,
hourMinuteTextStyle,
dayPeriodTextStyle,
helpTextStyle,
shape,
hourMinuteShape,
dayPeriodShape,
dayPeriodBorderSide,
inputDecorationTheme,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
return other is TimePickerThemeData
&& other.backgroundColor == backgroundColor
&& other.hourMinuteTextColor == hourMinuteTextColor
&& other.hourMinuteColor == hourMinuteColor
&& other.dayPeriodTextColor == dayPeriodTextColor
&& other.dayPeriodColor == dayPeriodColor
&& other.dialHandColor == dialHandColor
&& other.dialBackgroundColor == dialBackgroundColor
&& other.dialTextColor == dialTextColor
&& other.entryModeIconColor == entryModeIconColor
&& other.hourMinuteTextStyle == hourMinuteTextStyle
&& other.dayPeriodTextStyle == dayPeriodTextStyle
&& other.helpTextStyle == helpTextStyle
&& other.shape == shape
&& other.hourMinuteShape == hourMinuteShape
&& other.dayPeriodShape == dayPeriodShape
&& other.dayPeriodBorderSide == dayPeriodBorderSide
&& other.inputDecorationTheme == inputDecorationTheme;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
properties.add(ColorProperty('hourMinuteTextColor', hourMinuteTextColor, defaultValue: null));
properties.add(ColorProperty('hourMinuteColor', hourMinuteColor, defaultValue: null));
properties.add(ColorProperty('dayPeriodTextColor', dayPeriodTextColor, defaultValue: null));
properties.add(ColorProperty('dayPeriodColor', dayPeriodColor, defaultValue: null));
properties.add(ColorProperty('dialHandColor', dialHandColor, defaultValue: null));
properties.add(ColorProperty('dialBackgroundColor', dialBackgroundColor, defaultValue: null));
properties.add(ColorProperty('dialTextColor', dialTextColor, defaultValue: null));
properties.add(ColorProperty('entryModeIconColor', entryModeIconColor, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle>('hourMinuteTextStyle', hourMinuteTextStyle, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle>('dayPeriodTextStyle', dayPeriodTextStyle, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle>('helpTextStyle', helpTextStyle, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('hourMinuteShape', hourMinuteShape, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('dayPeriodShape', dayPeriodShape, defaultValue: null));
properties.add(DiagnosticsProperty<BorderSide>('dayPeriodBorderSide', dayPeriodBorderSide, defaultValue: null));
properties.add(DiagnosticsProperty<InputDecorationTheme>('inputDecorationTheme', inputDecorationTheme, defaultValue: null));
}
}
/// An inherited widget that defines the configuration for time pickers
/// displayed using [showTimePicker] in this widget's subtree.
///
/// Values specified here are used for time picker properties that are not
/// given an explicit non-null value.
class TimePickerTheme extends InheritedTheme {
/// Creates a time picker theme that controls the configurations for
/// time pickers displayed in its widget subtree.
const TimePickerTheme({
Key? key,
required this.data,
required Widget child,
}) : assert(data != null),
super(key: key, child: child);
/// The properties for descendant time picker widgets.
final TimePickerThemeData data;
/// The [data] value of the closest [TimePickerTheme] ancestor.
///
/// If there is no ancestor, it returns [ThemeData.timePickerTheme].
/// Applications can assume that the returned value will not be null.
///
/// Typical usage is as follows:
///
/// ```dart
/// TimePickerThemeData theme = TimePickerTheme.of(context);
/// ```
static TimePickerThemeData of(BuildContext context) {
final TimePickerTheme? timePickerTheme = context.dependOnInheritedWidgetOfExactType<TimePickerTheme>();
return timePickerTheme?.data ?? Theme.of(context).timePickerTheme;
}
@override
Widget wrap(BuildContext context, Widget child) {
return TimePickerTheme(data: data, child: child);
}
@override
bool updateShouldNotify(TimePickerTheme oldWidget) => data != oldWidget.data;
}