Update Saqel Platform: 2026-09-02 00:55:56
This commit is contained in:
+63
-20
@@ -10,6 +10,7 @@ import '../../../logic/cubits/auth_cubit.dart';
|
||||
import '../../../logic/cubits/video_playback_cubit.dart';
|
||||
import '../../widgets/luxury_widgets.dart';
|
||||
import '../../widgets/socratic_dialog.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
class SocraticVideoPlayerScreen extends StatefulWidget {
|
||||
final CurriculumLessonItemModel lesson;
|
||||
@@ -28,6 +29,8 @@ class SocraticVideoPlayerScreen extends StatefulWidget {
|
||||
class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> with SingleTickerProviderStateMixin {
|
||||
Timer? _playbackTicker;
|
||||
late AnimationController _watermarkController;
|
||||
VideoPlayerController? _videoController;
|
||||
bool _isVideoInitialized = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -40,14 +43,14 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
|
||||
duration: const Duration(seconds: 18),
|
||||
)..repeat(reverse: true);
|
||||
|
||||
// Ticker simulating video playback time progression and Socratic checkpoint gatekeeping
|
||||
// Read the real player clock and periodically persist progress.
|
||||
_playbackTicker = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
final cubit = context.read<VideoPlaybackCubit>();
|
||||
final state = cubit.state;
|
||||
if (state is VideoPlaybackReady && state.isPlaying && state.activeCheckpoint == null) {
|
||||
if (state.currentPositionSeconds < state.playbackData.durationSeconds) {
|
||||
cubit.updatePosition(state.currentPositionSeconds + 1);
|
||||
}
|
||||
if (state is VideoPlaybackReady && _videoController?.value.isInitialized == true) {
|
||||
final position = _videoController!.value.position.inSeconds;
|
||||
if (state.activeCheckpoint == null) cubit.updatePosition(position);
|
||||
cubit.saveProgress(positionSeconds: position, watchedSeconds: position);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -56,6 +59,7 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
|
||||
void dispose() {
|
||||
_playbackTicker?.cancel();
|
||||
_watermarkController.dispose();
|
||||
_videoController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -111,6 +115,31 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
|
||||
body: BlocConsumer<VideoPlaybackCubit, VideoPlaybackState>(
|
||||
listener: (context, state) {
|
||||
if (state is VideoPlaybackReady) {
|
||||
// Initialize Video Player if not yet initialized
|
||||
if (_videoController == null) {
|
||||
_videoController = VideoPlayerController.networkUrl(Uri.parse(state.playbackData.videoUrl))
|
||||
..initialize().then((_) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isVideoInitialized = true;
|
||||
});
|
||||
if (state.isPlaying && state.activeCheckpoint == null) {
|
||||
_videoController!.play();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (_isVideoInitialized) {
|
||||
// Sync play/pause state
|
||||
if (state.isPlaying && state.activeCheckpoint == null) {
|
||||
_videoController!.play();
|
||||
} else {
|
||||
_videoController!.pause();
|
||||
}
|
||||
if (state.currentPositionSeconds != _videoController!.value.position.inSeconds) {
|
||||
_videoController!.seekTo(Duration(seconds: state.currentPositionSeconds));
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger Socratic Checkpoint Modal automatically when active
|
||||
if (state.activeCheckpoint != null) {
|
||||
showDialog(
|
||||
@@ -187,20 +216,35 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Video Background Scene Simulation & Visualizer
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
center: Alignment(0.0, -0.3),
|
||||
radius: 1.3,
|
||||
colors: [
|
||||
Color(0xFF0F2342),
|
||||
Color(0xFF060B14),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Video Background Scene
|
||||
Stack(
|
||||
children: [
|
||||
if (_videoController != null && _isVideoInitialized)
|
||||
Positioned.fill(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
child: SizedBox(
|
||||
width: _videoController!.value.size.width,
|
||||
height: _videoController!.value.size.height,
|
||||
child: VideoPlayer(_videoController!),
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
center: Alignment(0.0, -0.3),
|
||||
radius: 1.3,
|
||||
colors: [
|
||||
Color(0xFF0F2342),
|
||||
Color(0xFF060B14),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Grid Overlay Effect
|
||||
Positioned.fill(
|
||||
child: Opacity(
|
||||
@@ -315,7 +359,6 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Dynamic Forensic Anti-Piracy Watermark (Moves continuously)
|
||||
AnimatedBuilder(
|
||||
|
||||
Reference in New Issue
Block a user