Skip to content

Commit 22924d1

Browse files
authored
Merge pull request #900 from wger-project/fix/video-error-handling
Improve video error handling
2 parents e896a2e + 1dc3776 commit 22924d1

File tree

3 files changed

+94
-18
lines changed

3 files changed

+94
-18
lines changed

lib/helpers/errors.dart

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ void showHttpExceptionErrorDialog(WgerHttpException exception, {BuildContext? co
4949
return;
5050
}
5151

52-
final errorList = formatErrors(extractErrors(exception.errors));
52+
final errorList = formatApiErrors(extractErrors(exception.errors));
5353

5454
showDialog(
5555
context: dialogContext,
@@ -104,6 +104,15 @@ void showGeneralErrorDialog(dynamic error, StackTrace? stackTrace, {BuildContext
104104
} else if (error is SocketException) {
105105
isNetworkError = true;
106106
}
107+
/*
108+
else if (error is PlatformException) {
109+
errorTitle = 'Problem with media';
110+
errorMessage =
111+
'There was a problem loading the media. This can be a e.g. problem with the codec that'
112+
'is not supported by your device. Original error message: ${error.message}';
113+
}
114+
115+
*/
107116

108117
final String fullStackTrace = stackTrace?.toString() ?? 'No stack trace available.';
109118

@@ -115,13 +124,13 @@ void showGeneralErrorDialog(dynamic error, StackTrace? stackTrace, {BuildContext
115124
builder: (BuildContext context) {
116125
return AlertDialog(
117126
title: Row(
127+
spacing: 8,
118128
mainAxisAlignment: MainAxisAlignment.center,
119129
children: [
120130
Icon(
121131
isNetworkError ? Icons.signal_wifi_connected_no_internet_4_outlined : Icons.error,
122132
color: Theme.of(context).colorScheme.error,
123133
),
124-
const SizedBox(width: 8),
125134
Expanded(
126135
child: Text(
127136
isNetworkError ? i18n.errorCouldNotConnectToServer : i18n.anErrorOccurred,
@@ -309,7 +318,7 @@ List<ApiError> extractErrors(Map<String, dynamic> errors) {
309318
}
310319

311320
/// Processes the error messages from the server and returns a list of widgets
312-
List<Widget> formatErrors(List<ApiError> errors, {Color? color}) {
321+
List<Widget> formatApiErrors(List<ApiError> errors, {Color? color}) {
313322
final textColor = color ?? Colors.black;
314323

315324
final List<Widget> errorList = [];
@@ -328,6 +337,26 @@ List<Widget> formatErrors(List<ApiError> errors, {Color? color}) {
328337
return errorList;
329338
}
330339

340+
/// Processes the error messages from the server and returns a list of widgets
341+
List<Widget> formatTextErrors(List<String> errors, {String? title, Color? color}) {
342+
final textColor = color ?? Colors.black;
343+
344+
final List<Widget> errorList = [];
345+
346+
if (title != null) {
347+
errorList.add(
348+
Text(title, style: TextStyle(fontWeight: FontWeight.bold, color: textColor)),
349+
);
350+
}
351+
352+
for (final message in errors) {
353+
errorList.add(Text(message, style: TextStyle(color: textColor)));
354+
}
355+
errorList.add(const SizedBox(height: 8));
356+
357+
return errorList;
358+
}
359+
331360
class FormHttpErrorsWidget extends StatelessWidget {
332361
final WgerHttpException exception;
333362

@@ -338,11 +367,32 @@ class FormHttpErrorsWidget extends StatelessWidget {
338367
return Column(
339368
children: [
340369
Icon(Icons.error_outline, color: Theme.of(context).colorScheme.error),
341-
...formatErrors(
370+
...formatApiErrors(
342371
extractErrors(exception.errors),
343372
color: Theme.of(context).colorScheme.error,
344373
),
345374
],
346375
);
347376
}
348377
}
378+
379+
class GeneralErrorsWidget extends StatelessWidget {
380+
final String? title;
381+
final List<String> widgets;
382+
383+
const GeneralErrorsWidget(this.widgets, {this.title, super.key});
384+
385+
@override
386+
Widget build(BuildContext context) {
387+
return Column(
388+
children: [
389+
Icon(Icons.error_outline, color: Theme.of(context).colorScheme.error),
390+
...formatTextErrors(
391+
widgets,
392+
title: title,
393+
color: Theme.of(context).colorScheme.error,
394+
),
395+
],
396+
);
397+
}
398+
}

lib/models/exercises/video.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class Video {
3232
@JsonKey(name: 'video', required: true)
3333
final String url;
3434

35+
Uri get uri => Uri.parse(url);
36+
3537
@JsonKey(name: 'exercise', required: true)
3638
final int exerciseId;
3739

lib/widgets/exercises/videos.dart

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
*/
1818

1919
import 'package:flutter/material.dart';
20+
import 'package:flutter/services.dart';
21+
import 'package:logging/logging.dart';
2022
import 'package:video_player/video_player.dart';
23+
import 'package:wger/helpers/errors.dart';
2124
import 'package:wger/models/exercises/video.dart';
2225

2326
class ExerciseVideoWidget extends StatefulWidget {
@@ -31,35 +34,56 @@ class ExerciseVideoWidget extends StatefulWidget {
3134

3235
class _ExerciseVideoWidgetState extends State<ExerciseVideoWidget> {
3336
late VideoPlayerController _controller;
37+
bool hasError = false;
38+
final logger = Logger('ExerciseVideoWidgetState');
3439

3540
@override
3641
void initState() {
3742
super.initState();
38-
_controller = VideoPlayerController.network(widget.video.url);
39-
_controller.addListener(() {
43+
_controller = VideoPlayerController.networkUrl(widget.video.uri);
44+
_initializeVideo();
45+
}
46+
47+
Future<void> _initializeVideo() async {
48+
try {
49+
await _controller.initialize();
4050
setState(() {});
41-
});
42-
_controller.initialize().then((_) => setState(() {}));
51+
} on PlatformException catch (e) {
52+
if (mounted) {
53+
setState(() => hasError = true);
54+
}
55+
56+
logger.warning('PlatformException while initializing video: ${e.message}');
57+
}
4358
}
4459

4560
@override
4661
void dispose() {
47-
super.dispose();
4862
_controller.dispose();
63+
super.dispose();
4964
}
5065

5166
@override
5267
Widget build(BuildContext context) {
53-
return _controller.value.isInitialized
54-
? AspectRatio(
55-
aspectRatio: _controller.value.aspectRatio,
56-
child: Stack(alignment: Alignment.bottomCenter, children: [
57-
VideoPlayer(_controller),
58-
_ControlsOverlay(controller: _controller),
59-
VideoProgressIndicator(_controller, allowScrubbing: true),
60-
]),
68+
return hasError
69+
? const GeneralErrorsWidget(
70+
[
71+
'An error happened while loading the video. If you can, please check the application logs.'
72+
],
6173
)
62-
: Container();
74+
: _controller.value.isInitialized
75+
? AspectRatio(
76+
aspectRatio: _controller.value.aspectRatio,
77+
child: Stack(
78+
alignment: Alignment.bottomCenter,
79+
children: [
80+
VideoPlayer(_controller),
81+
_ControlsOverlay(controller: _controller),
82+
VideoProgressIndicator(_controller, allowScrubbing: true),
83+
],
84+
),
85+
)
86+
: Container();
6387
}
6488
}
6589

0 commit comments

Comments
 (0)