fixed timetable ruler and day header overflowing at large system text sizes

Both have fixed geometry, so their text scaling is capped at 1.3. Ruler
times shrink instead of wrapping into the period number and are only
shown when they fit at the current scale.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-28 12:51:28 +02:00
co-authored by Claude Opus 5.5
parent 38a651f916
commit b413fe05de
2 changed files with 59 additions and 33 deletions
@@ -95,20 +95,24 @@ class _PeriodRuler extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return SizedBox(
width: width,
child: Stack(
clipBehavior: Clip.none,
children: [
for (final period in schedule.periods)
Positioned(
top: layout.topOf(period),
height: layout.heightOf(period),
left: 0,
right: 0,
child: _PeriodLabel(period: period, theme: theme),
),
],
// Fixed ruler width and period heights: past this the labels overlap.
return MediaQuery.withClampedTextScaling(
maxScaleFactor: 1.3,
child: SizedBox(
width: width,
child: Stack(
clipBehavior: Clip.none,
children: [
for (final period in schedule.periods)
Positioned(
top: layout.topOf(period),
height: layout.heightOf(period),
left: 0,
right: 0,
child: _PeriodLabel(period: period, theme: theme),
),
],
),
),
);
}
@@ -155,7 +159,11 @@ class _PeriodLabel extends StatelessWidget {
return LayoutBuilder(
builder: (context, constraints) {
final showTimes = constraints.maxHeight >= 38;
// Both times plus the number, at the current text scale (9 + 9 + 14
// plus the 3px insets and some air — 38px at scale 1).
final scaler = MediaQuery.textScalerOf(context);
final needed = 2 * scaler.scale(9) + scaler.scale(14) + 6;
final showTimes = constraints.maxHeight >= needed;
return DecoratedBox(
decoration: BoxDecoration(
border: Border(top: BorderSide(color: dividerColor, width: 0.5)),
@@ -169,11 +177,10 @@ class _PeriodLabel extends StatelessWidget {
top: 3,
left: 0,
right: 0,
child: Text(
child: _timeText(
_format(period.start),
style: timeStyle,
textAlign: TextAlign.center,
textHeightBehavior: tightTextHeight,
timeStyle,
tightTextHeight,
),
),
Text(
@@ -191,11 +198,10 @@ class _PeriodLabel extends StatelessWidget {
bottom: 3,
left: 0,
right: 0,
child: Text(
child: _timeText(
_format(period.end),
style: timeStyle,
textAlign: TextAlign.center,
textHeightBehavior: tightTextHeight,
timeStyle,
tightTextHeight,
),
),
],
@@ -205,6 +211,24 @@ class _PeriodLabel extends StatelessWidget {
);
}
/// The ruler is narrow: a scaled-up time must shrink, never wrap into the
/// period number.
static Widget _timeText(
String text,
TextStyle? style,
TextHeightBehavior heightBehavior,
) => FittedBox(
fit: BoxFit.scaleDown,
child: Text(
text,
style: style,
maxLines: 1,
softWrap: false,
textAlign: TextAlign.center,
textHeightBehavior: heightBehavior,
),
);
static String _format(TimeOfDay t) =>
'${t.hour.toString().padLeft(2, '0')}:${t.minute.toString().padLeft(2, '0')}';
}