Files
buzz/mobile/lib/features/channels/day_divider.dart
klopez4212 417eea2230 Polish mobile timeline navigation (#5874)
## Summary
- add sticky date headers that crossfade with in-timeline dates
- replace the Latest pill with a centered down-arrow control
- animate the control out from its bottom-center anchor
<img width="630" height="1368"
alt="2AF1BB3F-B295-4084-8A65-90AED60B58B5"
src="https://github.com/user-attachments/assets/505de450-66f5-4628-8c59-34b5511ffb5c"
/>

## Validation
- `bin/just mobile-check`
- `bin/just mobile-test` (1,359 tests)
- Android debug and signed iOS release builds

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Kenny Lopez <klopez4212@gmail.com>
Co-authored-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz>
2026-08-18 18:56:58 +01:00

62 lines
1.8 KiB
Dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../shared/theme/theme.dart';
/// In-flow date label. The active date gains a glass capsule when it sticks.
class DayDivider extends StatelessWidget {
final String label;
final int? dayTimestamp;
final ValueListenable<int?>? stickyDayTimestamp;
const DayDivider({
super.key,
required this.label,
this.dayTimestamp,
this.stickyDayTimestamp,
});
Widget _buildOpacity(BuildContext context, {required bool isSticky}) {
return ExcludeSemantics(
excluding: isSticky,
child: AnimatedOpacity(
key: dayTimestamp == null
? null
: ValueKey('channel-day-divider-opacity-$dayTimestamp'),
duration: MediaQuery.disableAnimationsOf(context)
? Duration.zero
: const Duration(milliseconds: 120),
curve: Curves.easeOutCubic,
opacity: isSticky ? 0 : 1,
child: Text(
label,
style: context.textTheme.labelSmall?.copyWith(
color: context.colors.onSurfaceVariant.withValues(alpha: 0.72),
fontWeight: FontWeight.w500,
),
),
),
);
}
@override
Widget build(BuildContext context) {
final activeTimestamp = stickyDayTimestamp;
final timestamp = dayTimestamp;
return Padding(
padding: const EdgeInsets.symmetric(vertical: Grid.xxs + Grid.quarter),
child: Center(
child: activeTimestamp == null || timestamp == null
? _buildOpacity(context, isSticky: false)
: ValueListenableBuilder<int?>(
valueListenable: activeTimestamp,
builder: (context, activeDayTimestamp, _) => _buildOpacity(
context,
isSticky: activeDayTimestamp == timestamp,
),
),
),
);
}
}