diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies
index 3aba78e..ac85ea7 100644
--- a/.flutter-plugins-dependencies
+++ b/.flutter-plugins-dependencies
@@ -1 +1 @@
-{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"permission_handler_apple","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler_apple-9.0.7\\\\","native_build":true,"dependencies":[]},{"name":"photo_gallery","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\photo_gallery-1.1.1\\\\","native_build":true,"dependencies":[]},{"name":"video_player_avfoundation","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_avfoundation-2.3.8\\\\","native_build":true,"dependencies":[]},{"name":"video_thumbnail","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_thumbnail-0.5.3\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"permission_handler_android","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler_android-10.2.0\\\\","native_build":true,"dependencies":[]},{"name":"photo_gallery","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\photo_gallery-1.1.1\\\\","native_build":true,"dependencies":[]},{"name":"video_player_android","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_android-2.3.10\\\\","native_build":true,"dependencies":[]},{"name":"video_thumbnail","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_thumbnail-0.5.3\\\\","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[{"name":"permission_handler_windows","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler_windows-0.1.2\\\\","native_build":true,"dependencies":[]}],"web":[{"name":"video_player_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_web-2.0.13\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]},{"name":"photo_gallery","dependencies":[]},{"name":"video_player","dependencies":["video_player_android","video_player_avfoundation","video_player_web"]},{"name":"video_player_android","dependencies":[]},{"name":"video_player_avfoundation","dependencies":[]},{"name":"video_player_web","dependencies":[]},{"name":"video_thumbnail","dependencies":[]}],"date_created":"2023-01-06 16:12:29.754813","version":"3.3.10"}
\ No newline at end of file
+{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"permission_handler_apple","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler_apple-9.0.7\\\\","native_build":true,"dependencies":[]},{"name":"photo_gallery","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\photo_gallery-1.1.1\\\\","native_build":true,"dependencies":[]},{"name":"video_player_avfoundation","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_avfoundation-2.3.8\\\\","native_build":true,"dependencies":[]},{"name":"video_thumbnail","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_thumbnail-0.5.3\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"permission_handler_android","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler_android-10.2.0\\\\","native_build":true,"dependencies":[]},{"name":"photo_gallery","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\photo_gallery-1.1.1\\\\","native_build":true,"dependencies":[]},{"name":"video_player_android","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_android-2.3.10\\\\","native_build":true,"dependencies":[]},{"name":"video_thumbnail","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_thumbnail-0.5.3\\\\","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[{"name":"permission_handler_windows","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler_windows-0.1.2\\\\","native_build":true,"dependencies":[]}],"web":[{"name":"video_player_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_web-2.0.13\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]},{"name":"photo_gallery","dependencies":[]},{"name":"video_player","dependencies":["video_player_android","video_player_avfoundation","video_player_web"]},{"name":"video_player_android","dependencies":[]},{"name":"video_player_avfoundation","dependencies":[]},{"name":"video_player_web","dependencies":[]},{"name":"video_thumbnail","dependencies":[]}],"date_created":"2023-01-20 09:57:50.239997","version":"3.3.10"}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b7189e8..4f2cf2e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -93,4 +93,10 @@
## 0.2.3
-* thumbnail's resolotion upgraded
\ No newline at end of file
+* thumbnail's resolotion upgraded
+
+## 0.3.0
+
+* Package performance has been improved
+* BottomSheetLayout changed into PickerScaffold
+* PermissionDeniedPage added
\ No newline at end of file
diff --git a/README.md b/README.md
index 2d0b15a..e4b0c5a 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Gallery Picker is a flutter package that will allow you to pick media file(s), manage and navigate inside your gallery with modern tools and views.
-
+
## Features
@@ -32,6 +32,8 @@ Gallery Picker is a flutter package that will allow you to pick media file(s), m
[✔] Examples provided (example/lib/examples)
+[✔] Permission requests handled within the library
+
[✔] Null-safety
You could find the code samples of the given gifs below in `/example/lib/examples` folder.
@@ -40,16 +42,16 @@ You could find the code samples of the given gifs below in `/example/lib/example
@@ -107,24 +109,23 @@ Dispose listener
GalleryPicker.disposeSelectedFilesListener();
```
-### BottomSheetLayout
+### PickerScaffold
-Gallery Picker could also work as a bottom sheet. Wrap your scaffold's body with BottomSheetLayout
+Gallery Picker could also work as a bottom sheet. Use PickerScaffold instead your Scaffold.
There is an example at `example/lib/examples/bottom_sheet_example.dart` to see how it could be done.
```dart
@override
Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text(widget.title),
- ),
- body: BottomSheetLayout(
- config: Config()
- onSelect: (media) {},
- child: Column(
- children: [
+ return PickerScaffold(
+ backgroundColor: Colors.transparent,
+ onSelect: (media) {},
+ initSelectedMedia: initMedia,
+ config: Config(mode: Mode.dark),
+ body: Container(),
+ )
+ }
```
### Customizable destination page
@@ -225,6 +226,7 @@ List? media = await GalleryPicker.pickMedia(
context: context,
config: Config(
backgroundColor: Colors.white,
+ permissionDeniedPage:PermissionDeniedPage(),
appbarColor: Colors.white,
bottomSheetColor: const Color.fromARGB(255, 247, 248, 250),
appbarIconColor: const Color.fromARGB(255, 130, 141, 148),
@@ -326,6 +328,19 @@ GalleryPicker returns MediaFile list. You can reach out features below.
[✔] getData function
[✔] Check if the file selected in gallery picker
+## Permission
+Required permissions will be requested when gallery picker is launched. In case of user's rejection of request, the problem will be handled within gallery picker package.
+
+
+
+### Customizing Permission Denied Page
+
+```dart
+Config(
+ permissionDeniedPage: PermissionDeniedPage(),
+)
+```
+
## Ready-to-use widgets
### ThumbnailMedia
@@ -413,6 +428,44 @@ AlbumCategoriesView(
)
```
+## Breaking Changes From 0.2.3
+
+### BottomSheetLayout changed into PickerScaffold
+
+Before:
+
+```dart
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(widget.title),
+ ),
+ body: BottomSheetLayout(
+ config: Config()
+ onSelect: (media) {},
+ child: Column(
+ children: [
+```
+
+Now:
+
+```dart
+ @override
+ Widget build(BuildContext context) {
+ return PickerScaffold(
+ backgroundColor: Colors.transparent,
+ onSelect: (media) {},
+ initSelectedMedia: initMedia,
+ config: Config(mode: Mode.dark),
+ body: Container(),
+ )
+ }
+```
+
+
+
+
## Examples
Check out our examples!
### Standart Gallery Picker
diff --git a/example/lib/examples/bottom_sheet_example.dart b/example/lib/examples/bottom_sheet_example.dart
index 6ee7083..3eb7759 100644
--- a/example/lib/examples/bottom_sheet_example.dart
+++ b/example/lib/examples/bottom_sheet_example.dart
@@ -15,136 +15,135 @@ class _BottomSheetExampleState extends State {
var controller = PageController(initialPage: 0);
@override
Widget build(BuildContext context) {
- return Scaffold(
+ return PickerScaffold(
backgroundColor: Colors.white,
- body: BottomSheetLayout(
- onSelect: (List selectedMedias) {
- this.selectedMedias = selectedMedias;
- pageIndex = 0;
- if (this.selectedMedias.isNotEmpty) {
- Future.delayed(const Duration(milliseconds: 500)).then((value) {
- controller.animateToPage(0,
- duration: const Duration(milliseconds: 500),
- curve: Curves.easeIn);
- });
- }
- setState(() {});
- },
- config: Config(mode: Mode.dark),
- child: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- const Spacer(),
- const Text(
- 'These are your selected medias',
- ),
- const Divider(),
- Expanded(
- flex: 5,
- child: Stack(children: [
- if (selectedMedias.isNotEmpty)
- PageView(
- controller: controller,
- children: [
- for (var media in selectedMedias)
- Center(
- child: MediaProvider(
- media: media,
- ),
- )
- ],
- ),
- if (selectedMedias.isNotEmpty)
- Align(
- alignment: Alignment.centerRight,
- child: TextButton(
- onPressed: () {
- if (pageIndex < selectedMedias.length - 1) {
- pageIndex++;
- controller.animateToPage(pageIndex,
- duration: const Duration(milliseconds: 500),
- curve: Curves.easeIn);
- setState(() {});
- }
- },
- child: const Icon(
- Icons.chevron_right,
- size: 100,
- color: Colors.red,
- )),
- ),
- if (selectedMedias.isNotEmpty)
- Align(
- alignment: Alignment.centerLeft,
- child: TextButton(
- onPressed: () {
- if (pageIndex > 0) {
- pageIndex--;
- controller.animateToPage(pageIndex,
- duration: const Duration(milliseconds: 500),
- curve: Curves.easeIn);
- setState(() {});
- }
- },
- child: const Icon(
- Icons.chevron_left,
- size: 100,
- color: Colors.red,
- )),
- ),
- ]),
- ),
- SizedBox(
- height: 65,
- child: ListView(
- scrollDirection: Axis.horizontal,
- children: [
- for (int i = 0; i < selectedMedias.length; i++)
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 5),
- child: TextButton(
- onPressed: () {
- pageIndex = i;
+ onSelect: (List selectedMedias) {
+ this.selectedMedias = selectedMedias;
+ pageIndex = 0;
+ if (this.selectedMedias.isNotEmpty) {
+ Future.delayed(const Duration(milliseconds: 500)).then((value) {
+ controller.animateToPage(0,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ });
+ }
+ setState(() {});
+ },
+ config: Config(mode: Mode.dark),
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Spacer(),
+ const Text(
+ 'These are your selected medias',
+ style: TextStyle(color: Colors.black),
+ ),
+ const Divider(),
+ Expanded(
+ flex: 5,
+ child: Stack(children: [
+ if (selectedMedias.isNotEmpty)
+ PageView(
+ controller: controller,
+ children: [
+ for (var media in selectedMedias)
+ Center(
+ child: MediaProvider(
+ media: media,
+ ),
+ )
+ ],
+ ),
+ if (selectedMedias.isNotEmpty)
+ Align(
+ alignment: Alignment.centerRight,
+ child: TextButton(
+ onPressed: () {
+ if (pageIndex < selectedMedias.length - 1) {
+ pageIndex++;
controller.animateToPage(pageIndex,
duration: const Duration(milliseconds: 500),
curve: Curves.easeIn);
setState(() {});
- },
- child: Container(
- width: 65,
- height: 50,
- decoration: BoxDecoration(
- border: Border.all(
- width: 2,
- color: pageIndex == i
- ? Colors.red
- : Colors.black)),
- child: ThumbnailMedia(
- media: selectedMedias[i],
- )),
- ),
- )
- ],
- ),
+ }
+ },
+ child: const Icon(
+ Icons.chevron_right,
+ size: 100,
+ color: Colors.red,
+ )),
+ ),
+ if (selectedMedias.isNotEmpty)
+ Align(
+ alignment: Alignment.centerLeft,
+ child: TextButton(
+ onPressed: () {
+ if (pageIndex > 0) {
+ pageIndex--;
+ controller.animateToPage(pageIndex,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ setState(() {});
+ }
+ },
+ child: const Icon(
+ Icons.chevron_left,
+ size: 100,
+ color: Colors.red,
+ )),
+ ),
+ ]),
+ ),
+ SizedBox(
+ height: 65,
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: [
+ for (int i = 0; i < selectedMedias.length; i++)
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 5),
+ child: TextButton(
+ onPressed: () {
+ pageIndex = i;
+ controller.animateToPage(pageIndex,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ setState(() {});
+ },
+ child: Container(
+ width: 65,
+ height: 50,
+ decoration: BoxDecoration(
+ border: Border.all(
+ width: 2,
+ color: pageIndex == i
+ ? Colors.red
+ : Colors.black)),
+ child: ThumbnailMedia(
+ media: selectedMedias[i],
+ )),
+ ),
+ )
+ ],
),
- const Spacer(
- flex: 1,
+ ),
+ const Spacer(
+ flex: 1,
+ ),
+ TextButton(
+ onPressed: () {
+ GalleryPicker.openSheet();
+ },
+ child: const Icon(
+ Icons.open_in_new,
+ size: 40,
),
- TextButton(
- onPressed: () {
- GalleryPicker.openSheet();
- },
- child: const Icon(
- Icons.open_in_new,
- size: 40,
- ),
- ),
- const Spacer(
- flex: 1,
- ),
- ],
- ),
+ ),
+ const Spacer(
+ flex: 1,
+ ),
+ ],
),
),
);
diff --git a/example/lib/examples/whatsapp_pick_photo.dart b/example/lib/examples/whatsapp_pick_photo.dart
index e7304f1..113ca7f 100644
--- a/example/lib/examples/whatsapp_pick_photo.dart
+++ b/example/lib/examples/whatsapp_pick_photo.dart
@@ -17,20 +17,22 @@ class _WhatsappPickPhotoState extends State {
GalleryMedia? gallery;
List selectedMedias = [];
List? cameras;
- CameraLensDirection cameraLensDirection = CameraLensDirection.front;
+ CameraLensDirection cameraLensDirection = CameraLensDirection.back;
@override
void initState() {
initCamera();
fetchMedias();
GalleryPicker.listenSelectedFiles.listen((medias) {
selectedMedias = medias;
- setState(() {});
+ if (mounted) {
+ setState(() {});
+ }
});
super.initState();
}
Future fetchMedias() async {
- gallery = await GalleryPicker.collectGallery;
+ gallery = await GalleryPicker.initializeGallery;
setState(() {});
}
@@ -72,211 +74,207 @@ class _WhatsappPickPhotoState extends State {
@override
Widget build(BuildContext context) {
- return Scaffold(
+ return PickerScaffold(
backgroundColor: Colors.transparent,
- body: BottomSheetLayout(
- onSelect: (List selectedMedias) {
- this.selectedMedias = selectedMedias;
+ onSelect: (List selectedMedias) {
+ this.selectedMedias = selectedMedias;
+ if (mounted) {
setState(() {});
- },
- initSelectedMedia: selectedMedias,
- config: Config(mode: Mode.dark),
- child: Stack(
- children: [
- if (cameraController != null &&
- cameraController!.value.isInitialized)
- SizedBox(
- height: MediaQuery.of(context).size.height,
- child: CameraPreview(
- cameraController!,
- ),
+ }
+ },
+ initSelectedMedia: selectedMedias,
+ config: Config(mode: Mode.dark),
+ body: Stack(
+ children: [
+ if (cameraController != null && cameraController!.value.isInitialized)
+ SizedBox(
+ height: MediaQuery.of(context).size.height,
+ child: CameraPreview(
+ cameraController!,
),
- if (gallery != null && gallery!.recent != null)
- Positioned(
- bottom: 100,
- child: SizedBox(
- width: MediaQuery.of(context).size.width,
- height: 65,
- child: ListView(
- scrollDirection: Axis.horizontal,
- children: [
- for (var media in gallery!.recent!.files)
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 5),
- child: GestureDetector(
- onTap: () {
- if (selectedMedias.isEmpty) {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) =>
- MultipleMediasView([media])),
- );
- } else {
- selectedMedias.any(
- (element) => element.id == media.id)
- ? selectedMedias.removeWhere(
- (element) => element.id == media.id)
- : selectedMedias.add(media);
- setState(() {});
- }
- },
- onLongPress: () {
- if (selectedMedias
- .any((element) => element.id == media.id)) {
- selectedMedias.removeWhere(
- (element) => element.id == media.id);
- } else {
- selectedMedias.add(media);
- }
- setState(() {});
- },
- child: Container(
- width: 65,
- height: 65,
- decoration: BoxDecoration(
- border: Border.all(
- width: 2,
- color: selectedMedias.any((element) =>
- element.id == media.id)
- ? Colors.red
- : Colors.black)),
- child: Stack(
- children: [
- SizedBox(
- width: 65,
- height: 65,
- child: ThumbnailMedia(
- media: media,
- ),
- ),
- if (selectedMedias.any(
- (element) => element.id == media.id))
- Container(
- color: Colors.black.withOpacity(0.3),
- alignment: Alignment.center,
- child: const Icon(
- Icons.check,
- size: 30,
- color: Colors.white,
- ),
- )
- ],
- )),
- ),
- )
- ],
- ),
- ),
- ),
- if (selectedMedias.isNotEmpty)
- Positioned(
- bottom: 150,
- right: 10,
- child: FloatingActionButton(
- onPressed: () {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) =>
- MultipleMediasView(selectedMedias)),
- );
- },
- mini: true,
- child: const Icon(
- Icons.check,
- color: Colors.white,
- ),
- )),
+ ),
+ if (gallery != null && gallery!.recent != null)
Positioned(
- bottom: 20,
- child: SizedBox(
- width: MediaQuery.of(context).size.width,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- TextButton(
- onPressed: () {
- GalleryPicker.openSheet();
- },
- child: Container(
- decoration: BoxDecoration(
- color: Colors.black.withOpacity(0.5),
- shape: BoxShape.circle),
- padding: const EdgeInsets.all(8),
- child: const Icon(
- Icons.image,
- size: 20,
- color: Colors.white,
- ),
- ),
- ),
- GestureDetector(
- onTap: () async {
- setState(() {
- anyProcess = true;
- });
- Future.delayed(const Duration(milliseconds: 100))
- .then((value) => setState(() {
- anyProcess = false;
- }));
- },
- onLongPressStart: (value) {
- setState(() {
- isRecording = true;
- anyProcess = true;
- });
- },
- onLongPressEnd: (value) async {
- setState(() {
- isRecording = false;
- anyProcess = false;
- });
- },
- child: Container(
- padding: const EdgeInsets.all(4),
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- border:
- Border.all(width: 2, color: Colors.white)),
- width: isRecording ? 80 : 65,
- height: isRecording ? 80 : 65,
+ bottom: 100,
+ child: SizedBox(
+ width: MediaQuery.of(context).size.width,
+ height: 65,
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: [
+ for (var media in gallery!.recent!.files)
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 5),
+ child: GestureDetector(
+ onTap: () {
+ if (selectedMedias.isEmpty) {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) =>
+ MultipleMediasView([media])),
+ );
+ } else {
+ selectedMedias
+ .any((element) => element.id == media.id)
+ ? selectedMedias.removeWhere(
+ (element) => element.id == media.id)
+ : selectedMedias.add(media);
+ setState(() {});
+ }
+ },
+ onLongPress: () {
+ if (selectedMedias
+ .any((element) => element.id == media.id)) {
+ selectedMedias.removeWhere(
+ (element) => element.id == media.id);
+ } else {
+ selectedMedias.add(media);
+ }
+ setState(() {});
+ },
child: Container(
- decoration: BoxDecoration(
- color:
- anyProcess ? Colors.red : Colors.transparent,
- shape: BoxShape.circle,
- ),
- ),
+ width: 65,
+ height: 65,
+ decoration: BoxDecoration(
+ border: Border.all(
+ width: 2,
+ color: selectedMedias.any((element) =>
+ element.id == media.id)
+ ? Colors.red
+ : Colors.black)),
+ child: Stack(
+ children: [
+ SizedBox(
+ width: 65,
+ height: 65,
+ child: ThumbnailMedia(
+ media: media,
+ ),
+ ),
+ if (selectedMedias
+ .any((element) => element.id == media.id))
+ Container(
+ color: Colors.black.withOpacity(0.3),
+ alignment: Alignment.center,
+ child: const Icon(
+ Icons.check,
+ size: 30,
+ color: Colors.white,
+ ),
+ )
+ ],
+ )),
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ if (selectedMedias.isNotEmpty)
+ Positioned(
+ bottom: 150,
+ right: 10,
+ child: FloatingActionButton(
+ onPressed: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) =>
+ MultipleMediasView(selectedMedias)),
+ );
+ },
+ mini: true,
+ child: const Icon(
+ Icons.check,
+ color: Colors.white,
+ ),
+ )),
+ Positioned(
+ bottom: 20,
+ child: SizedBox(
+ width: MediaQuery.of(context).size.width,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ TextButton(
+ onPressed: () {
+ GalleryPicker.openSheet();
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ color: Colors.black.withOpacity(0.5),
+ shape: BoxShape.circle),
+ padding: const EdgeInsets.all(8),
+ child: const Icon(
+ Icons.image,
+ size: 20,
+ color: Colors.white,
),
),
- TextButton(
- onPressed: () {
- if (cameraLensDirection ==
- CameraLensDirection.front) {
- cameraLensDirection = CameraLensDirection.back;
- } else {
- cameraLensDirection = CameraLensDirection.front;
- }
- initCamera();
- },
+ ),
+ GestureDetector(
+ onTap: () async {
+ setState(() {
+ anyProcess = true;
+ });
+ Future.delayed(const Duration(milliseconds: 100))
+ .then((value) => setState(() {
+ anyProcess = false;
+ }));
+ },
+ onLongPressStart: (value) {
+ setState(() {
+ isRecording = true;
+ anyProcess = true;
+ });
+ },
+ onLongPressEnd: (value) async {
+ setState(() {
+ isRecording = false;
+ anyProcess = false;
+ });
+ },
+ child: Container(
+ padding: const EdgeInsets.all(4),
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: Border.all(width: 2, color: Colors.white)),
+ width: isRecording ? 80 : 65,
+ height: isRecording ? 80 : 65,
child: Container(
decoration: BoxDecoration(
- color: Colors.black.withOpacity(0.5),
- shape: BoxShape.circle),
- padding: const EdgeInsets.all(8),
- child: const Icon(
- Icons.cameraswitch,
- size: 20,
- color: Colors.white,
+ color: anyProcess ? Colors.red : Colors.transparent,
+ shape: BoxShape.circle,
),
),
),
- ],
- ),
- ))
- ],
- ),
+ ),
+ TextButton(
+ onPressed: () {
+ if (cameraLensDirection == CameraLensDirection.front) {
+ cameraLensDirection = CameraLensDirection.back;
+ } else {
+ cameraLensDirection = CameraLensDirection.front;
+ }
+ initCamera();
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ color: Colors.black.withOpacity(0.5),
+ shape: BoxShape.circle),
+ padding: const EdgeInsets.all(8),
+ child: const Icon(
+ Icons.cameraswitch,
+ size: 20,
+ color: Colors.white,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ))
+ ],
),
);
}
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 302d916..ae2f9c6 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -15,13 +15,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
- bottom_sheet_bar:
+ bottom_sheet_scaffold:
dependency: transitive
description:
- name: bottom_sheet_bar
+ name: bottom_sheet_scaffold
url: "https://pub.dartlang.org"
source: hosted
- version: "2.3.8"
+ version: "0.1.1"
camera:
dependency: "direct dev"
description:
@@ -141,7 +141,7 @@ packages:
path: ".."
relative: true
source: path
- version: "0.2.3"
+ version: "0.3.0"
get:
dependency: transitive
description:
@@ -191,13 +191,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
- measure_size:
- dependency: transitive
- description:
- name: measure_size
- url: "https://pub.dartlang.org"
- source: hosted
- version: "4.0.0"
meta:
dependency: transitive
description:
@@ -205,6 +198,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
+ page_transition:
+ dependency: transitive
+ description:
+ name: page_transition
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.9"
path:
dependency: transitive
description:
@@ -379,5 +379,5 @@ packages:
source: hosted
version: "0.5.3"
sdks:
- dart: ">=2.18.5 <3.0.0"
+ dart: ">=2.18.6 <3.0.0"
flutter: ">=3.0.0"
diff --git a/lib/controller/bottom_sheet_controller.dart b/lib/controller/bottom_sheet_controller.dart
deleted file mode 100644
index 53691fc..0000000
--- a/lib/controller/bottom_sheet_controller.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-import 'package:bottom_sheet_bar/bottom_sheet_bar.dart';
-import 'package:get/get.dart';
-
-import 'gallery_controller.dart';
-
-class BottomSheetController extends GetxController {
- BottomSheetBarController sheetController;
- PhoneGalleryController? galleryController;
- bool isClosing = false;
- bool appBarTapping = false;
-
- Future open() async {
- await sheetController.expand();
- }
-
- void tapingStatus(bool value) {
- appBarTapping = value;
- if (galleryController == null) {
- Get.find().update();
- } else {
- galleryController!.update();
- }
- update();
- }
-
- Future close() async {
- isClosing = true;
- update();
- await sheetController.collapse();
- isClosing = false;
- update();
- }
-
- void disposeController() {
- isClosing = false;
- appBarTapping = false;
- GetInstance().delete();
- }
-
- BottomSheetController(this.sheetController);
-}
diff --git a/lib/controller/gallery_controller.dart b/lib/controller/gallery_controller.dart
index 3c807da..8d71867 100644
--- a/lib/controller/gallery_controller.dart
+++ b/lib/controller/gallery_controller.dart
@@ -1,3 +1,4 @@
+import 'dart:async';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
@@ -15,13 +16,22 @@ import 'picker_listener.dart';
class PhoneGalleryController extends GetxController {
late Config config;
- PhoneGalleryController(Config? config,
- {required this.onSelect,
- required this.heroBuilder,
- required this.isRecent,
+ void configuration(Config? config,
+ {required dynamic Function(List) onSelect,
+ required Widget Function(String, MediaFile, BuildContext)? heroBuilder,
+ required bool isRecent,
+ required bool startWithRecent,
required List? initSelectedMedias,
required List? extraRecentMedia,
- required this.multipleMediasBuilder}) {
+ required Widget Function(List, BuildContext)?
+ multipleMediasBuilder}) {
+ this.onSelect = onSelect;
+ this.heroBuilder = heroBuilder;
+ this.isRecent = isRecent;
+ this.startWithRecent = startWithRecent;
+ this.multipleMediasBuilder = multipleMediasBuilder;
+ pageController = PageController();
+ pickerPageController = PageController(initialPage: startWithRecent ? 0 : 1);
this.config = config ?? Config();
if (initSelectedMedias != null) {
_selectedFiles = initSelectedMedias.map((e) => e).toList();
@@ -32,16 +42,21 @@ class PhoneGalleryController extends GetxController {
if (selectedFiles.isNotEmpty) {
_pickerMode = true;
}
+ configurationCompleted = true;
}
- bool isRecent;
- Function(List selectedMedias) onSelect;
+
+ late bool startWithRecent;
+ late bool isRecent;
+ bool permissionGranted = false;
+ bool configurationCompleted = false;
+ late Function(List selectedMedias) onSelect;
Widget Function(String tag, MediaFile media, BuildContext context)?
heroBuilder;
Widget Function(List medias, BuildContext context)?
multipleMediasBuilder;
- GalleryAlbum? selectedAlbum;
- List _galleryAlbums = [];
- List get galleryAlbums => _galleryAlbums;
+ GalleryMedia? _media;
+ GalleryMedia? get media => _media;
+ List get galleryAlbums => _media == null ? [] : _media!.albums;
List _selectedFiles = [];
List? _extraRecentMedia;
List get selectedFiles => _selectedFiles;
@@ -50,6 +65,26 @@ class PhoneGalleryController extends GetxController {
List? get extraRecentMedia => _extraRecentMedia;
bool _pickerMode = false;
bool get pickerMode => _pickerMode;
+ late PageController pageController;
+ late PageController pickerPageController;
+ GalleryAlbum? selectedAlbum;
+
+ void resetBottomSheetView() {
+ if (permissionGranted) {
+ isRecent = true;
+ if (selectedAlbum == null) {
+ pickerPageController.jumpToPage(0);
+ } else {
+ pageController.jumpToPage(0);
+ }
+ selectedAlbum = null;
+ update();
+ }
+ }
+
+ void updateConfig(Config? config) {
+ this.config = config ?? Config();
+ }
void updateSelectedFiles(List media) {
_selectedFiles = media.map((e) => e).toList();
@@ -69,11 +104,26 @@ class PhoneGalleryController extends GetxController {
update();
}
- void changeAlbum(GalleryAlbum? album) {
- selectedAlbum = album;
+ Future changeAlbum(
+ {required GalleryAlbum album,
+ required BuildContext context,
+ required PhoneGalleryController controller,
+ required bool singleMedia,
+ required bool isBottomSheet}) async {
_selectedFiles.clear();
- update();
+ selectedAlbum = album;
updatePickerListener();
+ await pageController.animateToPage(1,
+ duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
+ }
+
+ Future backToPicker() async {
+ _selectedFiles.clear();
+ _pickerMode = false;
+ pickerPageController = PageController(initialPage: 1);
+ await pageController.animateToPage(0,
+ duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
+ selectedAlbum = null;
}
void unselectMedia(MediaFile file) {
@@ -99,10 +149,10 @@ class PhoneGalleryController extends GetxController {
void switchPickerMode(bool value) {
if (!value) {
_selectedFiles.clear();
+ updatePickerListener();
}
_pickerMode = value;
update();
- updatePickerListener();
}
void updatePickerListener() {
@@ -112,19 +162,33 @@ class PhoneGalleryController extends GetxController {
}
static Future promptPermissionSetting() async {
+ await PhoneGalleryController.requestStatus(Permission.storage);
+ if (Platform.isIOS) {
+ await PhoneGalleryController.requestStatus(Permission.photos);
+ }
if (Platform.isIOS &&
- await Permission.storage.request().isGranted &&
- await Permission.photos.request().isGranted ||
- Platform.isAndroid && await Permission.storage.request().isGranted) {
+ await Permission.storage.isGranted &&
+ await Permission.photos.isGranted ||
+ Platform.isAndroid && await Permission.storage.isGranted) {
return true;
}
return false;
}
+ static Future requestStatus(Permission permission) async {
+ while (true) {
+ try {
+ await permission.request();
+ break;
+ } catch (e) {
+ await Future.delayed(const Duration(milliseconds: 500), () {});
+ }
+ }
+ }
+
Future initializeAlbums() async {
- GalleryMedia? media = await PhoneGalleryController.collectGallery;
- if (media != null) {
- _galleryAlbums = media.albums;
+ _media = await PhoneGalleryController.collectGallery;
+ if (_media != null) {
if (_extraRecentMedia != null) {
GalleryAlbum? recentTmp = recent;
if (recentTmp != null) {
@@ -132,12 +196,24 @@ class PhoneGalleryController extends GetxController {
recentTmp.files.any((file) => element.id == file.id));
}
}
+ permissionGranted = true;
+ _isInitialized = true;
+ } else {
+ permissionGranted = false;
+ permissionListener();
}
-
- _isInitialized = true;
update();
}
+ void permissionListener() {
+ Timer.periodic(const Duration(seconds: 1), (timer) async {
+ if (await Permission.storage.isGranted) {
+ initializeAlbums();
+ timer.cancel();
+ }
+ });
+ }
+
static Future get collectGallery async {
if (await promptPermissionSetting()) {
List tempGalleryAlbums = [];
@@ -205,8 +281,8 @@ class PhoneGalleryController extends GetxController {
}
GalleryAlbum? get recent {
- return _galleryAlbums.isNotEmpty
- ? _galleryAlbums.singleWhere((element) => element.album.name == "All")
+ return galleryAlbums.isNotEmpty
+ ? galleryAlbums.singleWhere((element) => element.album.name == "All")
: null;
}
//GalleryAlbum? get recent {
@@ -245,14 +321,13 @@ class PhoneGalleryController extends GetxController {
}
bool isSelectedMedia(MediaFile file) {
- return _selectedFiles.any((element) => element.medium.id == file.medium.id);
+ return _selectedFiles.any((element) => element.id == file.id);
}
void disposeController() {
- _galleryAlbums = [];
+ _media = null;
_selectedFiles = [];
_isInitialized = false;
- selectedAlbum = null;
Get.delete();
update();
}
diff --git a/lib/functions/color.dart b/lib/functions/color.dart
new file mode 100644
index 0000000..2cb41ae
--- /dev/null
+++ b/lib/functions/color.dart
@@ -0,0 +1,15 @@
+import 'package:flutter/material.dart';
+
+Color darken(Color color, [double amount = .03]) {
+ assert(amount >= 0 && amount <= 1);
+ final hsl = HSLColor.fromColor(color);
+ final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
+ return hslDark.toColor();
+}
+
+Color lighten(Color color, [double amount = .07]) {
+ assert(amount >= 0 && amount <= 1);
+ final hsl = HSLColor.fromColor(color);
+ final hslLight = hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
+ return hslLight.toColor();
+}
diff --git a/lib/gallery_picker.dart b/lib/gallery_picker.dart
index 91b285b..ef60f7a 100644
--- a/lib/gallery_picker.dart
+++ b/lib/gallery_picker.dart
@@ -15,13 +15,13 @@ export 'user_widgets/gallery_picker_builder.dart';
export 'user_widgets/photo_provider.dart';
export 'user_widgets/video_provider.dart';
export 'user_widgets/media_provider.dart';
-export 'views/bottom_sheet.dart';
+export 'views/picker_scaffold.dart';
export 'views/gallery_picker_view/gallery_picker_view.dart';
+import 'package:bottom_sheet_scaffold/bottom_sheet_scaffold.dart';
import 'package:flutter/material.dart';
import 'package:gallery_picker/models/gallery_media.dart';
import 'package:get/get.dart';
import '../../controller/gallery_controller.dart';
-import 'controller/bottom_sheet_controller.dart';
import 'controller/picker_listener.dart';
import 'models/config.dart';
import 'models/media_file.dart';
@@ -43,9 +43,6 @@ class GalleryPicker {
if (GetInstance().isRegistered()) {
Get.find().disposeController();
}
- if (GetInstance().isRegistered()) {
- Get.find().disposeController();
- }
}
static Future?> pickMedia(
@@ -101,26 +98,35 @@ class GalleryPicker {
}
static Future openSheet() async {
- if (GetInstance().isRegistered()) {
- await Get.find().open();
- }
+ BottomSheetPanel.open();
}
static Future closeSheet() async {
- if (GetInstance().isRegistered()) {
- await Get.find().close();
+ BottomSheetPanel.close();
+ if (GetInstance().isRegistered()) {
+ Get.find().resetBottomSheetView();
}
}
static bool get isSheetOpened {
- if (GetInstance().isRegistered()) {
- return Get.find().sheetController.isExpanded;
- } else {
- return false;
- }
+ return BottomSheetPanel.isOpen;
+ }
+
+ static bool get isSheetExpanded {
+ return BottomSheetPanel.isExpanded;
+ }
+
+ static bool get isSheetCollapsed {
+ return BottomSheetPanel.isCollapsed;
}
static Future get collectGallery async {
return await PhoneGalleryController.collectGallery;
}
+
+ static Future get initializeGallery async {
+ final controller = Get.put(PhoneGalleryController());
+ await controller.initializeAlbums();
+ return controller.media;
+ }
}
diff --git a/lib/models/config.dart b/lib/models/config.dart
index ae55019..c752e93 100644
--- a/lib/models/config.dart
+++ b/lib/models/config.dart
@@ -4,6 +4,7 @@ import 'mode.dart';
class Config {
late Widget selectIcon;
+ Widget? permissionDeniedPage;
late Color backgroundColor,
appbarColor,
appbarIconColor,
@@ -33,6 +34,7 @@ class Config {
TextStyle? unselectedMenuStyle,
TextStyle? textStyle,
TextStyle? appbarTextStyle,
+ this.permissionDeniedPage,
this.recents = "RECENTS",
this.recent = "Recent",
this.gallery = "GALLERY",
diff --git a/lib/models/gallery_album.dart b/lib/models/gallery_album.dart
index 297b327..839a857 100644
--- a/lib/models/gallery_album.dart
+++ b/lib/models/gallery_album.dart
@@ -10,7 +10,7 @@ import 'config.dart';
class GalleryAlbum {
late Album album;
- late List? thumbnail;
+ List? thumbnail;
List dateCategories = [];
late AlbumType type;
int get count =>
@@ -49,8 +49,8 @@ class GalleryAlbum {
Future initialize() async {
List dateCategory = [];
for (var medium in sortAlbumMediaDates((await album.listMedia()).items)) {
- MediaFile mediaFile = MediaFile(medium: medium);
- String name = getDateCategory(medium);
+ MediaFile mediaFile = MediaFile.medium(medium);
+ String name = getDateCategory(mediaFile);
if (dateCategory.any((element) => element.name == name)) {
dateCategory
.singleWhere((element) => element.name == name)
@@ -71,8 +71,9 @@ class GalleryAlbum {
}
DateTime? get lastDate {
- if (dateCategories.isNotEmpty) {
- return dateCategories.first.files.first.medium.lastDate;
+ if (dateCategories.isNotEmpty &&
+ dateCategories.first.files.first.medium != null) {
+ return dateCategories.first.files.first.medium!.lastDate;
} else {
return null;
}
@@ -81,23 +82,22 @@ class GalleryAlbum {
List get files =>
dateCategories.expand((element) => element.files).toList();
- String getDateCategory(Medium mediaFile) {
+ String getDateCategory(MediaFile media) {
Config config = GetInstance().isRegistered()
? Get.find().config
: Config();
- if (daysBetween(mediaFile.lastDate!) <= 3) {
+ DateTime? lastDate = media.lastModified;
+ lastDate = lastDate ?? DateTime.now();
+ if (daysBetween(lastDate) <= 3) {
return config.recent;
- } else if (daysBetween(mediaFile.lastDate!) > 3 &&
- daysBetween(mediaFile.lastDate!) <= 7) {
+ } else if (daysBetween(lastDate) > 3 && daysBetween(lastDate) <= 7) {
return config.lastWeek;
- } else if (daysBetween(mediaFile.lastDate!) > 7 &&
- daysBetween(mediaFile.lastDate!) <= 31) {
+ } else if (daysBetween(lastDate) > 7 && daysBetween(lastDate) <= 31) {
return config.lastMonth;
- } else if (daysBetween(mediaFile.lastDate!) > 31 &&
- daysBetween(mediaFile.lastDate!) <= 365) {
- return DateFormat.MMMM().format(mediaFile.lastDate!).toString();
+ } else if (daysBetween(lastDate) > 31 && daysBetween(lastDate) <= 365) {
+ return DateFormat.MMMM().format(lastDate).toString();
} else {
- return DateFormat.y().format(mediaFile.lastDate!).toString();
+ return DateFormat.y().format(lastDate).toString();
}
}
@@ -113,7 +113,7 @@ class GalleryAlbum {
} else if (b.lastDate == null) {
return -1;
} else {
- return a.lastDate!.compareTo(b.lastDate!);
+ return b.lastDate!.compareTo(a.lastDate!);
}
});
return mediumList;
@@ -125,19 +125,19 @@ class GalleryAlbum {
for (var category in dateCategories) {
category.files.sort((a, b) {
- if (a.medium.lastDate == null) {
+ if (a.medium == null) {
return 1;
- } else if (b.medium.lastDate == null) {
+ } else if (b.medium == null) {
return -1;
} else {
- return b.medium.lastDate!.compareTo(a.medium.lastDate!);
+ return b.medium!.lastDate!.compareTo(a.medium!.lastDate!);
}
});
}
}
void addFile(MediaFile file) {
- String name = getDateCategory(file.medium);
+ String name = getDateCategory(file);
if (dateCategories.any((element) => element.name == name)) {
dateCategories
.singleWhere((element) => element.name == name)
diff --git a/lib/models/media_file.dart b/lib/models/media_file.dart
index 2be8ae3..98e2c63 100644
--- a/lib/models/media_file.dart
+++ b/lib/models/media_file.dart
@@ -1,108 +1,66 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
-import 'package:get/get.dart';
import 'package:photo_gallery/photo_gallery.dart';
import 'package:video_thumbnail/video_thumbnail.dart';
-import '../controller/gallery_controller.dart';
enum MediaType { image, video }
class MediaFile {
- late Medium medium;
- late MediaType type;
+ Medium? _medium;
+ File? _file;
Uint8List? thumbnail;
- Uint8List? data;
- late String id;
- bool thumbnailFailed = false;
- File? file;
- bool _noMedium = false;
- bool get isVideo => type == MediaType.video;
- bool get isImage => type == MediaType.image;
+ late MediaType _type;
+ late String _id;
+ bool get isVideo => _type == MediaType.video;
+ bool get isImage => _type == MediaType.image;
+ Medium? get medium => _medium;
+ MediaType get type => _type;
+ String get id => _id;
+ File? get file => _file;
+ DateTime? get lastModified =>
+ _medium != null ? _medium!.modifiedDate : _file!.lastModifiedSync();
- MediaFile({required this.medium}) {
- type = medium.mediumType == MediumType.video
+ MediaFile.medium(Medium medium) {
+ _medium = medium;
+ _type = _medium!.mediumType == MediumType.video
? MediaType.video
: MediaType.image;
- id = medium.id;
+ _id = _medium!.id;
}
- MediaFile.file({required this.id, required this.file, required this.type}) {
- _noMedium = true;
- medium = Medium(
- id: id,
- mediumType:
- type == MediaType.image ? MediumType.image : MediumType.video);
+ MediaFile.file(
+ {required String id, required File file, required MediaType type}) {
+ _file = file;
+ _id = id;
+ _type = type;
}
- Future getThumbnail() async {
- if (thumbnail == null) {
- try {
- if (_noMedium) {
- thumbnail = isVideo
- ? await VideoThumbnail.thumbnailData(
- video: file!.path,
- imageFormat: ImageFormat.JPEG,
- quality: 100,
- )
- : await getData();
- } else {
- thumbnail =
- Uint8List.fromList(await medium.getThumbnail(highQuality: true));
- }
- } catch (e) {
- thumbnailFailed = true;
- }
+ Future getThumbnail({bool highQuality = true}) async {
+ if (_medium == null) {
+ thumbnail = isVideo
+ ? (await VideoThumbnail.thumbnailData(
+ video: _file!.path,
+ imageFormat: ImageFormat.JPEG,
+ quality: highQuality ? 100 : 20,
+ ))!
+ : await getData();
+ } else {
+ thumbnail = Uint8List.fromList(
+ await _medium!.getThumbnail(highQuality: highQuality));
}
- return thumbnail;
+ return thumbnail!;
}
Future getFile() async {
- if (file == null) {
- file = await medium.getFile();
- return file!;
+ if (_medium != null) {
+ return await _medium!.getFile();
} else {
- return file!;
+ return _file!;
}
}
Future getData() async {
- if (file == null) {
- await getFile();
- }
- data ??= await file!.readAsBytes();
-
- return data!;
- }
-
- void unselect({PhoneGalleryController? controller}) {
- if (controller != null) {
- controller.unselectMedia(this);
- } else {
- if (GetInstance().isRegistered()) {
- Get.find().unselectMedia(this);
- }
- }
- }
-
- void select({PhoneGalleryController? controller}) {
- if (controller != null) {
- controller.selectMedia(this);
- } else {
- if (GetInstance().isRegistered()) {
- Get.find().selectMedia(this);
- }
- }
- }
-
- bool? isSelected({PhoneGalleryController? controller}) {
- if (controller != null) {
- return controller.isSelectedMedia(this);
- } else {
- if (GetInstance().isRegistered()) {
- return Get.find().isSelectedMedia(this);
- } else {
- return null;
- }
- }
+ _file ??= await getFile();
+ return _file!.readAsBytesSync();
}
}
diff --git a/lib/user_widgets/photo_provider.dart b/lib/user_widgets/photo_provider.dart
index 8bda4bf..d642f2d 100644
--- a/lib/user_widgets/photo_provider.dart
+++ b/lib/user_widgets/photo_provider.dart
@@ -1,59 +1,45 @@
import 'package:flutter/material.dart';
+import 'package:transparent_image/transparent_image.dart';
import '../models/media_file.dart';
-class PhotoProvider extends StatefulWidget {
+class PhotoProvider extends StatelessWidget {
final MediaFile media;
final BoxFit fit;
final double? width, height;
+ final Color? backgroundColor;
+ final Widget Function(BuildContext context)? onFailBuilder;
const PhotoProvider({
super.key,
required this.media,
+ this.onFailBuilder,
this.fit = BoxFit.contain,
+ this.backgroundColor,
this.width,
this.height,
});
- @override
- State createState() => _PhotoProviderState();
-}
-
-class _PhotoProviderState extends State {
- late MediaFile _media;
- @override
- void initState() {
- _media = widget.media;
- WidgetsBinding.instance.addPostFrameCallback((_) {
- initMedia();
- });
- super.initState();
- }
-
- Future initMedia() async {
- await _media.getData();
- if (mounted) {
- setState(() {});
- }
- }
-
@override
Widget build(BuildContext context) {
- if (_media != widget.media) {
- _media = widget.media;
- if (_media.data == null) {
- initMedia();
- }
- }
- return _media.data == null
- ? SizedBox(
- width: widget.width,
- height: widget.height,
- )
- : Image.memory(
- _media.data!,
- width: widget.width,
- height: widget.height,
- fit: widget.fit,
- );
+ return FutureBuilder(
+ future: media.getData(),
+ builder: ((context, snapshot) {
+ return Container(
+ color: backgroundColor,
+ width: width,
+ height: height,
+ child: (snapshot.hasError && onFailBuilder != null)
+ ? onFailBuilder!(context)
+ : (snapshot.hasData)
+ ? FadeInImage(
+ fadeInDuration: const Duration(milliseconds: 200),
+ fit: BoxFit.cover,
+ placeholder: MemoryImage(kTransparentImage),
+ image: MemoryImage(snapshot.data!),
+ )
+ : const SizedBox(),
+ );
+ }),
+ );
}
}
diff --git a/lib/user_widgets/thumbnail_album.dart b/lib/user_widgets/thumbnail_album.dart
index 26d110f..b4d0d71 100644
--- a/lib/user_widgets/thumbnail_album.dart
+++ b/lib/user_widgets/thumbnail_album.dart
@@ -1,6 +1,7 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
+import 'package:transparent_image/transparent_image.dart';
import '/models/gallery_album.dart';
import '../models/mode.dart';
@@ -58,9 +59,11 @@ class ThumbnailAlbum extends StatelessWidget {
color: failIconColor,
))
else if (album.thumbnail != null)
- Image.memory(
- Uint8List.fromList(album.thumbnail!),
+ FadeInImage(
+ image: MemoryImage(Uint8List.fromList(album.thumbnail!)),
+ fadeInDuration: const Duration(milliseconds: 200),
fit: BoxFit.cover,
+ placeholder: MemoryImage(kTransparentImage),
)
else
const SizedBox(),
diff --git a/lib/user_widgets/thumbnail_media.dart b/lib/user_widgets/thumbnail_media.dart
index ad221b4..a315aae 100644
--- a/lib/user_widgets/thumbnail_media.dart
+++ b/lib/user_widgets/thumbnail_media.dart
@@ -5,49 +5,83 @@ import 'package:transparent_image/transparent_image.dart';
class ThumbnailMedia extends StatelessWidget {
final MediaFile media;
final bool noIcon;
+ final double? width, height;
+ final Color? backgroundColor;
+ final BoxFit fit;
+ final bool highQuality;
+ final double radius, borderWidth;
+ final Color borderColor;
final Widget Function(MediaFile media, BuildContext context)? onErrorBuilder;
const ThumbnailMedia(
{super.key,
required this.media,
+ this.fit = BoxFit.cover,
this.onErrorBuilder,
+ this.radius = 0,
+ this.highQuality = true,
+ this.borderColor = Colors.transparent,
+ this.borderWidth = 0,
+ this.width,
+ this.height,
+ this.backgroundColor,
this.noIcon = false});
@override
Widget build(BuildContext context) {
return FutureBuilder(
- future: media.thumbnail == null ? media.getThumbnail() : null,
+ future: media.thumbnail == null
+ ? media.getThumbnail(highQuality: highQuality)
+ : null,
builder: (context, snapshot) {
- return Stack(
- fit: StackFit.passthrough,
- children: [
- if (media.thumbnailFailed && onErrorBuilder == null)
- Icon(
- media.isImage
- ? Icons.image_not_supported
- : Icons.videocam_off_rounded,
- color: Colors.grey,
- )
- else if (media.thumbnailFailed && onErrorBuilder == null)
- onErrorBuilder!(media, context)
- else if (media.thumbnail != null)
- FadeInImage(
- fadeInDuration: const Duration(milliseconds: 200),
- fit: BoxFit.cover,
- placeholder: MemoryImage(kTransparentImage),
- image: MemoryImage(media.thumbnail!),
- )
- else
- const SizedBox(),
- if (media.thumbnail != null && !media.thumbnailFailed && !noIcon)
- Positioned(
- bottom: 10,
- left: 10,
- child: Icon(
- media.isVideo ? Icons.video_camera_back : null,
- color: Colors.white,
- size: 20,
- )),
- ],
+ return Container(
+ width: width,
+ height: height,
+ decoration: BoxDecoration(
+ color: backgroundColor,
+ borderRadius: BorderRadius.circular(radius),
+ border: Border.all(color: borderColor, width: borderWidth)),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(radius),
+ child: Stack(
+ fit: StackFit.passthrough,
+ children: [
+ if (snapshot.hasError && onErrorBuilder == null)
+ Center(
+ child: Icon(
+ media.isImage
+ ? Icons.image_not_supported
+ : Icons.videocam_off_rounded,
+ color: Colors.grey,
+ ),
+ )
+ else if (snapshot.hasError && onErrorBuilder == null)
+ onErrorBuilder!(media, context)
+ else if (media.thumbnail != null)
+ FadeInImage(
+ width: width,
+ height: height,
+ fadeInDuration: const Duration(milliseconds: 200),
+ fit: fit,
+ placeholder: MemoryImage(kTransparentImage),
+ image: MemoryImage(media.thumbnail!),
+ )
+ else
+ SizedBox(
+ width: width,
+ height: height,
+ ),
+ if (media.thumbnail != null && !noIcon)
+ Positioned(
+ bottom: 10,
+ left: 10,
+ child: Icon(
+ media.isVideo ? Icons.video_camera_back : null,
+ color: Colors.white,
+ size: 20,
+ )),
+ ],
+ ),
+ ),
);
});
}
diff --git a/lib/views/album_categories_view/album_categories_view.dart b/lib/views/album_categories_view/album_categories_view.dart
index e2c52a9..f881d36 100644
--- a/lib/views/album_categories_view/album_categories_view.dart
+++ b/lib/views/album_categories_view/album_categories_view.dart
@@ -6,7 +6,14 @@ import '../../../controller/gallery_controller.dart';
class AlbumCategoriesView extends StatelessWidget {
final PhoneGalleryController controller;
final Config config;
- AlbumCategoriesView(this.controller, {super.key})
+ final bool isBottomSheet;
+ final bool singleMedia;
+
+ AlbumCategoriesView(
+ {super.key,
+ required this.controller,
+ required this.isBottomSheet,
+ required this.singleMedia})
: config = controller.config;
@override
Widget build(BuildContext context) {
@@ -19,7 +26,12 @@ class AlbumCategoriesView extends StatelessWidget {
children: [
...controller.galleryAlbums.map(
(album) => GestureDetector(
- onTap: () => controller.changeAlbum(album),
+ onTap: () => controller.changeAlbum(
+ album: album,
+ isBottomSheet: isBottomSheet,
+ controller: controller,
+ singleMedia: singleMedia,
+ context: context),
child: Stack(fit: StackFit.passthrough, children: [
ThumbnailAlbum(
album: album,
diff --git a/lib/views/album_view/album_appbar.dart b/lib/views/album_view/album_appbar.dart
index 6c54b48..b80f493 100644
--- a/lib/views/album_view/album_appbar.dart
+++ b/lib/views/album_view/album_appbar.dart
@@ -1,56 +1,51 @@
import 'package:flutter/material.dart';
import 'package:gallery_picker/models/gallery_album.dart';
-import '../../controller/bottom_sheet_controller.dart';
import '../../controller/gallery_controller.dart';
-import '../gallery_picker_view/tappable_appbar.dart';
class AlbumAppBar extends StatelessWidget with PreferredSizeWidget {
final PhoneGalleryController controller;
- final BottomSheetController? bottomSheetController;
final GalleryAlbum album;
+ final bool isBottomSheet;
const AlbumAppBar(
{super.key,
- required this.bottomSheetController,
required this.album,
- required this.controller});
+ required this.controller,
+ required this.isBottomSheet});
@override
Widget build(BuildContext context) {
- return TappableAppbar(
- controller: bottomSheetController,
- child: AppBar(
- elevation: 0,
- foregroundColor: controller.config.appbarIconColor,
- backgroundColor: controller.config.appbarColor,
- leading: TextButton(
- onPressed: () {
- controller.changeAlbum(null);
- },
- child: Icon(
- Icons.arrow_back,
- color: controller.config.appbarIconColor,
- )),
- title: getTitle(),
- actions: [
- !controller.pickerMode
- ? TextButton(
- onPressed: () {
- controller.switchPickerMode(true);
- },
- child: Icon(
- Icons.check_box_outlined,
- color: controller.config.appbarIconColor,
- ))
- : const SizedBox()
- ],
- ),
+ return AppBar(
+ elevation: 0,
+ foregroundColor: controller.config.appbarIconColor,
+ backgroundColor: controller.config.appbarColor,
+ leading: TextButton(
+ onPressed: () async {
+ controller.backToPicker();
+ },
+ child: Icon(
+ Icons.arrow_back,
+ color: controller.config.appbarIconColor,
+ )),
+ title: getTitle(),
+ actions: [
+ !controller.pickerMode
+ ? TextButton(
+ onPressed: () {
+ controller.switchPickerMode(true);
+ },
+ child: Icon(
+ Icons.check_box_outlined,
+ color: controller.config.appbarIconColor,
+ ))
+ : const SizedBox()
+ ],
);
}
Widget getTitle() {
if (!controller.pickerMode && controller.selectedFiles.isEmpty) {
return Text(
- album.name!,
+ album.name ?? "Unnamed Album",
style: controller.config.appbarTextStyle,
);
} else if (controller.pickerMode && controller.selectedFiles.isEmpty) {
diff --git a/lib/views/album_view/album_medias_view.dart b/lib/views/album_view/album_medias_view.dart
index e3d9d76..d021cbe 100644
--- a/lib/views/album_view/album_medias_view.dart
+++ b/lib/views/album_view/album_medias_view.dart
@@ -7,14 +7,15 @@ import 'selected_medias_view.dart';
class AlbumMediasView extends StatelessWidget {
final PhoneGalleryController controller;
final bool singleMedia;
- final bool isCollapsedSheet;
+ final bool isBottomSheet;
const AlbumMediasView(
{super.key,
required this.galleryAlbum,
required this.controller,
- required this.isCollapsedSheet,
+ required this.isBottomSheet,
required this.singleMedia});
final GalleryAlbum galleryAlbum;
+
@override
Widget build(BuildContext context) {
return Stack(
@@ -26,7 +27,7 @@ class AlbumMediasView extends StatelessWidget {
category: category,
controller: controller,
singleMedia: singleMedia,
- isCollapsedSheet: isCollapsedSheet,
+ isBottomSheet: isBottomSheet,
),
],
),
@@ -35,6 +36,7 @@ class AlbumMediasView extends StatelessWidget {
alignment: Alignment.bottomCenter,
child: SelectedMediasView(
controller: controller,
+ isBottomSheet: isBottomSheet,
))
],
);
diff --git a/lib/views/album_view/album_page.dart b/lib/views/album_view/album_page.dart
index 3bee5cd..f8ac9dd 100644
--- a/lib/views/album_view/album_page.dart
+++ b/lib/views/album_view/album_page.dart
@@ -2,38 +2,47 @@ import 'package:flutter/material.dart';
import 'package:gallery_picker/views/album_view/album_appbar.dart';
import '../../../controller/gallery_controller.dart';
import '../../../models/gallery_album.dart';
-import '../../controller/bottom_sheet_controller.dart';
-import '../../models/config.dart';
import 'album_medias_view.dart';
class AlbumPage extends StatelessWidget {
final bool singleMedia;
final PhoneGalleryController controller;
- final BottomSheetController? bottomSheetController;
- final GalleryAlbum album;
- final bool isCollapsedSheet;
+ final GalleryAlbum? album;
+ final bool isBottomSheet;
const AlbumPage(
{super.key,
required this.album,
required this.controller,
required this.singleMedia,
- required this.isCollapsedSheet,
- required this.bottomSheetController});
+ required this.isBottomSheet});
@override
Widget build(BuildContext context) {
- Config config = controller.config;
- return Scaffold(
- backgroundColor: config.backgroundColor,
- appBar: AlbumAppBar(
- bottomSheetController: bottomSheetController,
- album: album,
- controller: controller),
- body: AlbumMediasView(
- galleryAlbum: album,
- controller: controller,
- isCollapsedSheet: isCollapsedSheet,
- singleMedia: singleMedia,
- ),
- );
+ return WillPopScope(
+ child: Scaffold(
+ backgroundColor: controller.config.backgroundColor,
+ appBar: album != null
+ ? AlbumAppBar(
+ album: album!,
+ controller: controller,
+ isBottomSheet: isBottomSheet,
+ )
+ : null,
+ body: album != null
+ ? AlbumMediasView(
+ galleryAlbum: album!,
+ controller: controller,
+ isBottomSheet: isBottomSheet,
+ singleMedia: singleMedia,
+ )
+ : Center(
+ child: Text(
+ "No Album Found",
+ style: controller.config.textStyle,
+ )),
+ ),
+ onWillPop: () async {
+ controller.backToPicker();
+ return false;
+ });
}
}
diff --git a/lib/views/album_view/date_category_view.dart b/lib/views/album_view/date_category_view.dart
index 8f8190b..2011a43 100644
--- a/lib/views/album_view/date_category_view.dart
+++ b/lib/views/album_view/date_category_view.dart
@@ -8,13 +8,13 @@ class DateCategoryWiew extends StatelessWidget {
final PhoneGalleryController controller;
final bool singleMedia;
final DateCategory category;
- final bool isCollapsedSheet;
+ final bool isBottomSheet;
const DateCategoryWiew(
{super.key,
required this.category,
required this.controller,
- required this.isCollapsedSheet,
+ required this.isBottomSheet,
required this.singleMedia});
int getRowCount() {
@@ -45,14 +45,14 @@ class DateCategoryWiew extends StatelessWidget {
size: MediaQuery.of(context).size.width,
padding: EdgeInsets.zero,
crossAxisCount: 4,
- mainAxisSpacing: 1.0,
- crossAxisSpacing: 1.0,
+ mainAxisSpacing: 3.0,
+ crossAxisSpacing: 3.0,
children: [
...category.files.map(
(medium) => MediaView(medium,
controller: controller,
singleMedia: singleMedia,
- isCollapsedSheet: isCollapsedSheet),
+ isBottomSheet: isBottomSheet),
),
],
),
diff --git a/lib/views/album_view/media_view.dart b/lib/views/album_view/media_view.dart
index 30b25a3..163fe73 100644
--- a/lib/views/album_view/media_view.dart
+++ b/lib/views/album_view/media_view.dart
@@ -1,6 +1,5 @@
+import 'package:bottom_sheet_scaffold/bottom_sheet_scaffold.dart';
import 'package:flutter/material.dart';
-import '../../controller/bottom_sheet_controller.dart';
-import 'package:get/get.dart';
import '../../../controller/gallery_controller.dart';
import '../../../models/media_file.dart';
import '../thumbnail_media_file.dart';
@@ -9,89 +8,69 @@ class MediaView extends StatelessWidget {
final MediaFile file;
final PhoneGalleryController controller;
final bool singleMedia;
- final bool isCollapsedSheet;
+ final bool isBottomSheet;
const MediaView(this.file,
{super.key,
required this.controller,
required this.singleMedia,
- required this.isCollapsedSheet});
+ required this.isBottomSheet});
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: [
- GestureDetector(
- onLongPress: () {
- if (singleMedia) {
- if (controller.heroBuilder != null) {
- Navigator.of(context).push(
- MaterialPageRoute(builder: (BuildContext context) {
- return controller.heroBuilder!(file.medium.id, file, context);
- }));
- } else {
- controller.selectedFiles.add(file);
- controller.onSelect(controller.selectedFiles);
- controller.updatePickerListener();
- if (GetInstance().isRegistered()) {
- Get.find().close();
+ ThumbnailMediaFile(
+ onLongPress: () {
+ if (singleMedia) {
+ if (controller.heroBuilder != null) {
+ Navigator.of(context).push(
+ MaterialPageRoute(builder: (BuildContext context) {
+ return controller.heroBuilder!(file.id, file, context);
+ }));
} else {
- Navigator.pop(context);
- controller.disposeController();
+ controller.selectedFiles.add(file);
+ controller.onSelect(controller.selectedFiles);
+ controller.updatePickerListener();
+ if (isBottomSheet) {
+ BottomSheetPanel.close();
+ } else {
+ Navigator.pop(context);
+ controller.disposeController();
+ }
}
- }
- } else {
- file.select(controller: controller);
- }
- },
- onTap: () {
- if (controller.pickerMode) {
- file.isSelected(controller: controller)!
- ? file.unselect(controller: controller)
- : file.select(controller: controller);
- } else {
- if (controller.heroBuilder != null) {
- Navigator.of(context).push(
- MaterialPageRoute(builder: (BuildContext context) {
- return controller.heroBuilder!(file.medium.id, file, context);
- }));
} else {
- controller.selectedFiles.add(file);
- controller.onSelect(controller.selectedFiles);
- controller.updatePickerListener();
- if (GetInstance().isRegistered()) {
- Get.find().close();
- } else {
- Navigator.pop(context);
- controller.disposeController();
- }
+ controller.selectMedia(file);
}
- }
- },
- child: ThumbnailMediaFile(
- file: file,
- failIconColor: controller.config.appbarIconColor,
- controller: controller,
- isCollapsedSheet: isCollapsedSheet),
- ),
- if (file.isSelected(controller: controller)!)
- GestureDetector(
- onTap: () {
- file.isSelected(controller: controller)!
- ? file.unselect(controller: controller)
- : file.select(controller: controller);
},
- child: Opacity(
- opacity: 0.5,
- child: Container(
- color: Colors.black,
- child: const Icon(
- Icons.check,
- color: Colors.white,
- size: 45,
- ),
- ),
- ),
- ),
+ onTap: () {
+ if (controller.pickerMode) {
+ if (controller.isSelectedMedia(file)) {
+ controller.unselectMedia(file);
+ } else {
+ controller.selectMedia(file);
+ }
+ } else {
+ if (controller.heroBuilder != null) {
+ Navigator.of(context).push(
+ MaterialPageRoute(builder: (BuildContext context) {
+ return controller.heroBuilder!(file.id, file, context);
+ }));
+ } else {
+ controller.selectedFiles.add(file);
+ controller.onSelect(controller.selectedFiles);
+ controller.updatePickerListener();
+ if (isBottomSheet) {
+ BottomSheetPanel.close();
+ } else {
+ Navigator.pop(context);
+ controller.disposeController();
+ }
+ }
+ }
+ },
+ file: file,
+ failIconColor: controller.config.appbarIconColor,
+ controller: controller),
],
);
}
diff --git a/lib/views/album_view/selected_media_thumbnail.dart b/lib/views/album_view/selected_media_thumbnail.dart
new file mode 100644
index 0000000..65107a8
--- /dev/null
+++ b/lib/views/album_view/selected_media_thumbnail.dart
@@ -0,0 +1,62 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:transparent_image/transparent_image.dart';
+
+import '../../controller/gallery_controller.dart';
+import '../../functions/color.dart';
+import '../../models/mode.dart';
+
+class SelectedMediaThumbnail extends StatelessWidget {
+ final Uint8List? data;
+ final double? width, height;
+ final BoxFit fit;
+ final PhoneGalleryController controller;
+ final Color failIconColor;
+ const SelectedMediaThumbnail({
+ super.key,
+ required this.failIconColor,
+ required this.controller,
+ required this.data,
+ required this.width,
+ required this.height,
+ this.fit = BoxFit.cover,
+ });
+
+ Color adjustFailedBgColor() {
+ if (controller.config.mode == Mode.dark) {
+ return lighten(
+ controller.config.backgroundColor,
+ );
+ } else {
+ return darken(controller.config.backgroundColor);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: width,
+ height: height,
+ decoration: BoxDecoration(
+ color: adjustFailedBgColor(),
+ borderRadius: BorderRadius.circular(5),
+ ),
+ child: data != null
+ ? FadeInImage(
+ width: width,
+ height: height,
+ fadeInDuration: const Duration(milliseconds: 200),
+ fit: fit,
+ placeholder: MemoryImage(kTransparentImage),
+ image: MemoryImage(data!),
+ )
+ : Center(
+ child: Icon(
+ Icons.browser_not_supported,
+ size: 50,
+ color: failIconColor,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/album_view/selected_medias_view.dart b/lib/views/album_view/selected_medias_view.dart
index c8a7b51..b335ba7 100644
--- a/lib/views/album_view/selected_medias_view.dart
+++ b/lib/views/album_view/selected_medias_view.dart
@@ -1,13 +1,15 @@
+import 'package:bottom_sheet_scaffold/bottom_sheet_scaffold.dart';
import 'package:flutter/material.dart';
-import 'package:get/get.dart';
-import '../../controller/bottom_sheet_controller.dart';
+import 'package:gallery_picker/views/thumbnail_media_file.dart';
import '../../controller/gallery_controller.dart';
import '../../models/config.dart';
class SelectedMediasView extends StatelessWidget {
final PhoneGalleryController controller;
final Config config;
- SelectedMediasView({super.key, required this.controller})
+ final bool isBottomSheet;
+ SelectedMediasView(
+ {super.key, required this.controller, required this.isBottomSheet})
: config = controller.config;
@override
@@ -27,37 +29,19 @@ class SelectedMediasView extends StatelessWidget {
child: ListView(
scrollDirection: Axis.horizontal,
children: [
- for (var selectedMedia in controller.selectedFiles)
+ for (var mediaFile in controller.selectedFiles)
Padding(
padding: const EdgeInsets.only(
top: 3.0, bottom: 3.0, right: 2),
- child: !selectedMedia.thumbnailFailed
- ? Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(5),
- image: DecorationImage(
- fit: BoxFit.fill,
- image: Image.memory(
- selectedMedia.thumbnail!,
- fit: BoxFit.fill,
- ).image),
- ),
- child: const SizedBox(
- width: 47,
- height: 47,
- ),
- )
- : Container(
- width: 47,
- height: 47,
- alignment: Alignment.center,
- child: Icon(
- selectedMedia.isImage
- ? Icons.image_not_supported
- : Icons.videocam_off_rounded,
- color: Colors.grey,
- ),
- ),
+ child: ThumbnailMediaFile(
+ file: mediaFile,
+ width: 50,
+ height: 55,
+ radius: 5,
+ noIcon: true,
+ noSelectedIcon: true,
+ failIconColor: controller.config.appbarIconColor,
+ controller: controller),
),
],
),
@@ -69,7 +53,7 @@ class SelectedMediasView extends StatelessWidget {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) {
return controller.heroBuilder!(
- controller.selectedFiles[0].medium.id,
+ controller.selectedFiles[0].id,
controller.selectedFiles[0],
context);
}));
@@ -81,8 +65,8 @@ class SelectedMediasView extends StatelessWidget {
}));
} else {
controller.onSelect(controller.selectedFiles);
- if (GetInstance().isRegistered()) {
- Get.find().close();
+ if (isBottomSheet) {
+ BottomSheetPanel.close();
} else {
Navigator.pop(context);
controller.disposeController();
diff --git a/lib/views/bottom_sheet.dart b/lib/views/bottom_sheet.dart
deleted file mode 100644
index bfe16f4..0000000
--- a/lib/views/bottom_sheet.dart
+++ /dev/null
@@ -1,167 +0,0 @@
-import 'package:bottom_sheet_bar/bottom_sheet_bar.dart';
-import 'package:flutter/material.dart';
-import 'package:gallery_picker/controller/gallery_controller.dart';
-import '/gallery_picker.dart';
-import 'package:get/get.dart';
-import '../controller/bottom_sheet_controller.dart';
-
-class BottomSheetLayout extends StatefulWidget {
- final Widget child;
- final Config? config;
- final List? initSelectedMedia;
- final List? extraRecentMedia;
- final bool singleMedia;
- final Function(List selectedMedia) onSelect;
- final Widget Function(String tag, MediaFile media, BuildContext context)?
- heroBuilder;
- final Widget Function(List media, BuildContext context)?
- multipleMediaBuilder;
- final bool startWithRecent;
- BottomSheetLayout(
- {super.key,
- required this.child,
- required this.onSelect,
- this.config,
- this.heroBuilder,
- this.initSelectedMedia,
- this.extraRecentMedia,
- this.singleMedia = false,
- this.multipleMediaBuilder,
- this.startWithRecent = true}) {
- if (GetInstance().isRegistered()) {
- if (initSelectedMedia != null) {
- Get.find()
- .updateSelectedFiles(initSelectedMedia!);
- }
- if (extraRecentMedia != null) {
- Get.find()
- .updateExtraRecentMedia(extraRecentMedia!);
- }
- }
- }
-
- @override
- State createState() => _BottomSheetLayoutState();
-}
-
-class _BottomSheetLayoutState extends State {
- BuildContext? collapsedContext;
- bool viewCollapsedPicker = false;
- BottomSheetBarController bottomSheetBarController =
- BottomSheetBarController();
- late BottomSheetController controller;
-
- @override
- void initState() {
- controller = Get.put(BottomSheetController(bottomSheetBarController));
- super.initState();
- }
-
- check() async {
- var sheetController = controller.sheetController;
- if (collapsedContext != null) {
- final RenderBox renderBox =
- collapsedContext!.findRenderObject() as RenderBox;
- if (renderBox.size.height > 200 &&
- !sheetController.isExpanded &&
- !viewCollapsedPicker) {
- await Future.delayed(const Duration(milliseconds: 100));
- controller.appBarTapping = false;
- setState(() {
- viewCollapsedPicker = true;
- });
- } else if ((renderBox.size.height <= 200 || sheetController.isExpanded)) {
- if (viewCollapsedPicker) {
- viewCollapsedPicker = false;
- controller.appBarTapping = false;
- await Future.delayed(const Duration(milliseconds: 10));
- setState(() {});
- }
- }
- }
- }
-
- @override
- void dispose() {
- controller.disposeController();
- super.dispose();
- }
-
- @override
- Widget build(BuildContext context) {
- return GetBuilder(builder: (controller) {
- return BottomSheetBar(
- willPopScope: true,
- height: 0,
- color: Colors.transparent,
- locked:
- (controller.sheetController.isExpanded && !controller.appBarTapping)
- ? true
- : false,
- controller: controller.sheetController,
- expandedBuilder: (scrollController) {
- check();
- return controller.sheetController.isExpanded
- ? GalleryPickerView(
- onSelect: widget.onSelect,
- config: widget.config,
- sheetController: bottomSheetBarController,
- heroBuilder: widget.heroBuilder,
- multipleMediaBuilder: widget.multipleMediaBuilder,
- singleMedia: widget.singleMedia,
- initSelectedMedia: widget.initSelectedMedia,
- extraRecentMedia: widget.extraRecentMedia,
- startWithRecent: widget.startWithRecent,
- )
- : Container(
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.height,
- color: Colors.transparent,
- );
- },
- body: widget.child,
- collapsed: GetBuilder(
- builder: (controller) => ViewCollapsed(
- picker: GalleryPickerView(
- onSelect: widget.onSelect,
- config: widget.config,
- sheetController: bottomSheetBarController,
- heroBuilder: widget.heroBuilder,
- singleMedia: widget.singleMedia,
- multipleMediaBuilder: widget.multipleMediaBuilder,
- initSelectedMedia: widget.initSelectedMedia,
- isCollapsedSheet: true,
- extraRecentMedia: widget.extraRecentMedia,
- startWithRecent: widget.startWithRecent,
- ),
- viewPicker: controller.isClosing ? false : viewCollapsedPicker,
- onBuild: (context) {
- collapsedContext = context;
- }),
- ),
- );
- });
- }
-}
-
-class ViewCollapsed extends StatelessWidget {
- final GalleryPickerView picker;
- final bool viewPicker;
- final Function(BuildContext context) onBuild;
- const ViewCollapsed({
- super.key,
- required this.picker,
- required this.onBuild,
- required this.viewPicker,
- });
-
- @override
- Widget build(BuildContext context) {
- onBuild(context);
- return Container(
- height: 50,
- color: Colors.transparent,
- child: viewPicker ? picker : null,
- );
- }
-}
diff --git a/lib/views/gallery_picker_view/gallery_picker_view.dart b/lib/views/gallery_picker_view/gallery_picker_view.dart
index 275435c..951d7cb 100644
--- a/lib/views/gallery_picker_view/gallery_picker_view.dart
+++ b/lib/views/gallery_picker_view/gallery_picker_view.dart
@@ -1,7 +1,5 @@
-import 'package:bottom_sheet_bar/bottom_sheet_bar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
-import '../../controller/bottom_sheet_controller.dart';
import '../../controller/gallery_controller.dart';
import '../../models/config.dart';
import '../../models/gallery_album.dart';
@@ -9,6 +7,7 @@ import '../../models/media_file.dart';
import '../album_categories_view/album_categories_view.dart';
import '../album_view/album_page.dart';
import '../album_view/album_medias_view.dart';
+import 'permission_denied_view.dart';
import 'picker_appbar.dart';
import 'reload_gallery.dart';
@@ -20,11 +19,10 @@ class GalleryPickerView extends StatefulWidget {
final Widget Function(List media, BuildContext context)?
multipleMediaBuilder;
final bool startWithRecent;
- final BottomSheetBarController? sheetController;
+ final bool isBottomSheet;
final List? initSelectedMedia;
final List? extraRecentMedia;
final bool singleMedia;
- final bool isCollapsedSheet;
const GalleryPickerView(
{super.key,
this.config,
@@ -32,8 +30,7 @@ class GalleryPickerView extends StatefulWidget {
this.initSelectedMedia,
this.extraRecentMedia,
this.singleMedia = false,
- this.isCollapsedSheet = false,
- this.sheetController,
+ this.isBottomSheet = false,
this.heroBuilder,
this.multipleMediaBuilder,
this.startWithRecent = false});
@@ -44,43 +41,39 @@ class GalleryPickerView extends StatefulWidget {
class _GalleryPickerState extends State {
late PhoneGalleryController galleryController;
- BottomSheetController? bottomSheetController;
- late PageController _scrollController;
bool noPhotoSeleceted = true;
late Config config;
@override
void initState() {
- _scrollController =
- PageController(initialPage: widget.startWithRecent ? 0 : 1);
if (GetInstance().isRegistered()) {
galleryController = Get.find();
- config = galleryController.config;
+ if (galleryController.configurationCompleted) {
+ galleryController.updateConfig(widget.config);
+ } else {
+ galleryController.configuration(widget.config,
+ onSelect: widget.onSelect,
+ startWithRecent: widget.startWithRecent,
+ heroBuilder: widget.heroBuilder,
+ multipleMediasBuilder: widget.multipleMediaBuilder,
+ initSelectedMedias: widget.initSelectedMedia,
+ extraRecentMedia: widget.extraRecentMedia,
+ isRecent: widget.startWithRecent);
+ }
} else {
- galleryController = Get.put(PhoneGalleryController(widget.config,
+ galleryController = Get.put(PhoneGalleryController());
+ galleryController.configuration(widget.config,
onSelect: widget.onSelect,
+ startWithRecent: widget.startWithRecent,
heroBuilder: widget.heroBuilder,
multipleMediasBuilder: widget.multipleMediaBuilder,
initSelectedMedias: widget.initSelectedMedia,
extraRecentMedia: widget.extraRecentMedia,
- isRecent: widget.startWithRecent));
- config = galleryController.config;
- }
-
- if (widget.sheetController != null) {
- if (GetInstance().isRegistered()) {
- bottomSheetController = Get.find();
- } else {
- bottomSheetController =
- Get.put(BottomSheetController(widget.sheetController!));
- }
- bottomSheetController!.galleryController = galleryController;
+ isRecent: widget.startWithRecent);
}
+ config = galleryController.config;
if (!galleryController.isInitialized) {
galleryController.initializeAlbums();
}
- if (galleryController.isRecent) {
- _scrollController = PageController(initialPage: 0);
- }
super.initState();
}
@@ -90,144 +83,153 @@ class _GalleryPickerState extends State {
}
GalleryAlbum? selectedAlbum;
+
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
return GetBuilder(builder: (controller) {
- if (controller.selectedAlbum == null && selectedAlbum != null) {
- _scrollController = PageController(initialPage: 1);
- }
- selectedAlbum = controller.selectedAlbum;
return GetInstance().isRegistered()
- ? controller.selectedAlbum == null
- ? Scaffold(
- backgroundColor: config.backgroundColor,
- appBar: PickerAppBar(
- controller: controller,
- bottomSheetController: bottomSheetController,
- ),
- body: Column(
- children: [
- Container(
- width: width,
- height: 48,
- color: config.appbarColor,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- Container(
- decoration: controller.isRecent
- ? BoxDecoration(
- border: Border(
- bottom: BorderSide(
- color: config.underlineColor,
- width: 3.0,
- ),
- ))
- : null,
- height: 48,
- width: width / 2,
- child: TextButton(
- onPressed: () {
- _scrollController.animateTo(0,
- duration:
- const Duration(milliseconds: 50),
- curve: Curves.easeIn);
- setState(() {
- controller.isRecent = true;
- controller.switchPickerMode(false);
- });
- },
- child: Text(config.recents,
- style: controller.isRecent
- ? config.selectedMenuStyle
- : config.unselectedMenuStyle)),
+ ? controller.permissionGranted
+ ? PageView(
+ controller: controller.pageController,
+ physics: const NeverScrollableScrollPhysics(),
+ children: [
+ Scaffold(
+ backgroundColor: config.backgroundColor,
+ appBar: PickerAppBar(
+ controller: controller,
+ isBottomSheet: widget.isBottomSheet,
+ ),
+ body: Column(
+ children: [
+ Container(
+ width: width,
+ height: 48,
+ color: config.appbarColor,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ Container(
+ decoration: controller.isRecent
+ ? BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: config.underlineColor,
+ width: 3.0,
+ ),
+ ))
+ : null,
+ height: 48,
+ width: width / 2,
+ child: TextButton(
+ onPressed: () {
+ controller.pickerPageController
+ .animateToPage(0,
+ duration: const Duration(
+ milliseconds: 50),
+ curve: Curves.easeIn);
+ setState(() {
+ controller.isRecent = true;
+ controller.switchPickerMode(false);
+ });
+ },
+ child: Text(config.recents,
+ style: controller.isRecent
+ ? config.selectedMenuStyle
+ : config.unselectedMenuStyle)),
+ ),
+ Container(
+ decoration: !controller.isRecent
+ ? BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: config.underlineColor,
+ width: 3.0,
+ ),
+ ))
+ : null,
+ height: 48,
+ width: width / 2,
+ child: TextButton(
+ onPressed: () {
+ controller.pickerPageController
+ .animateToPage(1,
+ duration: const Duration(
+ milliseconds: 50),
+ curve: Curves.easeIn);
+ controller.isRecent = false;
+ controller.switchPickerMode(false);
+ },
+ child: Text(
+ config.gallery,
+ style: controller.isRecent
+ ? config.unselectedMenuStyle
+ : config.selectedMenuStyle,
+ )),
+ )
+ ],
),
- Container(
- decoration: !controller.isRecent
- ? BoxDecoration(
- border: Border(
- bottom: BorderSide(
- color: config.underlineColor,
- width: 3.0,
- ),
- ))
- : null,
- height: 48,
- width: width / 2,
- child: TextButton(
- onPressed: () {
- _scrollController.animateTo(width,
- duration:
- const Duration(milliseconds: 50),
- curve: Curves.easeIn);
+ ),
+ Expanded(
+ child: PageView(
+ controller: controller.pickerPageController,
+ onPageChanged: (value) {
+ if (value == 0) {
+ controller.isRecent = true;
+ controller.switchPickerMode(false);
+ } else {
controller.isRecent = false;
controller.switchPickerMode(false);
- },
- child: Text(
- config.gallery,
- style: controller.isRecent
- ? config.unselectedMenuStyle
- : config.selectedMenuStyle,
- )),
- )
- ],
- ),
+ }
+ },
+ scrollDirection: Axis.horizontal,
+ children: [
+ controller.isInitialized &&
+ controller.recent != null
+ ? AlbumMediasView(
+ galleryAlbum: controller.recent!,
+ controller: controller,
+ isBottomSheet: widget.isBottomSheet,
+ singleMedia: widget.singleMedia)
+ : const Center(
+ child: CircularProgressIndicator(
+ color: Colors.grey,
+ )),
+ AlbumCategoriesView(
+ controller: controller,
+ isBottomSheet: widget.isBottomSheet,
+ singleMedia: widget.singleMedia,
+ )
+ ]),
+ ),
+ ],
),
- Expanded(
- child: PageView(
- controller: _scrollController,
- onPageChanged: (value) {
- if (value == 0) {
- controller.isRecent = true;
- controller.switchPickerMode(false);
- } else {
- controller.isRecent = false;
- controller.switchPickerMode(false);
- }
- },
- scrollDirection: Axis.horizontal,
- children: [
- controller.isInitialized
- ? AlbumMediasView(
- galleryAlbum: controller.recent!,
- controller: controller,
- isCollapsedSheet: widget.isCollapsedSheet,
- singleMedia: widget.singleMedia)
- : const Center(
- child: CircularProgressIndicator(
- color: Colors.grey,
- )),
- AlbumCategoriesView(controller)
- ]),
- ),
- ],
- ),
- )
- : AlbumPage(
- controller: controller,
- album: controller.selectedAlbum!,
- singleMedia: widget.singleMedia,
- isCollapsedSheet: widget.isCollapsedSheet,
- bottomSheetController: bottomSheetController,
+ ),
+ AlbumPage(
+ album: controller.selectedAlbum,
+ controller: controller,
+ singleMedia: widget.singleMedia,
+ isBottomSheet: widget.isBottomSheet)
+ ],
)
+ : controller.config.permissionDeniedPage ??
+ PermissionDeniedView(
+ config: controller.config,
+ )
: ReloadGallery(
config,
onpressed: () async {
- if (widget.sheetController != null) {
- bottomSheetController =
- Get.put(BottomSheetController(widget.sheetController!));
- }
- galleryController = Get.put(PhoneGalleryController(config,
+ galleryController = Get.put(PhoneGalleryController());
+ galleryController.configuration(widget.config,
onSelect: widget.onSelect,
+ startWithRecent: widget.startWithRecent,
heroBuilder: widget.heroBuilder,
+ multipleMediasBuilder: widget.multipleMediaBuilder,
initSelectedMedias: widget.initSelectedMedia,
extraRecentMedia: widget.extraRecentMedia,
- multipleMediasBuilder: widget.multipleMediaBuilder,
- isRecent: widget.startWithRecent));
- await controller.initializeAlbums();
- if (bottomSheetController != null) {
- bottomSheetController!.galleryController = galleryController;
+ isRecent: widget.startWithRecent);
+ if (!controller.isInitialized) {
+ await controller.initializeAlbums();
}
setState(() {});
},
diff --git a/lib/views/gallery_picker_view/permission_denied_view.dart b/lib/views/gallery_picker_view/permission_denied_view.dart
new file mode 100644
index 0000000..611eceb
--- /dev/null
+++ b/lib/views/gallery_picker_view/permission_denied_view.dart
@@ -0,0 +1,48 @@
+import 'package:flutter/material.dart';
+import 'package:gallery_picker/gallery_picker.dart';
+import 'package:permission_handler/permission_handler.dart';
+
+class PermissionDeniedView extends StatelessWidget {
+ final Config config;
+ const PermissionDeniedView({super.key, required this.config});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ color: config.backgroundColor,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const SizedBox(
+ height: 50,
+ ),
+ Text(
+ "Please allow access to your photos",
+ style: TextStyle(
+ color: config.textStyle.color,
+ fontSize: 20,
+ fontWeight: FontWeight.w500),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Text(
+ "This lets access your photos and videos from your library.",
+ style: TextStyle(
+ color: config.textStyle.color,
+ ),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ TextButton(
+ onPressed: () async {
+ await openAppSettings();
+ },
+ child: const Text("Enable library access"),
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/views/gallery_picker_view/picker_appbar.dart b/lib/views/gallery_picker_view/picker_appbar.dart
index c821ad4..abc32c7 100644
--- a/lib/views/gallery_picker_view/picker_appbar.dart
+++ b/lib/views/gallery_picker_view/picker_appbar.dart
@@ -1,52 +1,45 @@
+import 'package:bottom_sheet_scaffold/bottom_sheet_scaffold.dart';
import 'package:flutter/material.dart';
-import 'package:get/get.dart';
-import '../../controller/bottom_sheet_controller.dart';
import '../../controller/gallery_controller.dart';
-import 'tappable_appbar.dart';
class PickerAppBar extends StatelessWidget with PreferredSizeWidget {
final PhoneGalleryController controller;
- final BottomSheetController? bottomSheetController;
+ final bool isBottomSheet;
const PickerAppBar(
- {super.key,
- required this.bottomSheetController,
- required this.controller});
+ {super.key, required this.isBottomSheet, required this.controller});
@override
Widget build(BuildContext context) {
- return TappableAppbar(
- controller: bottomSheetController,
- child: AppBar(
- elevation: 0,
- backgroundColor: controller.config.appbarColor,
- leading: TextButton(
- onPressed: () async {
- if (GetInstance().isRegistered()) {
- bottomSheetController!.close();
- } else {
- Navigator.pop(context);
- await Future.delayed(const Duration(milliseconds: 500));
- controller.disposeController();
- }
- },
- child: Icon(
- Icons.arrow_back,
- color: controller.config.appbarIconColor,
- )),
- title: getTitle(),
- actions: [
- !controller.pickerMode && controller.isRecent
- ? TextButton(
- onPressed: () {
- controller.switchPickerMode(true);
- },
- child: Icon(
- Icons.check_box_outlined,
- color: controller.config.appbarIconColor,
- ))
- : const SizedBox()
- ],
- ),
+ return AppBar(
+ elevation: 0,
+ backgroundColor: controller.config.appbarColor,
+ leading: TextButton(
+ onPressed: () async {
+ if (isBottomSheet) {
+ BottomSheetPanel.close();
+ } else {
+ Navigator.pop(context);
+ await Future.delayed(const Duration(milliseconds: 500));
+ controller.disposeController();
+ }
+ },
+ child: Icon(
+ Icons.arrow_back,
+ color: controller.config.appbarIconColor,
+ )),
+ title: getTitle(),
+ actions: [
+ !controller.pickerMode && controller.isRecent
+ ? TextButton(
+ onPressed: () {
+ controller.switchPickerMode(true);
+ },
+ child: Icon(
+ Icons.check_box_outlined,
+ color: controller.config.appbarIconColor,
+ ))
+ : const SizedBox()
+ ],
);
}
diff --git a/lib/views/gallery_picker_view/tappable_appbar.dart b/lib/views/gallery_picker_view/tappable_appbar.dart
deleted file mode 100644
index c9ae64c..0000000
--- a/lib/views/gallery_picker_view/tappable_appbar.dart
+++ /dev/null
@@ -1,30 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:get/get.dart';
-import '../../controller/bottom_sheet_controller.dart';
-
-class TappableAppbar extends StatelessWidget {
- final BottomSheetController? controller;
- final Widget child;
- const TappableAppbar(
- {super.key, required this.controller, required this.child});
-
- @override
- Widget build(BuildContext context) {
- return GetInstance().isRegistered()
- ? GestureDetector(
- onLongPressEnd: (a) {
- if (GetInstance().isRegistered()) {
- controller!.tapingStatus(false);
- }
- },
- onPanCancel: () {},
- onPanDown: (a) {
- if (GetInstance().isRegistered()) {
- controller!.tapingStatus(true);
- }
- },
- child: child,
- )
- : child;
- }
-}
diff --git a/lib/views/picker_scaffold.dart b/lib/views/picker_scaffold.dart
new file mode 100644
index 0000000..b0e8ec1
--- /dev/null
+++ b/lib/views/picker_scaffold.dart
@@ -0,0 +1,136 @@
+import 'package:bottom_sheet_scaffold/bottom_sheet_scaffold.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:gallery_picker/controller/gallery_controller.dart';
+import '/gallery_picker.dart';
+import 'package:get/get.dart';
+
+class PickerScaffold extends StatelessWidget {
+ PickerScaffold(
+ {super.key,
+ required this.onSelect,
+ this.body,
+ this.appBar,
+ this.floatingActionButton,
+ this.floatingActionButtonLocation,
+ this.floatingActionButtonAnimator,
+ this.persistentFooterButtons,
+ this.persistentFooterAlignment = AlignmentDirectional.centerEnd,
+ this.drawer,
+ this.onDrawerChanged,
+ this.endDrawer,
+ this.onEndDrawerChanged,
+ this.bottomNavigationBar,
+ this.backgroundColor,
+ this.resizeToAvoidBottomInset,
+ this.primary = true,
+ this.drawerDragStartBehavior = DragStartBehavior.start,
+ this.extendBody = false,
+ this.extendBodyBehindAppBar = false,
+ this.drawerScrimColor,
+ this.drawerEdgeDragWidth,
+ this.drawerEnableOpenDragGesture = true,
+ this.endDrawerEnableOpenDragGesture = true,
+ this.restorationId,
+ this.config,
+ this.heroBuilder,
+ this.initSelectedMedia,
+ this.extraRecentMedia,
+ this.singleMedia = false,
+ this.multipleMediaBuilder,}) {
+ if (GetInstance().isRegistered()) {
+ if (initSelectedMedia != null) {
+ Get.find()
+ .updateSelectedFiles(initSelectedMedia!);
+ }
+ if (extraRecentMedia != null) {
+ Get.find()
+ .updateExtraRecentMedia(extraRecentMedia!);
+ }
+ }
+ }
+
+ final Widget? body;
+ final bool extendBody;
+ final bool extendBodyBehindAppBar;
+ final PreferredSizeWidget? appBar;
+ final Widget? floatingActionButton;
+ final FloatingActionButtonLocation? floatingActionButtonLocation;
+ final FloatingActionButtonAnimator? floatingActionButtonAnimator;
+ final List? persistentFooterButtons;
+ final AlignmentDirectional persistentFooterAlignment;
+ final Widget? drawer;
+ final DrawerCallback? onDrawerChanged;
+ final Widget? endDrawer;
+ final DrawerCallback? onEndDrawerChanged;
+ final Color? drawerScrimColor;
+ final Color? backgroundColor;
+ final Widget? bottomNavigationBar;
+ final bool? resizeToAvoidBottomInset;
+ final bool primary;
+ final DragStartBehavior drawerDragStartBehavior;
+ final double? drawerEdgeDragWidth;
+ final bool drawerEnableOpenDragGesture;
+ final bool endDrawerEnableOpenDragGesture;
+ final String? restorationId;
+ final Config? config;
+ final List? initSelectedMedia;
+ final List? extraRecentMedia;
+ final bool singleMedia;
+ final Function(List selectedMedia) onSelect;
+ final Widget Function(String tag, MediaFile media, BuildContext context)?
+ heroBuilder;
+ final Widget Function(List media, BuildContext context)?
+ multipleMediaBuilder;
+ @override
+ Widget build(BuildContext context) {
+ return BottomSheetScaffold(
+ extendBody: extendBody,
+ extendBodyBehindAppBar: extendBodyBehindAppBar,
+ appBar: appBar,
+ floatingActionButton: floatingActionButton,
+ floatingActionButtonAnimator: floatingActionButtonAnimator,
+ floatingActionButtonLocation: floatingActionButtonLocation,
+ persistentFooterAlignment: persistentFooterAlignment,
+ persistentFooterButtons: persistentFooterButtons,
+ drawer: drawer,
+ onDrawerChanged: onDrawerChanged,
+ endDrawer: endDrawer,
+ onEndDrawerChanged: onEndDrawerChanged,
+ drawerDragStartBehavior: drawerDragStartBehavior,
+ drawerEdgeDragWidth: drawerEdgeDragWidth,
+ drawerEnableOpenDragGesture: drawerEnableOpenDragGesture,
+ drawerScrimColor: drawerScrimColor,
+ endDrawerEnableOpenDragGesture: endDrawerEnableOpenDragGesture,
+ resizeToAvoidBottomInset: resizeToAvoidBottomInset,
+ restorationId: restorationId,
+ primary: primary,
+ backgroundColor: backgroundColor,
+ bottomNavigationBar: bottomNavigationBar,
+ body: body,
+ bottomSheet: DraggableBottomSheet(
+ draggableBody: true,
+ maxHeight: MediaQuery.of(context).size.height,
+ onHide: () {
+ if (GetInstance().isRegistered()) {
+ Get.find().resetBottomSheetView();
+ }
+ },
+ body: SizedBox(
+ width: MediaQuery.of(context).size.width,
+ height: MediaQuery.of(context).size.height,
+ child: GalleryPickerView(
+ onSelect: onSelect,
+ config: config,
+ heroBuilder: heroBuilder,
+ multipleMediaBuilder: multipleMediaBuilder,
+ singleMedia: singleMedia,
+ isBottomSheet: true,
+ initSelectedMedia: initSelectedMedia,
+ extraRecentMedia: extraRecentMedia,
+ startWithRecent: true,
+ )),
+ ),
+ );
+ }
+}
diff --git a/lib/views/thumbnail_media_file.dart b/lib/views/thumbnail_media_file.dart
index 557707a..3528c1b 100644
--- a/lib/views/thumbnail_media_file.dart
+++ b/lib/views/thumbnail_media_file.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import '../controller/gallery_controller.dart';
+import '../functions/color.dart';
import '../models/mode.dart';
import '/models/media_file.dart';
import 'package:transparent_image/transparent_image.dart';
@@ -8,12 +9,29 @@ class ThumbnailMediaFile extends StatelessWidget {
final MediaFile file;
final Color failIconColor;
final PhoneGalleryController controller;
- final bool isCollapsedSheet;
+ final BoxFit fit;
+ final double? width, height;
+ final double radius, borderWidth;
+ final Color borderColor;
+ final bool noIcon, noSelectedIcon;
+ final bool highQuality;
+ final Function()? onTap;
+ final Function()? onLongPress;
const ThumbnailMediaFile(
{super.key,
+ this.fit = BoxFit.cover,
required this.file,
+ this.width,
+ this.onLongPress,
+ this.onTap,
+ this.height,
+ this.radius = 0,
+ this.noIcon = false,
+ this.noSelectedIcon = false,
+ this.highQuality = true,
+ this.borderColor = Colors.transparent,
+ this.borderWidth = 0,
required this.failIconColor,
- required this.isCollapsedSheet,
required this.controller});
Color adjustFailedBgColor() {
@@ -26,70 +44,99 @@ class ThumbnailMediaFile extends StatelessWidget {
}
}
- Color darken(Color color, [double amount = .03]) {
- assert(amount >= 0 && amount <= 1);
- final hsl = HSLColor.fromColor(color);
- final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
- return hslDark.toColor();
- }
-
- Color lighten(Color color, [double amount = .05]) {
- assert(amount >= 0 && amount <= 1);
- final hsl = HSLColor.fromColor(color);
- final hslLight =
- hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
- return hslLight.toColor();
- }
-
@override
Widget build(BuildContext context) {
return FutureBuilder(
- future: file.thumbnail == null ? file.getThumbnail() : null,
+ future: file.thumbnail == null
+ ? file.getThumbnail(highQuality: highQuality)
+ : null,
builder: (context, snapshot) {
- return Stack(
- fit: StackFit.passthrough,
- children: [
- if (file.thumbnailFailed)
- Container(
- color: adjustFailedBgColor(),
- child: Icon(
- file.isImage
- ? Icons.image_not_supported
- : Icons.videocam_off_rounded,
- size: 50,
- color: failIconColor,
- ))
- else if (file.thumbnail != null &&
- !isCollapsedSheet &&
- controller.heroBuilder != null)
- Hero(
- tag: file.medium.id,
- child: FadeInImage(
- fadeInDuration: const Duration(milliseconds: 200),
- fit: BoxFit.cover,
- placeholder: MemoryImage(kTransparentImage),
- image: MemoryImage(file.thumbnail!),
- ),
- )
- else if (file.thumbnail != null && controller.heroBuilder == null)
- FadeInImage(
- fadeInDuration: const Duration(milliseconds: 200),
- fit: BoxFit.cover,
- placeholder: MemoryImage(kTransparentImage),
- image: MemoryImage(file.thumbnail!),
- )
- else
- const SizedBox(),
- if (file.thumbnail != null && !file.thumbnailFailed)
- Positioned(
- bottom: 10,
- left: 10,
- child: Icon(
- file.isVideo ? Icons.video_camera_back : null,
- color: Colors.white,
- size: 20,
- )),
- ],
+ return GestureDetector(
+ onTap: onTap != null
+ ? () {
+ onTap!();
+ }
+ : null,
+ onLongPress: onLongPress != null
+ ? () {
+ onLongPress!();
+ }
+ : null,
+ child: Container(
+ width: width,
+ height: height,
+ decoration: BoxDecoration(
+ color: adjustFailedBgColor(),
+ borderRadius: BorderRadius.circular(radius),
+ border: Border.all(color: borderColor, width: borderWidth)),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(radius),
+ child: Stack(
+ fit: StackFit.passthrough,
+ children: [
+ if (snapshot.hasError)
+ Center(
+ child: Icon(
+ file.isImage
+ ? Icons.image_not_supported
+ : Icons.videocam_off_rounded,
+ size: 50,
+ color: failIconColor,
+ ),
+ )
+ else if (file.thumbnail != null &&
+ controller.heroBuilder != null)
+ Hero(
+ tag: file.id,
+ child: FadeInImage(
+ width: width,
+ height: height,
+ fadeInDuration: const Duration(milliseconds: 200),
+ fit: fit,
+ placeholder: MemoryImage(kTransparentImage),
+ image: MemoryImage(file.thumbnail!),
+ ),
+ )
+ else if (file.thumbnail != null &&
+ controller.heroBuilder == null)
+ FadeInImage(
+ width: width,
+ height: height,
+ fadeInDuration: const Duration(milliseconds: 200),
+ fit: fit,
+ placeholder: MemoryImage(kTransparentImage),
+ image: MemoryImage(file.thumbnail!),
+ )
+ else
+ SizedBox(
+ width: width,
+ height: height,
+ ),
+ if (!noIcon && file.thumbnail != null)
+ Positioned(
+ bottom: 10,
+ left: 10,
+ child: Icon(
+ file.isVideo ? Icons.video_camera_back : null,
+ color: Colors.white,
+ size: 20,
+ )),
+ if (!noSelectedIcon && controller.isSelectedMedia(file))
+ Opacity(
+ opacity: 0.5,
+ child: Container(
+ color: Colors.black,
+ child: const Icon(
+ Icons.check,
+ color: Colors.white,
+ size: 45,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
);
});
}
diff --git a/pubspec.yaml b/pubspec.yaml
index cb2935e..b94aa93 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: gallery_picker
description: Gallery Picker is a flutter package that will allow you to pick media file(s), manage and navigate inside your gallery with modern tools and views.
-version: 0.2.3
+version: 0.3.0
homepage: https://github.com/FlutterWay/gallery_picker
environment:
@@ -18,7 +18,8 @@ dependencies:
get: ^4.6.5
video_thumbnail: ^0.5.3
intl: ^0.18.0
- bottom_sheet_bar: ^2.3.8
+ bottom_sheet_scaffold: ^0.1.1
+ page_transition: ^2.0.9
dev_dependencies:
flutter_test:
sdk: flutter