Merge pull request #5 from Iconica-Development/feature/add-mime-type

feat: add mimeType
This commit is contained in:
Tim 2023-01-04 16:33:44 +01:00 committed by GitHub
commit 800e3c9240
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 193 additions and 112 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,12 @@
## 0.3.0
- Added mimeType to MediaPickerResult.
- Made mimeType and fileName parameters of MediaPickerResult public so they can be changed if wanted.
- Removed fileType from MediaPickerResult since fileName already includes it.
- Added audio play button on result page so it doesn't play automatically anymore.
## 0.2.0 ## 0.2.0
- Added option to select a file - Added option to select a file
- Added file_picker package for file picking possibility - Added file_picker package for file picking possibility
- Inputs now have widgets so any widget can be used in the media picker now. - Inputs now have widgets so any widget can be used in the media picker now.

View file

@ -45,7 +45,7 @@ class _MyHomePageState extends State<MyHomePage> {
), ),
body: Center( body: Center(
child: ElevatedButton( child: ElevatedButton(
child: const Text('Media Picker Text Options'), child: const Text('Media Picker'),
onPressed: () { onPressed: () {
showModalBottomSheet( showModalBottomSheet(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,

View file

@ -135,9 +135,12 @@ class _MediaPickerExampleState extends ConsumerState<MediaPickerExample> {
'bmp', 'bmp',
'gif', 'gif',
'txt', 'txt',
'mp4',
'mp3',
], ],
checkPageSettings: { checkPageSettings: {
'title': 'Share file', 'title': 'Share file',
'height': 200.0,
}, },
onComplete: (MediaResult result) { onComplete: (MediaResult result) {
Navigator.pop(context); Navigator.pop(context);

View file

@ -131,7 +131,7 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "0.0.1" version: "0.3.0"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@ -296,6 +296,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.0" version: "1.8.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
nested: nested:
dependency: transitive dependency: transitive
description: description:

View file

@ -5,13 +5,14 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter_media_picker/flutter_media_picker.dart';
abstract class MediaPickerService { abstract class MediaPickerService {
/// Returns [Uint8List] based on given [ImageSource]. /// Returns [MediaResult] based on given [ImageSource].
Future<Uint8List?> pickImageFile(); Future<MediaResult?> pickImageFile();
/// Returns [Uint8List] based on given [VideoSource]. /// Returns [Uint8List] based on given [VideoSource].
Future<Uint8List?> pickVideoFile(); Future<MediaResult?> pickVideoFile();
/// Returns [FilePickerResult] based on given [File]. /// Returns [FilePickerResult] based on given [File].
Future<FilePickerResult?> pickFile(List<String> fileExtensions); Future<FilePickerResult?> pickFile(List<String> fileExtensions);

View file

@ -44,6 +44,7 @@ class MediaPickerInputAudio implements MediaPickerInput {
audioService: audioService, audioService: audioService,
onComplete: (MediaResult content) { onComplete: (MediaResult content) {
if (content.fileValue != null) { if (content.fileValue != null) {
content.mimeType = 'audio/mpeg';
audio = content; audio = content;
} else { } else {
throw Exception('No recording returned'); throw Exception('No recording returned');
@ -61,7 +62,17 @@ class MediaPickerInputAudio implements MediaPickerInput {
Future<Widget> displayResult(MediaResult result) async { Future<Widget> displayResult(MediaResult result) async {
var data = result.fileValue; var data = result.fileValue;
if (data != null) { if (data != null) {
audioService.playAudio(data); Column(
children: [
if (result.fileName != null) ...[
Text(result.fileName!),
],
ElevatedButton(
onPressed: () => audioService.playAudio(data),
child: const Text("Play audio"),
),
],
);
} }
return Container(); return Container();

View file

@ -4,8 +4,7 @@
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mime/mime.dart';
import 'package:path/path.dart' as path;
import '../../flutter_media_picker.dart'; import '../../flutter_media_picker.dart';
@ -22,6 +21,8 @@ class MediaPickerInputFile implements MediaPickerInput {
final Future<FilePickerResult?> Function(List<String>)? pickFile; final Future<FilePickerResult?> Function(List<String>)? pickFile;
final List<String> fileExtensions; final List<String> fileExtensions;
final VideoPlayerFactory videoPlayerFactory = MediaPickerVideoPlayerFactory();
final AudioService audioService = MediaPickerAudioService();
@override @override
String label; String label;
@ -31,13 +32,16 @@ class MediaPickerInputFile implements MediaPickerInput {
@override @override
Future<MediaResult> onPressed(BuildContext context) async { Future<MediaResult> onPressed(BuildContext context) async {
var file = await pickFile?.call(fileExtensions); var filePicked = await pickFile?.call(fileExtensions);
if (filePicked != null && filePicked.files.first.bytes != null) {
if (file != null && file.files.first.bytes != null) { var file = filePicked.files.first;
return MediaResult( return MediaResult(
fileValue: file.files.first.bytes, fileValue: file.bytes,
fileType: path.extension(file.files.first.name), fileName: file.name,
fileName: file.files.first.name, mimeType: lookupMimeType(
file.name,
headerBytes: file.bytes,
),
); );
} }
return MediaResult(); return MediaResult();
@ -46,23 +50,20 @@ class MediaPickerInputFile implements MediaPickerInput {
@override @override
Future<Widget> displayResult(MediaResult result) async { Future<Widget> displayResult(MediaResult result) async {
if (result.fileValue != null) { if (result.fileValue != null) {
switch (result.fileType) { var mime = result.mimeType!.split("/").first;
case '.png': switch (mime) {
case '.jpg': case 'image':
case '.jpeg':
case '.webp':
case '.bmp':
case '.gif':
return Image.memory( return Image.memory(
result.fileValue!, result.fileValue!,
height: 250,
); );
case '.pdf': case 'video':
case '.doc': return videoPlayerFactory.createVideoPlayer(
case '.docx': result.fileValue!,
return Text(result.fileName!); );
case '.txt': case 'text':
return Column(mainAxisSize: MainAxisSize.min, children: [ return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(result.fileName!), Text(result.fileName!),
const SizedBox( const SizedBox(
height: 20, height: 20,
@ -70,12 +71,25 @@ class MediaPickerInputFile implements MediaPickerInput {
Center( Center(
child: Text(String.fromCharCodes(result.fileValue!)), child: Text(String.fromCharCodes(result.fileValue!)),
) )
]); ],
);
case 'audio':
return Column(
children: [
if (result.fileName != null) ...[
Text(result.fileName!),
],
ElevatedButton(
onPressed: () => audioService.playAudio(result.fileValue!),
child: const Text("Play audio"),
),
],
);
default: default:
return Text(result.fileName!);
} }
} }
return Text(result.fileName!);
return Container();
} }
@override @override

View file

@ -1,13 +1,11 @@
// SPDX-FileCopyrightText: 2022 Iconica // SPDX-FileCopyrightText: 2022 Iconica
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_media_picker/src/abstracts/media_picker_input.dart'; import 'package:flutter_media_picker/src/abstracts/media_picker_input.dart';
import 'package:flutter_media_picker/src/media_result.dart'; import 'package:flutter_media_picker/src/media_result.dart';
import 'package:mime/mime.dart';
/// Input for photo used by [MediaPicker]. /// Input for photo used by [MediaPicker].
class MediaPickerInputPhoto implements MediaPickerInput { class MediaPickerInputPhoto implements MediaPickerInput {
@ -19,7 +17,7 @@ class MediaPickerInputPhoto implements MediaPickerInput {
this.pickFile, this.pickFile,
}); });
final Future<Uint8List?> Function()? pickFile; final Future<MediaResult?> Function()? pickFile;
@override @override
String label; String label;
@ -31,11 +29,15 @@ class MediaPickerInputPhoto implements MediaPickerInput {
Future<MediaResult> onPressed(BuildContext context) async { Future<MediaResult> onPressed(BuildContext context) async {
var image = await pickFile?.call(); var image = await pickFile?.call();
if (image != null && image.isNotEmpty) { if (image != null) {
return MediaResult( if (image.mimeType == null || image.mimeType!.isEmpty) {
fileValue: image, image.mimeType = lookupMimeType(
image.fileName!,
headerBytes: image.fileValue,
); );
} }
return image;
}
return MediaResult(); return MediaResult();
} }

View file

@ -25,7 +25,7 @@ class MediaPickerInputText implements MediaPickerInput {
@override @override
Future<MediaResult> onPressed(BuildContext context) async { Future<MediaResult> onPressed(BuildContext context) async {
return MediaResult(); return MediaResult(mimeType: 'plain/text');
} }
@override @override
@ -56,7 +56,7 @@ class _DisplayTextState extends ConsumerState<DisplayText> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FlutterFormInputPlainText( return FlutterFormInputPlainText(
label: const Text('Titel'), label: const Text('Title'),
controller: _controller, controller: _controller,
); );
} }

View file

@ -6,6 +6,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_media_picker/flutter_media_picker.dart'; import 'package:flutter_media_picker/flutter_media_picker.dart';
import 'package:mime/mime.dart';
/// Input for video used by [MediaPicker]. /// Input for video used by [MediaPicker].
class MediaPickerInputVideo implements MediaPickerInput { class MediaPickerInputVideo implements MediaPickerInput {
@ -18,7 +19,7 @@ class MediaPickerInputVideo implements MediaPickerInput {
required this.videoPlayerFactory, required this.videoPlayerFactory,
}); });
final Future<Uint8List?> Function()? pickFile; final Future<MediaResult?> Function()? pickFile;
final VideoPlayerFactory videoPlayerFactory; final VideoPlayerFactory videoPlayerFactory;
@override @override
@ -29,13 +30,17 @@ class MediaPickerInputVideo implements MediaPickerInput {
@override @override
Future<MediaResult> onPressed(BuildContext context) async { Future<MediaResult> onPressed(BuildContext context) async {
var image = await pickFile?.call(); var video = await pickFile?.call();
if (image != null && image.isNotEmpty) { if (video != null) {
return MediaResult( if (video.mimeType == null || video.mimeType!.isEmpty) {
fileValue: image, video.mimeType = lookupMimeType(
video.fileName!,
headerBytes: video.fileValue,
); );
} }
return video;
}
return MediaResult(); return MediaResult();
} }

View file

@ -101,7 +101,7 @@ import '../flutter_media_picker.dart';
/// ); /// );
///``` ///```
class MediaPicker extends ConsumerWidget { class MediaPicker extends ConsumerStatefulWidget {
const MediaPicker({ const MediaPicker({
this.mediaPickerInputs, this.mediaPickerInputs,
this.inputsDirection = Axis.horizontal, this.inputsDirection = Axis.horizontal,
@ -123,7 +123,14 @@ class MediaPicker extends ConsumerWidget {
Function(Map<String, dynamic> results) onComplete)? mediaCheckPage; Function(Map<String, dynamic> results) onComplete)? mediaCheckPage;
@override @override
Widget build(BuildContext context, ref) { ConsumerState<ConsumerStatefulWidget> createState() => _MediaPickerState();
}
class _MediaPickerState extends ConsumerState<MediaPicker> {
bool _isLoading = false;
@override
Widget build(BuildContext context) {
List<MediaPickerInput> inputs = [ List<MediaPickerInput> inputs = [
MediaPickerInputPhoto(), MediaPickerInputPhoto(),
MediaPickerInputVideo( MediaPickerInputVideo(
@ -135,19 +142,33 @@ class MediaPicker extends ConsumerWidget {
), ),
]; ];
if (mediaPickerInputs != null) { if (widget.mediaPickerInputs != null) {
inputs = mediaPickerInputs!; inputs = widget.mediaPickerInputs!;
} }
var theme = Theme.of(context);
return Wrap( return Wrap(
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
direction: inputsDirection, direction: widget.inputsDirection,
spacing: horizontalSpacing, spacing: widget.horizontalSpacing,
runSpacing: verticalSpacing, runSpacing: widget.verticalSpacing,
children: [ children: [
if (_isLoading) ...[
SizedBox(
height: 150,
width: 150,
child: CircularProgressIndicator(
color: theme.primaryColor,
),
),
] else ...[
for (final input in inputs) ...[ for (final input in inputs) ...[
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
setState(() {
_isLoading = true;
});
await onPressedMediaType(context, input); await onPressedMediaType(context, input);
}, },
child: Wrap( child: Wrap(
@ -170,7 +191,7 @@ class MediaPicker extends ConsumerWidget {
padding: const EdgeInsets.only(left: 15), padding: const EdgeInsets.only(left: 15),
child: Text( child: Text(
input.label, input.label,
style: Theme.of(context).textTheme.headline6, style: theme.textTheme.headline6,
), ),
), ),
), ),
@ -179,6 +200,7 @@ class MediaPicker extends ConsumerWidget {
), ),
), ),
] ]
]
], ],
); );
} }
@ -187,16 +209,16 @@ class MediaPicker extends ConsumerWidget {
BuildContext context, MediaPickerInput input) async { BuildContext context, MediaPickerInput input) async {
MediaResult content = await input.onPressed(context); MediaResult content = await input.onPressed(context);
if (mediaCheckPage != null && if (widget.mediaCheckPage != null &&
(input.runtimeType == MediaPickerInputText || _hasContent(content))) { (input.runtimeType == MediaPickerInputText || _hasContent(content))) {
var checkPage = mediaCheckPage!( var checkPage = widget.mediaCheckPage!(
await input.displayResult(content), await input.displayResult(content),
input.checkPageSettings, input.checkPageSettings,
(Map<String, dynamic> results) { (Map<String, dynamic> results) {
MediaResult result = MediaResult( MediaResult result = MediaResult(
fileValue: content.fileValue, fileValue: content.fileValue,
textValue: content.textValue, textValue: content.textValue,
fileType: content.fileType, mimeType: content.mimeType,
checkPageResults: results, checkPageResults: results,
); );
@ -204,8 +226,8 @@ class MediaPicker extends ConsumerWidget {
input.onComplete!(result); input.onComplete!(result);
} }
if (onComplete != null) { if (widget.onComplete != null) {
onComplete!(result); widget.onComplete!(result);
} }
}, },
); );
@ -222,8 +244,8 @@ class MediaPicker extends ConsumerWidget {
input.onComplete!(content); input.onComplete!(content);
} }
if (onComplete != null) { if (widget.onComplete != null) {
onComplete!(content); widget.onComplete!(content);
} }
} }
} }

View file

@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: 2022 Iconica // SPDX-FileCopyrightText: 2022 Iconica
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'dart:typed_data'; import 'dart:typed_data';
/// MediaResult is a model that is used to return the media selected/media with the [MediaPicker]. /// MediaResult is a model that is used to return the media selected/media with the [MediaPicker].
@ -10,7 +9,7 @@ class MediaResult {
this.textValue, this.textValue,
this.fileValue, this.fileValue,
this.checkPageResults, this.checkPageResults,
this.fileType, this.mimeType,
this.fileName, this.fileName,
}); });
@ -24,9 +23,9 @@ class MediaResult {
/// Returns the values from the checkPageResults if checkpage is set. /// Returns the values from the checkPageResults if checkpage is set.
final Map<String, dynamic>? checkPageResults; final Map<String, dynamic>? checkPageResults;
/// Returns the filetype when a file is selected with the FilePicker. /// Returns the mime type of the file.
final String? fileType; String? mimeType;
/// Returns the file name when a file is selected with the FilePicker. /// Returns the file name when a file is selected with the FilePicker.
final String? fileName; String? fileName;
} }

View file

@ -59,7 +59,7 @@ class MediaPickerAudioService implements AudioService {
_recorder.closeRecorder(); _recorder.closeRecorder();
_player.openPlayer(); _player.openPlayer();
_player.startPlayer( _player.startPlayer(
fromURI: _directory!.path, fromDataBuffer: audio,
); );
} }
} }

View file

@ -6,7 +6,7 @@ import 'dart:typed_data';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_media_picker/src/abstracts/media_picker_service.dart'; import 'package:flutter_media_picker/flutter_media_picker.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:flutter_media_picker/video_player/video_player_native.dart' import 'package:flutter_media_picker/video_player/video_player_native.dart'
if (dart.library.html) 'package:media_picker/video_player/video_player_web.dart' if (dart.library.html) 'package:media_picker/video_player/video_player_web.dart'
@ -17,21 +17,29 @@ class MediaPickerFileService implements MediaPickerService {
late VideoPlayerController controller; late VideoPlayerController controller;
@override @override
Future<Uint8List?> pickImageFile() async { Future<MediaResult?> pickImageFile() async {
var image = await ImagePicker().pickImage(source: ImageSource.camera); var image = await ImagePicker().pickImage(source: ImageSource.camera);
if (image != null) { if (image != null) {
return image.readAsBytes(); return MediaResult(
fileName: image.name,
mimeType: image.mimeType,
fileValue: await image.readAsBytes(),
);
} }
return Future.value(null); return Future.value(null);
} }
@override @override
Future<Uint8List?> pickVideoFile() async { Future<MediaResult?> pickVideoFile() async {
var video = await ImagePicker().pickVideo(source: ImageSource.camera); var video = await ImagePicker().pickVideo(source: ImageSource.camera);
if (video != null) { if (video != null) {
return video.readAsBytes(); return MediaResult(
fileName: video.name,
mimeType: video.mimeType,
fileValue: await video.readAsBytes(),
);
} }
return Future.value(null); return Future.value(null);
} }

View file

@ -1,6 +1,6 @@
name: flutter_media_picker name: flutter_media_picker
description: A new Flutter package project. description: A new Flutter package project.
version: 0.0.1 version: 0.3.0
homepage: https://github.com/Iconica-Development/flutter_media_picker homepage: https://github.com/Iconica-Development/flutter_media_picker
publish_to: "none" publish_to: "none"
@ -26,6 +26,7 @@ dependencies:
git: git:
url: https://github.com/Iconica-Development/flutter_form.git url: https://github.com/Iconica-Development/flutter_form.git
ref: 2.0.1 ref: 2.0.1
mime: ^1.0.3
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: