mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

Fix issue reference for https://github.com/flutter/flutter/issues/131247 (it was added in the bug fix PR https://github.com/flutter/flutter/pull/131253)
339 lines
12 KiB
Dart
339 lines
12 KiB
Dart
// 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 'template.dart';
|
|
|
|
class TimePickerTemplate extends TokenTemplate {
|
|
const TimePickerTemplate(super.blockName, super.fileName, super.tokens, {
|
|
super.colorSchemePrefix = '_colors.',
|
|
super.textThemePrefix = '_textTheme.'
|
|
});
|
|
|
|
static const String tokenGroup = 'md.comp.time-picker';
|
|
static const String hourMinuteComponent = '$tokenGroup.time-selector';
|
|
static const String dayPeriodComponent = '$tokenGroup.period-selector';
|
|
static const String dialComponent = '$tokenGroup.clock-dial';
|
|
static const String variant = '';
|
|
|
|
@override
|
|
String generate() => '''
|
|
class _${blockName}DefaultsM3 extends _TimePickerDefaults {
|
|
_${blockName}DefaultsM3(this.context);
|
|
|
|
final BuildContext context;
|
|
|
|
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
|
late final TextTheme _textTheme = Theme.of(context).textTheme;
|
|
|
|
@override
|
|
Color get backgroundColor {
|
|
return ${componentColor("$tokenGroup.container")};
|
|
}
|
|
|
|
@override
|
|
ButtonStyle get cancelButtonStyle {
|
|
return TextButton.styleFrom();
|
|
}
|
|
|
|
@override
|
|
ButtonStyle get confirmButtonStyle {
|
|
return TextButton.styleFrom();
|
|
}
|
|
|
|
@override
|
|
BorderSide get dayPeriodBorderSide {
|
|
return ${border('$dayPeriodComponent.outline')};
|
|
}
|
|
|
|
@override
|
|
Color get dayPeriodColor {
|
|
return MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return ${componentColor("$dayPeriodComponent.selected.container")};
|
|
}
|
|
// The unselected day period should match the overall picker dialog color.
|
|
// Making it transparent enables that without being redundant and allows
|
|
// the optional elevation overlay for dark mode to be visible.
|
|
return Colors.transparent;
|
|
});
|
|
}
|
|
|
|
@override
|
|
OutlinedBorder get dayPeriodShape {
|
|
return ${shape("$dayPeriodComponent.container")}.copyWith(side: dayPeriodBorderSide);
|
|
}
|
|
|
|
@override
|
|
Size get dayPeriodPortraitSize {
|
|
return ${size('$dayPeriodComponent.vertical.container')};
|
|
}
|
|
|
|
@override
|
|
Size get dayPeriodLandscapeSize {
|
|
return ${size('$dayPeriodComponent.horizontal.container')};
|
|
}
|
|
|
|
@override
|
|
Size get dayPeriodInputSize {
|
|
// Input size is eight pixels smaller than the portrait size in the spec,
|
|
// but there's not token for it yet.
|
|
return Size(dayPeriodPortraitSize.width, dayPeriodPortraitSize.height - 8);
|
|
}
|
|
|
|
@override
|
|
Color get dayPeriodTextColor {
|
|
return MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
if (states.contains(MaterialState.focused)) {
|
|
return ${componentColor("$dayPeriodComponent.selected.focus.label-text")};
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return ${componentColor("$dayPeriodComponent.selected.hover.label-text")};
|
|
}
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return ${componentColor("$dayPeriodComponent.selected.pressed.label-text")};
|
|
}
|
|
return ${componentColor("$dayPeriodComponent.selected.label-text")};
|
|
}
|
|
if (states.contains(MaterialState.focused)) {
|
|
return ${componentColor("$dayPeriodComponent.unselected.focus.label-text")};
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return ${componentColor("$dayPeriodComponent.unselected.hover.label-text")};
|
|
}
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return ${componentColor("$dayPeriodComponent.unselected.pressed.label-text")};
|
|
}
|
|
return ${componentColor("$dayPeriodComponent.unselected.label-text")};
|
|
});
|
|
}
|
|
|
|
@override
|
|
TextStyle get dayPeriodTextStyle {
|
|
return ${textStyle("$dayPeriodComponent.label-text")}!.copyWith(color: dayPeriodTextColor);
|
|
}
|
|
|
|
@override
|
|
Color get dialBackgroundColor {
|
|
return ${componentColor(dialComponent)};
|
|
}
|
|
|
|
@override
|
|
Color get dialHandColor {
|
|
return ${componentColor('$dialComponent.selector.handle.container')};
|
|
}
|
|
|
|
@override
|
|
Size get dialSize {
|
|
return ${size("$dialComponent.container")};
|
|
}
|
|
|
|
@override
|
|
double get handWidth {
|
|
return ${size("$dialComponent.selector.track.container")}.width;
|
|
}
|
|
|
|
@override
|
|
double get dotRadius {
|
|
return ${size("$dialComponent.selector.handle.container")}.width / 2;
|
|
}
|
|
|
|
@override
|
|
double get centerRadius {
|
|
return ${size("$dialComponent.selector.center.container")}.width / 2;
|
|
}
|
|
|
|
@override
|
|
Color get dialTextColor {
|
|
return MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return ${componentColor('$dialComponent.selected.label-text')};
|
|
}
|
|
return ${componentColor('$dialComponent.unselected.label-text')};
|
|
});
|
|
}
|
|
|
|
@override
|
|
TextStyle get dialTextStyle {
|
|
return ${textStyle('$dialComponent.label-text')}!;
|
|
}
|
|
|
|
@override
|
|
double get elevation {
|
|
return ${elevation("$tokenGroup.container")};
|
|
}
|
|
|
|
@override
|
|
Color get entryModeIconColor {
|
|
return _colors.onSurface;
|
|
}
|
|
|
|
@override
|
|
TextStyle get helpTextStyle {
|
|
return MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
|
|
final TextStyle textStyle = ${textStyle('$tokenGroup.headline')}!;
|
|
return textStyle.copyWith(color: ${componentColor('$tokenGroup.headline')});
|
|
});
|
|
}
|
|
|
|
@override
|
|
EdgeInsetsGeometry get padding {
|
|
return const EdgeInsets.all(24);
|
|
}
|
|
|
|
@override
|
|
Color get hourMinuteColor {
|
|
return MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
Color overlayColor = ${componentColor('$hourMinuteComponent.selected.container')};
|
|
if (states.contains(MaterialState.pressed)) {
|
|
overlayColor = ${componentColor('$hourMinuteComponent.selected.pressed.state-layer')};
|
|
} else if (states.contains(MaterialState.hovered)) {
|
|
const double hoverOpacity = ${opacity('$hourMinuteComponent.hover.state-layer.opacity')};
|
|
overlayColor = ${componentColor('$hourMinuteComponent.selected.hover.state-layer')}.withOpacity(hoverOpacity);
|
|
} else if (states.contains(MaterialState.focused)) {
|
|
const double focusOpacity = ${opacity('$hourMinuteComponent.focus.state-layer.opacity')};
|
|
overlayColor = ${componentColor('$hourMinuteComponent.selected.focus.state-layer')}.withOpacity(focusOpacity);
|
|
}
|
|
return Color.alphaBlend(overlayColor, ${componentColor('$hourMinuteComponent.selected.container')});
|
|
} else {
|
|
Color overlayColor = ${componentColor('$hourMinuteComponent.unselected.container')};
|
|
if (states.contains(MaterialState.pressed)) {
|
|
overlayColor = ${componentColor('$hourMinuteComponent.unselected.pressed.state-layer')};
|
|
} else if (states.contains(MaterialState.hovered)) {
|
|
const double hoverOpacity = ${opacity('$hourMinuteComponent.hover.state-layer.opacity')};
|
|
overlayColor = ${componentColor('$hourMinuteComponent.unselected.hover.state-layer')}.withOpacity(hoverOpacity);
|
|
} else if (states.contains(MaterialState.focused)) {
|
|
const double focusOpacity = ${opacity('$hourMinuteComponent.focus.state-layer.opacity')};
|
|
overlayColor = ${componentColor('$hourMinuteComponent.unselected.focus.state-layer')}.withOpacity(focusOpacity);
|
|
}
|
|
return Color.alphaBlend(overlayColor, ${componentColor('$hourMinuteComponent.unselected.container')});
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
ShapeBorder get hourMinuteShape {
|
|
return ${shape('$hourMinuteComponent.container')};
|
|
}
|
|
|
|
@override
|
|
Size get hourMinuteSize {
|
|
return ${size('$hourMinuteComponent.container')};
|
|
}
|
|
|
|
@override
|
|
Size get hourMinuteSize24Hour {
|
|
return Size(${size('$hourMinuteComponent.24h-vertical.container')}.width, hourMinuteSize.height);
|
|
}
|
|
|
|
@override
|
|
Size get hourMinuteInputSize {
|
|
// Input size is eight pixels smaller than the regular size in the spec, but
|
|
// there's not token for it yet.
|
|
return Size(hourMinuteSize.width, hourMinuteSize.height - 8);
|
|
}
|
|
|
|
@override
|
|
Size get hourMinuteInputSize24Hour {
|
|
// Input size is eight pixels smaller than the regular size in the spec, but
|
|
// there's not token for it yet.
|
|
return Size(hourMinuteSize24Hour.width, hourMinuteSize24Hour.height - 8);
|
|
}
|
|
|
|
@override
|
|
Color get hourMinuteTextColor {
|
|
return MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
|
return _hourMinuteTextColor.resolve(states);
|
|
});
|
|
}
|
|
|
|
MaterialStateProperty<Color> get _hourMinuteTextColor {
|
|
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return ${componentColor("$hourMinuteComponent.selected.pressed.label-text")};
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return ${componentColor("$hourMinuteComponent.selected.hover.label-text")};
|
|
}
|
|
if (states.contains(MaterialState.focused)) {
|
|
return ${componentColor("$hourMinuteComponent.selected.focus.label-text")};
|
|
}
|
|
return ${componentColor("$hourMinuteComponent.selected.label-text")};
|
|
} else {
|
|
// unselected
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return ${componentColor("$hourMinuteComponent.unselected.pressed.label-text")};
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return ${componentColor("$hourMinuteComponent.unselected.hover.label-text")};
|
|
}
|
|
if (states.contains(MaterialState.focused)) {
|
|
return ${componentColor("$hourMinuteComponent.unselected.focus.label-text")};
|
|
}
|
|
return ${componentColor("$hourMinuteComponent.unselected.label-text")};
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
TextStyle get hourMinuteTextStyle {
|
|
return MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
|
|
// TODO(tahatesser): Update this when https://github.com/flutter/flutter/issues/131247 is fixed.
|
|
// This is using the correct text style from Material 3 spec.
|
|
// https://m3.material.io/components/time-pickers/specs#fd0b6939-edab-4058-82e1-93d163945215
|
|
return _textTheme.displayMedium!.copyWith(color: _hourMinuteTextColor.resolve(states));
|
|
});
|
|
}
|
|
|
|
@override
|
|
InputDecorationTheme get inputDecorationTheme {
|
|
// This is NOT correct, but there's no token for
|
|
// 'time-input.container.shape', so this is using the radius from the shape
|
|
// for the hour/minute selector. It's a BorderRadiusGeometry, so we have to
|
|
// resolve it before we can use it.
|
|
final BorderRadius selectorRadius = ${shape('$hourMinuteComponent.container')}
|
|
.borderRadius
|
|
.resolve(Directionality.of(context));
|
|
return InputDecorationTheme(
|
|
contentPadding: EdgeInsets.zero,
|
|
filled: true,
|
|
// This should be derived from a token, but there isn't one for 'time-input'.
|
|
fillColor: hourMinuteColor,
|
|
// This should be derived from a token, but there isn't one for 'time-input'.
|
|
focusColor: _colors.primaryContainer,
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: selectorRadius,
|
|
borderSide: const BorderSide(color: Colors.transparent),
|
|
),
|
|
errorBorder: OutlineInputBorder(
|
|
borderRadius: selectorRadius,
|
|
borderSide: BorderSide(color: _colors.error, width: 2),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: selectorRadius,
|
|
borderSide: BorderSide(color: _colors.primary, width: 2),
|
|
),
|
|
focusedErrorBorder: OutlineInputBorder(
|
|
borderRadius: selectorRadius,
|
|
borderSide: BorderSide(color: _colors.error, width: 2),
|
|
),
|
|
hintStyle: hourMinuteTextStyle.copyWith(color: _colors.onSurface.withOpacity(0.36)),
|
|
// Prevent the error text from appearing.
|
|
// TODO(rami-a): Remove this workaround once
|
|
// https://github.com/flutter/flutter/issues/54104
|
|
// is fixed.
|
|
errorStyle: const TextStyle(fontSize: 0, height: 0),
|
|
);
|
|
}
|
|
|
|
@override
|
|
ShapeBorder get shape {
|
|
return ${shape("$tokenGroup.container")};
|
|
}
|
|
}
|
|
''';
|
|
}
|