offline updates and playhead fix
This commit is contained in:
parent
7a199fe4df
commit
6663330260
14 changed files with 1673 additions and 545 deletions
|
|
@ -7,6 +7,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||
|
||||
import '../state/providers.dart';
|
||||
import '../theme/tokens.dart';
|
||||
import 'art_image.dart';
|
||||
|
||||
/// Animated cassette for the Now Playing screen. Composites, in the shell's
|
||||
/// `469×298` coordinate space, from back to front:
|
||||
|
|
@ -44,13 +45,18 @@ class _CassetteViewState extends ConsumerState<CassetteView>
|
|||
}
|
||||
|
||||
void _onTick(Duration elapsed) {
|
||||
final dt = (elapsed - _last).inMicroseconds / Duration.microsecondsPerSecond;
|
||||
final dt =
|
||||
(elapsed - _last).inMicroseconds / Duration.microsecondsPerSecond;
|
||||
_last = elapsed;
|
||||
if (dt <= 0) return;
|
||||
// Read (not watch) inside the ticker: the model drives repaints itself, and
|
||||
// watching here would rebuild the whole widget every position tick.
|
||||
final s = ref.read(activePlaybackProvider);
|
||||
_model.update(dt: dt, playing: s.playing && s.supported, progress: s.progress);
|
||||
_model.update(
|
||||
dt: dt,
|
||||
playing: s.playing && s.supported,
|
||||
progress: s.progress,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -118,8 +124,7 @@ class _CassetteViewState extends ConsumerState<CassetteView>
|
|||
child: AnimatedBuilder(
|
||||
animation: _model,
|
||||
child: cog,
|
||||
builder: (_, child) =>
|
||||
Transform.rotate(angle: angle(), child: child),
|
||||
builder: (_, child) => Transform.rotate(angle: angle(), child: child),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -133,15 +138,12 @@ class _LabelArt extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (artUri == null) {
|
||||
return ColoredBox(color: TimbreColors.surface);
|
||||
}
|
||||
return Image.network(
|
||||
artUri!,
|
||||
key: ValueKey(artUri),
|
||||
fit: BoxFit.cover, // square art → wide label: crop the sides/top
|
||||
gaplessPlayback: true,
|
||||
errorBuilder: (_, _, _) => ColoredBox(color: TimbreColors.surface),
|
||||
// Square art → wide label: crop the sides/top. A plain surface fill backs
|
||||
// the null/error cases (no album glyph here — the shell frames the label).
|
||||
return ArtImage(
|
||||
artUri,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: ColoredBox(color: TimbreColors.surface),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -193,9 +195,15 @@ class _TapePainter extends CustomPainter {
|
|||
canvas.drawRect(cfg.windowRect, Paint()..color = cfg.padColor);
|
||||
final tape = Paint()..color = cfg.tapeColor;
|
||||
canvas.drawCircle(
|
||||
cfg.leftReel, cfg.radius(model.progress, supply: true), tape);
|
||||
cfg.leftReel,
|
||||
cfg.radius(model.progress, supply: true),
|
||||
tape,
|
||||
);
|
||||
canvas.drawCircle(
|
||||
cfg.rightReel, cfg.radius(model.progress, supply: false), tape);
|
||||
cfg.rightReel,
|
||||
cfg.radius(model.progress, supply: false),
|
||||
tape,
|
||||
);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +253,8 @@ class _CassetteConfig {
|
|||
/// reel is full at p=0 and empty at p=1; the take-up reel is the reverse.
|
||||
double radius(double progress, {required bool supply}) {
|
||||
final frac = (supply ? 1 - progress : progress).clamp(0.0, 1.0);
|
||||
final r2 = hubRadius * hubRadius +
|
||||
final r2 =
|
||||
hubRadius * hubRadius +
|
||||
(fullRadius * fullRadius - hubRadius * hubRadius) * frac;
|
||||
return math.sqrt(r2);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue