Refine iOS navigation, media controls, and background sync
Build APK / build (push) Successful in 5m50s
Build APK / build (push) Successful in 5m50s
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
@@ -198,12 +199,14 @@ class VideoTransportControls extends StatelessWidget {
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 12, 8),
|
||||
child: WavySeekBar(
|
||||
controller: controller,
|
||||
playedColor: fg,
|
||||
trackColor: fg.withValues(alpha: 0.3),
|
||||
style: progressBarStyle,
|
||||
),
|
||||
child: Theme.of(context).platform == TargetPlatform.iOS
|
||||
? IosSeekBar(controller: controller, color: fg)
|
||||
: WavySeekBar(
|
||||
controller: controller,
|
||||
playedColor: fg,
|
||||
trackColor: fg.withValues(alpha: 0.3),
|
||||
style: progressBarStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -213,6 +216,60 @@ class VideoTransportControls extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class IosSeekBar extends StatefulWidget {
|
||||
final VideoPlayerController controller;
|
||||
final Color color;
|
||||
const IosSeekBar({super.key, required this.controller, required this.color});
|
||||
|
||||
@override
|
||||
State<IosSeekBar> createState() => _IosSeekBarState();
|
||||
}
|
||||
|
||||
class _IosSeekBarState extends State<IosSeekBar> {
|
||||
double? _dragRatio;
|
||||
bool _resumeAfterScrubbing = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final value = widget.controller.value;
|
||||
final durationMs = value.duration.inMilliseconds;
|
||||
final ratio =
|
||||
(_dragRatio ??
|
||||
(durationMs > 0
|
||||
? value.position.inMilliseconds / durationMs
|
||||
: 0.0))
|
||||
.clamp(0.0, 1.0);
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: CupertinoSlider(
|
||||
value: ratio,
|
||||
activeColor: widget.color,
|
||||
thumbColor: CupertinoColors.white,
|
||||
onChangeStart: durationMs <= 0
|
||||
? null
|
||||
: (_) {
|
||||
_resumeAfterScrubbing = value.isPlaying;
|
||||
if (_resumeAfterScrubbing) widget.controller.pause();
|
||||
},
|
||||
onChanged: durationMs <= 0
|
||||
? null
|
||||
: (ratio) {
|
||||
setState(() => _dragRatio = ratio);
|
||||
widget.controller.seekTo(value.duration * ratio);
|
||||
},
|
||||
onChangeEnd: durationMs <= 0
|
||||
? null
|
||||
: (ratio) async {
|
||||
await widget.controller.seekTo(value.duration * ratio);
|
||||
if (!mounted) return;
|
||||
setState(() => _dragRatio = null);
|
||||
if (_resumeAfterScrubbing) widget.controller.play();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A Material You "expressive" wavy seek bar: the played portion of the
|
||||
/// track animates as a travelling sine wave while playing and settles flat
|
||||
/// when paused, matching the Android 16+ media player style. Drag or tap
|
||||
|
||||
Reference in New Issue
Block a user