feat: add mimeType

This commit is contained in:
Tim 2023-01-04 14:35:36 +01:00
parent 4ba167aacb
commit d0a55263b2
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
- Added option to select a file
- Added file_picker package for file picking possibility
- 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(
child: ElevatedButton(
child: const Text('Media Picker Text Options'),
child: const Text('Media Picker'),
onPressed: () {
showModalBottomSheet(
backgroundColor: Colors.transparent,

View file

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

View file

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

View file

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

View file

@ -44,6 +44,7 @@ class MediaPickerInputAudio implements MediaPickerInput {
audioService: audioService,
onComplete: (MediaResult content) {
if (content.fileValue != null) {
content.mimeType = 'audio/mpeg';
audio = content;
} else {
throw Exception('No recording returned');
@ -61,7 +62,17 @@ class MediaPickerInputAudio implements MediaPickerInput {
Future<Widget> displayResult(MediaResult result) async {
var data = result.fileValue;
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();

View file

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

View file

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

View file

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

View file

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

View file

@ -101,7 +101,7 @@ import '../flutter_media_picker.dart';
/// );
///```
class MediaPicker extends ConsumerWidget {
class MediaPicker extends ConsumerStatefulWidget {
const MediaPicker({
this.mediaPickerInputs,
this.inputsDirection = Axis.horizontal,
@ -123,7 +123,14 @@ class MediaPicker extends ConsumerWidget {
Function(Map<String, dynamic> results) onComplete)? mediaCheckPage;
@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 = [
MediaPickerInputPhoto(),
MediaPickerInputVideo(
@ -135,49 +142,64 @@ class MediaPicker extends ConsumerWidget {
),
];
if (mediaPickerInputs != null) {
inputs = mediaPickerInputs!;
if (widget.mediaPickerInputs != null) {
inputs = widget.mediaPickerInputs!;
}
var theme = Theme.of(context);
return Wrap(
alignment: WrapAlignment.center,
direction: inputsDirection,
spacing: horizontalSpacing,
runSpacing: verticalSpacing,
direction: widget.inputsDirection,
spacing: widget.horizontalSpacing,
runSpacing: widget.verticalSpacing,
children: [
for (final input in inputs) ...[
GestureDetector(
onTap: () async {
await onPressedMediaType(context, input);
},
child: Wrap(
children: [
input.widget ??
Container(
height: 55,
width: MediaQuery.of(context).size.width * 0.9,
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xFF979797),
width: 1,
),
),
),
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 15),
child: Text(
input.label,
style: Theme.of(context).textTheme.headline6,
),
),
),
),
],
if (_isLoading) ...[
SizedBox(
height: 150,
width: 150,
child: CircularProgressIndicator(
color: theme.primaryColor,
),
),
] else ...[
for (final input in inputs) ...[
GestureDetector(
onTap: () async {
setState(() {
_isLoading = true;
});
await onPressedMediaType(context, input);
},
child: Wrap(
children: [
input.widget ??
Container(
height: 55,
width: MediaQuery.of(context).size.width * 0.9,
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xFF979797),
width: 1,
),
),
),
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 15),
child: Text(
input.label,
style: theme.textTheme.headline6,
),
),
),
),
],
),
),
]
]
],
);
@ -187,16 +209,16 @@ class MediaPicker extends ConsumerWidget {
BuildContext context, MediaPickerInput input) async {
MediaResult content = await input.onPressed(context);
if (mediaCheckPage != null &&
if (widget.mediaCheckPage != null &&
(input.runtimeType == MediaPickerInputText || _hasContent(content))) {
var checkPage = mediaCheckPage!(
var checkPage = widget.mediaCheckPage!(
await input.displayResult(content),
input.checkPageSettings,
(Map<String, dynamic> results) {
MediaResult result = MediaResult(
fileValue: content.fileValue,
textValue: content.textValue,
fileType: content.fileType,
mimeType: content.mimeType,
checkPageResults: results,
);
@ -204,8 +226,8 @@ class MediaPicker extends ConsumerWidget {
input.onComplete!(result);
}
if (onComplete != null) {
onComplete!(result);
if (widget.onComplete != null) {
widget.onComplete!(result);
}
},
);
@ -222,8 +244,8 @@ class MediaPicker extends ConsumerWidget {
input.onComplete!(content);
}
if (onComplete != null) {
onComplete!(content);
if (widget.onComplete != null) {
widget.onComplete!(content);
}
}
}

View file

@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:typed_data';
/// 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.fileValue,
this.checkPageResults,
this.fileType,
this.mimeType,
this.fileName,
});
@ -24,9 +23,9 @@ class MediaResult {
/// Returns the values from the checkPageResults if checkpage is set.
final Map<String, dynamic>? checkPageResults;
/// Returns the filetype when a file is selected with the FilePicker.
final String? fileType;
/// Returns the mime type of the file.
String? mimeType;
/// 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();
_player.openPlayer();
_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: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:flutter_media_picker/video_player/video_player_native.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;
@override
Future<Uint8List?> pickImageFile() async {
Future<MediaResult?> pickImageFile() async {
var image = await ImagePicker().pickImage(source: ImageSource.camera);
if (image != null) {
return image.readAsBytes();
return MediaResult(
fileName: image.name,
mimeType: image.mimeType,
fileValue: await image.readAsBytes(),
);
}
return Future.value(null);
}
@override
Future<Uint8List?> pickVideoFile() async {
Future<MediaResult?> pickVideoFile() async {
var video = await ImagePicker().pickVideo(source: ImageSource.camera);
if (video != null) {
return video.readAsBytes();
return MediaResult(
fileName: video.name,
mimeType: video.mimeType,
fileValue: await video.readAsBytes(),
);
}
return Future.value(null);
}

View file

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