| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- import 'dart:async';
- import 'package:flutter/material.dart';
- import 'package:intl/intl.dart';
- import '../../../core/theme/app_colors.dart';
- /// Large, quiet clock. It sleeps until the next minute instead of ticking
- /// every second.
- class ClockBlock extends StatefulWidget {
- const ClockBlock({
- super.key,
- required this.compact,
- this.temperatureText,
- this.temperatureAlert = false,
- });
- final bool compact;
- final String? temperatureText;
- final bool temperatureAlert;
- @override
- State<ClockBlock> createState() => _ClockBlockState();
- }
- class _ClockBlockState extends State<ClockBlock> {
- late DateTime _now = DateTime.now();
- Timer? _timer;
- @override
- void initState() {
- super.initState();
- _scheduleTick();
- }
- void _scheduleTick() {
- _timer?.cancel();
- final now = DateTime.now();
- final next = DateTime(
- now.year,
- now.month,
- now.day,
- now.hour,
- now.minute,
- ).add(const Duration(minutes: 1));
- _timer = Timer(next.difference(now) + const Duration(milliseconds: 80), () {
- if (!mounted) return;
- setState(() => _now = DateTime.now());
- _scheduleTick();
- });
- }
- @override
- void dispose() {
- _timer?.cancel();
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final colors = context.colors;
- final locale = Localizations.localeOf(context).toLanguageTag();
- final use24Hour = MediaQuery.alwaysUse24HourFormatOf(context);
- final time = (use24Hour ? DateFormat.Hm(locale) : DateFormat.jm(locale))
- .format(_now);
- final date = DateFormat('EEEE, d MMMM', locale).format(_now);
- return Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- time,
- maxLines: 1,
- style: theme.textTheme.displayLarge?.copyWith(
- fontSize: widget.compact ? 56 : 72,
- fontFeatures: const [FontFeature.tabularFigures()],
- height: 0.95,
- ),
- ),
- const SizedBox(height: 10),
- Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Container(
- width: 6,
- height: 6,
- decoration: BoxDecoration(
- color: colors.accent,
- shape: BoxShape.circle,
- ),
- ),
- const SizedBox(width: 10),
- Text(
- date,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- style: theme.textTheme.titleMedium?.copyWith(
- color: colors.inkMuted,
- fontWeight: FontWeight.w500,
- fontSize: widget.compact ? 14 : 16,
- ),
- ),
- ],
- ),
- if (widget.temperatureText != null) ...[
- const SizedBox(height: 7),
- AnimatedSwitcher(
- duration: const Duration(milliseconds: 220),
- child: Text(
- widget.temperatureText!,
- key: ValueKey(widget.temperatureText),
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- style: theme.textTheme.titleSmall?.copyWith(
- color: widget.temperatureAlert ? colors.danger : colors.ink,
- fontWeight: FontWeight.w600,
- fontSize: widget.compact ? 13 : 14,
- ),
- ),
- ),
- ],
- ],
- );
- }
- }
|