| // 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. |
| /// |
| /// If this color is null then the dial's text colors are based on the |
| /// theme's [ThemeData.colorScheme]. |
| 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; |
| } |