mirror of
https://github.com/foss42/apidash.git
synced 2025-08-06 13:51:20 +08:00
Merge pull request #315 from mdrazak2001/video-preview-hotfix
add video preview with updated logic
This commit is contained in:
@ -21,7 +21,7 @@ final kIsLinux = !kIsWeb && Platform.isLinux;
|
||||
final kIsApple = !kIsWeb && (Platform.isIOS || Platform.isMacOS);
|
||||
final kIsDesktop =
|
||||
!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux);
|
||||
|
||||
final kIsRunningTests = Platform.environment.containsKey('FLUTTER_TEST');
|
||||
final kIsIOS = !kIsWeb && Platform.isIOS;
|
||||
final kIsAndroid = !kIsWeb && Platform.isAndroid;
|
||||
final kIsMobile = !kIsWeb && (Platform.isIOS || Platform.isAndroid);
|
||||
@ -403,7 +403,7 @@ const Map<String, Map<String, List<ResponseBodyView>>>
|
||||
kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
|
||||
},
|
||||
kTypeVideo: {
|
||||
kSubTypeDefaultViewOptions: kNoBodyViewOptions,
|
||||
kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
|
||||
},
|
||||
kTypeText: {
|
||||
kSubTypeDefaultViewOptions: kRawBodyViewOptions,
|
||||
@ -512,6 +512,9 @@ const kMimeTypeRaiseIssue =
|
||||
const kUnexpectedRaiseIssue =
|
||||
"\nIf the behaviour is unexpected, please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
const kVideoError =
|
||||
"There seems to be an issue playing this video. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
const kImageError =
|
||||
"There seems to be an issue rendering this image. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
|
@ -8,6 +8,7 @@ import 'error_message.dart';
|
||||
import 'uint8_audio_player.dart';
|
||||
import 'json_previewer.dart';
|
||||
import 'csv_previewer.dart';
|
||||
import 'video_previewer.dart';
|
||||
import '../consts.dart';
|
||||
|
||||
class Previewer extends StatefulWidget {
|
||||
@ -86,7 +87,12 @@ class _PreviewerState extends State<Previewer> {
|
||||
return CsvPreviewer(body: widget.body);
|
||||
}
|
||||
if (widget.type == kTypeVideo) {
|
||||
// TODO: Video Player
|
||||
try {
|
||||
var preview = VideoPreviewer(videoBytes: widget.bytes);
|
||||
return preview;
|
||||
} catch (e) {
|
||||
return const ErrorMessage(message: kVideoError);
|
||||
}
|
||||
}
|
||||
String message = widget.hasRaw
|
||||
? "$kMimeTypeRawRaiseIssueStart${widget.type}/${widget.subtype}$kMimeTypeRaiseIssue"
|
||||
|
147
lib/widgets/video_previewer.dart
Normal file
147
lib/widgets/video_previewer.dart
Normal file
@ -0,0 +1,147 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:fvp/fvp.dart' as fvp;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class VideoPreviewer extends StatefulWidget {
|
||||
const VideoPreviewer({
|
||||
super.key,
|
||||
required this.videoBytes,
|
||||
});
|
||||
|
||||
final Uint8List videoBytes;
|
||||
|
||||
@override
|
||||
State<VideoPreviewer> createState() => _VideoPreviewerState();
|
||||
}
|
||||
|
||||
class _VideoPreviewerState extends State<VideoPreviewer> {
|
||||
VideoPlayerController? _videoController;
|
||||
bool _isPlaying = false;
|
||||
File? _tempVideoFile;
|
||||
bool _showControls = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
registerWithAllPlatforms();
|
||||
_initializeVideoPlayer();
|
||||
}
|
||||
|
||||
void registerWithAllPlatforms() {
|
||||
try {
|
||||
fvp.registerWith();
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
void _initializeVideoPlayer() async {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
_tempVideoFile = File(
|
||||
'${tempDir.path}/temp_video_${DateTime.now().millisecondsSinceEpoch}');
|
||||
try {
|
||||
await _tempVideoFile?.writeAsBytes(widget.videoBytes);
|
||||
_videoController = VideoPlayerController.file(_tempVideoFile!)
|
||||
..initialize().then((_) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_videoController!.play();
|
||||
_videoController!.setLooping(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconColor = Theme.of(context).iconTheme.color;
|
||||
final progressBarColors = VideoProgressColors(
|
||||
playedColor: iconColor!,
|
||||
bufferedColor: iconColor.withOpacity(0.5),
|
||||
backgroundColor: iconColor.withOpacity(0.3),
|
||||
);
|
||||
return Scaffold(
|
||||
body: MouseRegion(
|
||||
onEnter: (_) => setState(() => _showControls = true),
|
||||
onExit: (_) => setState(() => _showControls = false),
|
||||
child: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: _videoController?.value.isInitialized == true
|
||||
? AspectRatio(
|
||||
aspectRatio: _videoController!.value.aspectRatio,
|
||||
child: VideoPlayer(_videoController!),
|
||||
)
|
||||
: const CircularProgressIndicator(),
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: _videoController?.value.isInitialized == true
|
||||
? SizedBox(
|
||||
height: 50.0,
|
||||
child: VideoProgressIndicator(
|
||||
_videoController!,
|
||||
allowScrubbing: true,
|
||||
padding: const EdgeInsets.all(20),
|
||||
colors: progressBarColors,
|
||||
),
|
||||
)
|
||||
: Container(height: 0),
|
||||
),
|
||||
if (_showControls)
|
||||
Center(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (_videoController!.value.isPlaying) {
|
||||
_videoController!.pause();
|
||||
} else {
|
||||
_videoController!.play();
|
||||
}
|
||||
setState(() {
|
||||
_isPlaying = !_isPlaying;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Icon(
|
||||
_isPlaying ? Icons.play_arrow : Icons.pause,
|
||||
size: 64,
|
||||
color: iconColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_videoController?.pause();
|
||||
_videoController?.dispose();
|
||||
if (!kIsRunningTests) {
|
||||
Future.delayed(const Duration(seconds: 1), () async {
|
||||
try {
|
||||
if (_tempVideoFile != null) {
|
||||
await _tempVideoFile!.delete();
|
||||
}
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
64
pubspec.lock
64
pubspec.lock
@ -225,6 +225,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
csv:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -504,6 +512,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
fvp:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: fvp
|
||||
sha256: "995328479ba4641da6760ddc84a168db157a3b9db4f0417fa68713d99344a146"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.14.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -552,6 +568,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.4"
|
||||
html_unescape:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1350,6 +1374,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
video_player:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: video_player
|
||||
sha256: afc65f4b8bcb2c188f64a591f84fb471f4f2e19fc607c65fd8d2f8fedb3dec23
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.8.3"
|
||||
video_player_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_android
|
||||
sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.12"
|
||||
video_player_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_avfoundation
|
||||
sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
video_player_platform_interface:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: video_player_platform_interface
|
||||
sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.2"
|
||||
video_player_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_web
|
||||
sha256: "41245cef5ef29c4585dbabcbcbe9b209e34376642c7576cabf11b4ad9289d6e4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -44,6 +44,9 @@ dependencies:
|
||||
package_info_plus: ^5.0.1
|
||||
flutter_typeahead: ^5.2.0
|
||||
provider: ^6.1.2
|
||||
fvp: ^0.14.0
|
||||
video_player: ^2.3.2
|
||||
video_player_platform_interface: ^6.2.2
|
||||
json_data_explorer:
|
||||
git:
|
||||
url: https://github.com/foss42/json_data_explorer.git
|
||||
|
@ -5,6 +5,7 @@ import 'package:apidash/consts.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:printing/printing.dart' show PdfPreview;
|
||||
import 'package:flutter_svg/flutter_svg.dart' show SvgPicture;
|
||||
import 'package:apidash/widgets/video_previewer.dart';
|
||||
import '../test_consts.dart';
|
||||
|
||||
void main() {
|
||||
@ -63,10 +64,7 @@ void main() {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
find.text("${kMimeTypeRaiseIssueStart}video/H264$kMimeTypeRaiseIssue"),
|
||||
findsOneWidget);
|
||||
expect(find.byType(VideoPreviewer), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Testing when type/subtype is model/step+xml', (tester) async {
|
||||
|
Reference in New Issue
Block a user