Merge pull request #4 from Iconica-Development/feature/add-file-picker

Added file picker and option to change widget
This commit is contained in:
Tim 2023-01-03 14:46:35 +01:00 committed by GitHub
commit 4ba167aacb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 345 additions and 103 deletions

View file

@ -1,22 +1,23 @@
# This is a generated file; do not edit or check into version control.
flutter_plugin_android_lifecycle=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/flutter_plugin_android_lifecycle-2.0.7/
flutter_sound=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/flutter_sound-9.2.13/
flutter_sound_web=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/flutter_sound_web-9.2.13/
image_picker=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/image_picker-0.8.6/
image_picker_android=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/image_picker_android-0.8.5+3/
image_picker_for_web=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/image_picker_for_web-2.1.10/
image_picker_ios=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/image_picker_ios-0.8.6+1/
path_provider=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.11/
path_provider_android=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/path_provider_android-2.0.20/
path_provider_ios=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/path_provider_ios-2.0.11/
path_provider_linux=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.1.7/
path_provider_macos=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.6/
path_provider_windows=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.1.3/
permission_handler=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/permission_handler-10.2.0/
permission_handler_android=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/permission_handler_android-10.2.0/
permission_handler_apple=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/permission_handler_apple-9.0.7/
permission_handler_windows=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/permission_handler_windows-0.1.2/
video_player=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/video_player-2.4.7/
video_player_android=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/video_player_android-2.3.9/
video_player_avfoundation=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/video_player_avfoundation-2.3.7/
video_player_web=/Users/nielsgorter/.pub-cache/hosted/pub.dartlang.org/video_player_web-2.0.12/
file_picker=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\file_picker-5.2.4\\
flutter_plugin_android_lifecycle=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\flutter_plugin_android_lifecycle-2.0.7\\
flutter_sound=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\flutter_sound-9.2.13\\
flutter_sound_web=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\flutter_sound_web-9.2.13\\
image_picker=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\image_picker-0.8.6\\
image_picker_android=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\image_picker_android-0.8.5+4\\
image_picker_for_web=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\image_picker_for_web-2.1.10\\
image_picker_ios=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\image_picker_ios-0.8.6+2\\
path_provider=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\path_provider-2.0.11\\
path_provider_android=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\path_provider_android-2.0.22\\
path_provider_ios=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\path_provider_ios-2.0.11\\
path_provider_linux=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\path_provider_linux-2.1.7\\
path_provider_macos=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\path_provider_macos-2.0.6\\
path_provider_windows=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\path_provider_windows-2.1.3\\
permission_handler=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\permission_handler-10.2.0\\
permission_handler_android=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\permission_handler_android-10.2.0\\
permission_handler_apple=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\permission_handler_apple-9.0.7\\
permission_handler_windows=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\permission_handler_windows-0.1.2\\
video_player=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\video_player-2.4.10\\
video_player_android=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\video_player_android-2.3.10\\
video_player_avfoundation=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\video_player_avfoundation-2.3.8\\
video_player_web=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\video_player_web-2.0.13\\

File diff suppressed because one or more lines are too long

View file

@ -1,19 +1,25 @@
## 0.0.1
## 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.
- Translated the example to English
- Initial port
## 0.1.1
## 0.0.2
- Updated flutter_form version
## 0.0.3
- Fixed bug where onTap was not working when header is set
- Updated flutter_form version to 2.0.1
## 0.1.0
- Ability to set styling for the audio input.
## 0.1.1
## 0.0.3
- Updated flutter_form version to 2.0.1
- Fixed bug where onTap was not working when header is set
## 0.0.2
- Updated flutter_form version
## 0.0.1
- Initial port

BIN
MediaPickerGifNew.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 MiB

View file

@ -17,11 +17,11 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
title: 'Flutter Demo',
title: 'Media Picker Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
home: const MyHomePage(title: 'Media Picker Example'),
),
);
}
@ -45,13 +45,13 @@ class _MyHomePageState extends State<MyHomePage> {
),
body: Center(
child: ElevatedButton(
child: const Text('Media Picker'),
child: const Text('Media Picker Text Options'),
onPressed: () {
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) {
return MediaPickerPage(
return MediaPickerExample(
callback: () {
Navigator.pop(context);
},

View file

@ -8,15 +8,17 @@ import 'package:flutter/material.dart';
import 'package:flutter_media_picker/flutter_media_picker.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class MediaPickerPage extends ConsumerStatefulWidget {
const MediaPickerPage({required this.callback, Key? key}) : super(key: key);
class MediaPickerExample extends ConsumerStatefulWidget {
const MediaPickerExample({required this.callback, Key? key})
: super(key: key);
final Function callback;
@override
ConsumerState<ConsumerStatefulWidget> createState() => _MediaPickerState();
ConsumerState<ConsumerStatefulWidget> createState() =>
_MediaPickerExampleState();
}
class _MediaPickerState extends ConsumerState<MediaPickerPage> {
class _MediaPickerExampleState extends ConsumerState<MediaPickerExample> {
@override
Widget build(BuildContext context) {
var mediaService = ref.read<MediaPickerService>(mediaPickerServiceProvider);
@ -49,7 +51,7 @@ class _MediaPickerState extends ConsumerState<MediaPickerPage> {
height: 14,
),
const Text(
'Maken',
'Create/Pick',
style: TextStyle(
fontWeight: FontWeight.w900,
fontSize: 20,
@ -61,9 +63,14 @@ class _MediaPickerState extends ConsumerState<MediaPickerPage> {
MediaPicker(
mediaPickerInputs: [
MediaPickerInputPhoto(
label: 'Make photo',
// widget: const IconButtonWithText(
// icon: Icons.photo,
// iconText: "Make photo",
// ),
pickFile: mediaService.pickImageFile,
checkPageSettings: {
'title': 'Foto delen',
'title': 'Share photo',
'width': 125.0,
'height': 200.0,
},
@ -72,10 +79,15 @@ class _MediaPickerState extends ConsumerState<MediaPickerPage> {
},
),
MediaPickerInputVideo(
label: 'Make video',
// widget: const IconButtonWithText(
// icon: Icons.video_camera_front,
// iconText: "Make video",
// ),
pickFile: mediaService.pickVideoFile,
videoPlayerFactory: MediaPickerVideoPlayerFactory(),
checkPageSettings: {
'title': 'Video delen',
'title': 'Share video',
'width': 122.5,
'height': 200.0,
},
@ -85,14 +97,48 @@ class _MediaPickerState extends ConsumerState<MediaPickerPage> {
),
if (!kIsWeb)
MediaPickerInputAudio(
checkPageSettings: {'title': 'Audio delen'},
label: 'Record audio',
// widget: const IconButtonWithText(
// icon: Icons.record_voice_over,
// iconText: "Record audio",
// ),
checkPageSettings: {'title': 'Share audio'},
onComplete: (MediaResult result) {
Navigator.pop(context);
},
audioService: audioService,
),
MediaPickerInputText(
checkPageSettings: {'title': 'Tekst delen'},
label: 'Write text',
// widget: const IconButtonWithText(
// icon: Icons.text_fields,
// iconText: "Write text",
// ),
checkPageSettings: {'title': 'Share text'},
onComplete: (MediaResult result) {
Navigator.pop(context);
},
),
MediaPickerInputFile(
label: 'Select file',
// widget: const IconButtonWithText(
// icon: Icons.file_copy,
// iconText: "Select file",
// ),
pickFile: mediaService.pickFile,
fileExtensions: [
'pdf',
'doc',
'png',
'jpg',
'docx',
'bmp',
'gif',
'txt',
],
checkPageSettings: {
'title': 'Share file',
},
onComplete: (MediaResult result) {
Navigator.pop(context);
},

View file

@ -70,7 +70,7 @@ class _MediaCheckPageState extends State<MediaCheckPage> {
widget.cancel();
formController.autoNextStep();
},
child: const Text("Delen"),
child: const Text('Share'),
),
),
),
@ -140,7 +140,7 @@ class _MediaCheckPageState extends State<MediaCheckPage> {
),
Expanded(
child: FlutterFormInputMultiLine(
hint: "Voeg omschrijving toe...",
hint: 'Add description...',
maxCharacters: 300,
controller: descriptionController),
),
@ -149,7 +149,7 @@ class _MediaCheckPageState extends State<MediaCheckPage> {
),
FlutterFormInputSwitch(
label: const Text(
'Deel op je tijdlijn',
'Share on time line',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18,
@ -162,7 +162,7 @@ class _MediaCheckPageState extends State<MediaCheckPage> {
),
FlutterFormInputSwitch(
label: const Text(
'Bewaar in de kluis',
'Save in vault',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18,

View file

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart';
class IconButtonWithText extends StatelessWidget {
const IconButtonWithText({
super.key,
this.iconSize = 40,
this.iconText = 'Button',
this.iconTextSize = 16,
this.icon = Icons.file_copy,
});
final double iconSize;
final String iconText;
final double iconTextSize;
final IconData icon;
@override
Widget build(BuildContext context) {
return FittedBox(
fit: BoxFit.fitHeight,
child: Column(
children: [
Icon(
icon,
size: iconSize,
),
Text(
iconText,
style: TextStyle(fontSize: iconTextSize),
textAlign: TextAlign.center,
),
],
),
);
}
}

View file

@ -85,6 +85,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.4"
file_picker:
dependency: transitive
description:
name: file_picker
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.4"
flutter:
dependency: "direct main"
description: flutter

View file

@ -6,26 +6,24 @@ import 'package:flutter/material.dart';
import 'package:flutter_media_picker/src/media_result.dart';
/// Abstract class for inputs used by [MediaPicker].
///
/// The [label] is used as the title in the header.
///
/// [onPressed] is called when the user has chosen the input to use.
///
/// [displayResult] is used when the checkpage parameter is set for the [MediaPicker].
/// The widget will be given as [displayResult] within the checkpage parameter.
///
/// [checkPageSettings] are some settings that can be set when needed so they can be used in the checkPage.
///
/// [onComplete] will be called when the user has selected/made the media.
/// If checkpage is set this method will be called when the [onComplete] is called in the checkPage.
abstract class MediaPickerInput {
String label = "Media Picker input";
/// The [label] is used as the title for the object in the media picker and no [widget] has been given.
String label = 'Media Picker input';
/// The [widget] is the object that is used to show in the media picker where the user can click on.
Widget? widget;
/// [onPressed] is called when the user has chosen the input to use.
Future<MediaResult> onPressed(BuildContext context);
/// [displayResult] is used when the checkpage parameter is set for the [MediaPicker].
/// The widget will be given as [displayResult] within the checkpage parameter.
Future<Widget> displayResult(MediaResult result);
/// [checkPageSettings] are some settings that can be set when needed so they can be used in the checkPage.
Map<String, dynamic>? checkPageSettings;
/// [onComplete] will be called when the user has selected/made the media.
/// If checkpage is set this method will be called when the [onComplete] is called in the checkPage.
void Function(MediaResult result)? onComplete;
}

View file

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

View file

@ -12,10 +12,11 @@ import 'package:intl/intl.dart';
/// Input for audio used by [MediaPicker].
///
/// This feature is only usable for nativa applications.
/// This feature is only usable for native applications.
class MediaPickerInputAudio implements MediaPickerInput {
MediaPickerInputAudio({
this.label = "Audio",
this.label = 'Audio',
this.widget,
this.checkPageSettings,
this.onComplete,
required this.audioService,
@ -29,6 +30,9 @@ class MediaPickerInputAudio implements MediaPickerInput {
@override
String label;
@override
Widget? widget;
@override
Future<MediaResult> onPressed(BuildContext context) async {
MediaResult audio = MediaResult();
@ -42,7 +46,7 @@ class MediaPickerInputAudio implements MediaPickerInput {
if (content.fileValue != null) {
audio = content;
} else {
throw Exception("No recording returned");
throw Exception('No recording returned');
}
},
inputStyling: inputStyling ?? AudioInputStyling(),

View file

@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as path;
import '../../flutter_media_picker.dart';
/// Input for file used by [MediaPicker].
class MediaPickerInputFile implements MediaPickerInput {
MediaPickerInputFile({
this.label = 'File',
this.widget,
this.fileExtensions = const ['pdf', 'jpg', 'png'],
this.checkPageSettings,
this.onComplete,
this.pickFile,
});
final Future<FilePickerResult?> Function(List<String>)? pickFile;
final List<String> fileExtensions;
@override
String label;
@override
Widget? widget;
@override
Future<MediaResult> onPressed(BuildContext context) async {
var file = await pickFile?.call(fileExtensions);
if (file != null && file.files.first.bytes != null) {
return MediaResult(
fileValue: file.files.first.bytes,
fileType: path.extension(file.files.first.name),
fileName: file.files.first.name,
);
}
return MediaResult();
}
@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':
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!)),
)
]);
default:
}
}
return Container();
}
@override
Map<String, dynamic>? checkPageSettings;
@override
void Function(MediaResult value)? onComplete;
}

View file

@ -12,7 +12,8 @@ import 'package:flutter_media_picker/src/media_result.dart';
/// Input for photo used by [MediaPicker].
class MediaPickerInputPhoto implements MediaPickerInput {
MediaPickerInputPhoto({
this.label = "Foto",
this.label = 'Photo',
this.widget,
this.checkPageSettings,
this.onComplete,
this.pickFile,
@ -23,6 +24,9 @@ class MediaPickerInputPhoto implements MediaPickerInput {
@override
String label;
@override
Widget? widget;
@override
Future<MediaResult> onPressed(BuildContext context) async {
var image = await pickFile?.call();

View file

@ -11,7 +11,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
/// Input for text used by [MediaPicker].
class MediaPickerInputText implements MediaPickerInput {
MediaPickerInputText({
this.label = "Tekst",
this.label = 'Text',
this.widget,
this.checkPageSettings,
this.onComplete,
});
@ -19,6 +20,9 @@ class MediaPickerInputText implements MediaPickerInput {
@override
String label;
@override
Widget? widget;
@override
Future<MediaResult> onPressed(BuildContext context) async {
return MediaResult();

View file

@ -10,7 +10,8 @@ import 'package:flutter_media_picker/flutter_media_picker.dart';
/// Input for video used by [MediaPicker].
class MediaPickerInputVideo implements MediaPickerInput {
MediaPickerInputVideo({
this.label = "Video",
this.label = 'Video',
this.widget,
this.checkPageSettings,
this.onComplete,
this.pickFile,
@ -20,6 +21,9 @@ class MediaPickerInputVideo implements MediaPickerInput {
final Future<Uint8List?> Function()? pickFile;
final VideoPlayerFactory videoPlayerFactory;
@override
Widget? widget;
@override
String label;

View file

@ -6,3 +6,4 @@ export 'input_audio.dart';
export 'input_photo.dart';
export 'input_text.dart';
export 'input_video.dart';
export 'input_file.dart';

View file

@ -100,18 +100,23 @@ import '../flutter_media_picker.dart';
/// ],
/// );
///```
class MediaPicker extends ConsumerWidget {
const MediaPicker({
this.mediaPickerInputs,
this.header,
this.inputsDirection = Axis.horizontal,
this.onComplete,
this.mediaCheckPage,
this.horizontalSpacing = 0,
this.verticalSpacing = 0,
Key? key,
}) : super(key: key);
final List<MediaPickerInput>? mediaPickerInputs;
final Widget Function(String label, Function onPressed)? header;
final void Function(MediaResult result)? onComplete;
final Axis inputsDirection;
final double horizontalSpacing;
final double verticalSpacing;
final Widget Function(
Widget displayResult,
Map<String, dynamic>? inputSettings,
@ -134,18 +139,21 @@ class MediaPicker extends ConsumerWidget {
inputs = mediaPickerInputs!;
}
return Column(
return Wrap(
alignment: WrapAlignment.center,
direction: inputsDirection,
spacing: horizontalSpacing,
runSpacing: verticalSpacing,
children: [
for (final input in inputs) ...[
const SizedBox(height: 2.5),
header?.call(input.label, (BuildContext ct) async {
await onPressedMediaType(ct, input);
}) ??
GestureDetector(
onTap: () async {
await onPressedMediaType(context, input);
},
child: Container(
child: Wrap(
children: [
input.widget ??
Container(
height: 55,
width: MediaQuery.of(context).size.width * 0.9,
decoration: const BoxDecoration(
@ -167,9 +175,10 @@ class MediaPicker extends ConsumerWidget {
),
),
),
),
const SizedBox(height: 2.5),
],
),
),
]
],
);
}
@ -187,6 +196,7 @@ class MediaPicker extends ConsumerWidget {
MediaResult result = MediaResult(
fileValue: content.fileValue,
textValue: content.textValue,
fileType: content.fileType,
checkPageResults: results,
);

View file

@ -10,6 +10,8 @@ class MediaResult {
this.textValue,
this.fileValue,
this.checkPageResults,
this.fileType,
this.fileName,
});
/// For textfield returns actual text,
@ -21,4 +23,10 @@ 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 file name when a file is selected with the FilePicker.
final String? fileName;
}

View file

@ -4,6 +4,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:image_picker/image_picker.dart';
@ -47,4 +48,19 @@ class MediaPickerFileService implements MediaPickerService {
await controller.setLooping(true);
await controller.play();
}
@override
Future<FilePickerResult?> pickFile(List<String> extensions) async {
var file = await FilePicker.platform.pickFiles(
withData: true,
type: FileType.custom,
allowedExtensions: extensions,
);
if (file != null) {
return file;
}
return Future.value(null);
}
}

View file

@ -12,7 +12,9 @@ dependencies:
flutter:
sdk: flutter
path: any
image_picker: any
file_picker: any
video_player: any
path_provider: any
hooks_riverpod: any