mirror of
https://github.com/Iconica-Development/flutter_media_picker.git
synced 2025-05-18 16:33:45 +02:00
feat: add mimeType
This commit is contained in:
parent
4ba167aacb
commit
d0a55263b2
16 changed files with 193 additions and 112 deletions
File diff suppressed because one or more lines are too long
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ class MediaPickerAudioService implements AudioService {
|
|||
_recorder.closeRecorder();
|
||||
_player.openPlayer();
|
||||
_player.startPlayer(
|
||||
fromURI: _directory!.path,
|
||||
fromDataBuffer: audio,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue