upgrading package to 0.3.0
This commit is contained in:
parent
24acdf8401
commit
3d84689c77
@ -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"}
|
{"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"}
|
@ -93,4 +93,10 @@
|
|||||||
|
|
||||||
## 0.2.3
|
## 0.2.3
|
||||||
|
|
||||||
* thumbnail's resolotion upgraded
|
* thumbnail's resolotion upgraded
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
|
* Package performance has been improved
|
||||||
|
* BottomSheetLayout changed into PickerScaffold
|
||||||
|
* PermissionDeniedPage added
|
85
README.md
85
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.
|
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.
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/galleryPickerSlide.png" width="1200"/>
|
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_views/gallery_picker_poster.png" width="1200"/>
|
||||||
|
|
||||||
## Features
|
## 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)
|
[✔] Examples provided (example/lib/examples)
|
||||||
|
|
||||||
|
[✔] Permission requests handled within the library
|
||||||
|
|
||||||
[✔] Null-safety
|
[✔] Null-safety
|
||||||
|
|
||||||
You could find the code samples of the given gifs below in `/example/lib/examples` folder.
|
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
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="text-align: center">
|
<td style="text-align: center">
|
||||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_light.gif" width="200"/>
|
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_views/gallery_picker_light.gif" width="200"/>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: center">
|
<td style="text-align: center">
|
||||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_dark.gif" width="200"/>
|
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_views/gallery_picker_dark.gif" width="200"/>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: center">
|
<td style="text-align: center">
|
||||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_destination.gif" width="200" />
|
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_views/gallery_picker_destination.gif" width="200" />
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: center">
|
<td style="text-align: center">
|
||||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/camera_page.gif" width="200" />
|
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_views/camera_page.gif" width="200" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@ -107,24 +109,23 @@ Dispose listener
|
|||||||
GalleryPicker.disposeSelectedFilesListener();
|
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.
|
There is an example at `example/lib/examples/bottom_sheet_example.dart` to see how it could be done.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return PickerScaffold(
|
||||||
appBar: AppBar(
|
backgroundColor: Colors.transparent,
|
||||||
title: Text(widget.title),
|
onSelect: (media) {},
|
||||||
),
|
initSelectedMedia: initMedia,
|
||||||
body: BottomSheetLayout(
|
config: Config(mode: Mode.dark),
|
||||||
config: Config()
|
body: Container(),
|
||||||
onSelect: (media) {},
|
)
|
||||||
child: Column(
|
}
|
||||||
children: [
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Customizable destination page
|
### Customizable destination page
|
||||||
@ -225,6 +226,7 @@ List<MediaFile>? media = await GalleryPicker.pickMedia(
|
|||||||
context: context,
|
context: context,
|
||||||
config: Config(
|
config: Config(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
|
permissionDeniedPage:PermissionDeniedPage(),
|
||||||
appbarColor: Colors.white,
|
appbarColor: Colors.white,
|
||||||
bottomSheetColor: const Color.fromARGB(255, 247, 248, 250),
|
bottomSheetColor: const Color.fromARGB(255, 247, 248, 250),
|
||||||
appbarIconColor: const Color.fromARGB(255, 130, 141, 148),
|
appbarIconColor: const Color.fromARGB(255, 130, 141, 148),
|
||||||
@ -326,6 +328,19 @@ GalleryPicker returns MediaFile list. You can reach out features below.
|
|||||||
[✔] getData function
|
[✔] getData function
|
||||||
[✔] Check if the file selected in gallery picker
|
[✔] 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.
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_views/permission_denied.gif" width="200" />
|
||||||
|
|
||||||
|
### Customizing Permission Denied Page
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Config(
|
||||||
|
permissionDeniedPage: PermissionDeniedPage(),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
## Ready-to-use widgets
|
## Ready-to-use widgets
|
||||||
|
|
||||||
### ThumbnailMedia
|
### 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
|
## Examples
|
||||||
Check out our examples!
|
Check out our examples!
|
||||||
### Standart Gallery Picker
|
### Standart Gallery Picker
|
||||||
|
@ -15,136 +15,135 @@ class _BottomSheetExampleState extends State<BottomSheetExample> {
|
|||||||
var controller = PageController(initialPage: 0);
|
var controller = PageController(initialPage: 0);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return PickerScaffold(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
body: BottomSheetLayout(
|
onSelect: (List<MediaFile> selectedMedias) {
|
||||||
onSelect: (List<MediaFile> selectedMedias) {
|
this.selectedMedias = selectedMedias;
|
||||||
this.selectedMedias = selectedMedias;
|
pageIndex = 0;
|
||||||
pageIndex = 0;
|
if (this.selectedMedias.isNotEmpty) {
|
||||||
if (this.selectedMedias.isNotEmpty) {
|
Future.delayed(const Duration(milliseconds: 500)).then((value) {
|
||||||
Future.delayed(const Duration(milliseconds: 500)).then((value) {
|
controller.animateToPage(0,
|
||||||
controller.animateToPage(0,
|
duration: const Duration(milliseconds: 500),
|
||||||
duration: const Duration(milliseconds: 500),
|
curve: Curves.easeIn);
|
||||||
curve: Curves.easeIn);
|
});
|
||||||
});
|
}
|
||||||
}
|
setState(() {});
|
||||||
setState(() {});
|
},
|
||||||
},
|
config: Config(mode: Mode.dark),
|
||||||
config: Config(mode: Mode.dark),
|
body: Center(
|
||||||
child: Center(
|
child: Column(
|
||||||
child: Column(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: <Widget>[
|
||||||
children: <Widget>[
|
const Spacer(),
|
||||||
const Spacer(),
|
const Text(
|
||||||
const Text(
|
'These are your selected medias',
|
||||||
'These are your selected medias',
|
style: TextStyle(color: Colors.black),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 5,
|
flex: 5,
|
||||||
child: Stack(children: [
|
child: Stack(children: [
|
||||||
if (selectedMedias.isNotEmpty)
|
if (selectedMedias.isNotEmpty)
|
||||||
PageView(
|
PageView(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
children: [
|
children: [
|
||||||
for (var media in selectedMedias)
|
for (var media in selectedMedias)
|
||||||
Center(
|
Center(
|
||||||
child: MediaProvider(
|
child: MediaProvider(
|
||||||
media: media,
|
media: media,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (selectedMedias.isNotEmpty)
|
if (selectedMedias.isNotEmpty)
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (pageIndex < selectedMedias.length - 1) {
|
if (pageIndex < selectedMedias.length - 1) {
|
||||||
pageIndex++;
|
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;
|
|
||||||
controller.animateToPage(pageIndex,
|
controller.animateToPage(pageIndex,
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
curve: Curves.easeIn);
|
curve: Curves.easeIn);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
}
|
||||||
child: Container(
|
},
|
||||||
width: 65,
|
child: const Icon(
|
||||||
height: 50,
|
Icons.chevron_right,
|
||||||
decoration: BoxDecoration(
|
size: 100,
|
||||||
border: Border.all(
|
color: Colors.red,
|
||||||
width: 2,
|
)),
|
||||||
color: pageIndex == i
|
),
|
||||||
? Colors.red
|
if (selectedMedias.isNotEmpty)
|
||||||
: Colors.black)),
|
Align(
|
||||||
child: ThumbnailMedia(
|
alignment: Alignment.centerLeft,
|
||||||
media: selectedMedias[i],
|
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: () {
|
const Spacer(
|
||||||
GalleryPicker.openSheet();
|
flex: 1,
|
||||||
},
|
),
|
||||||
child: const Icon(
|
],
|
||||||
Icons.open_in_new,
|
|
||||||
size: 40,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(
|
|
||||||
flex: 1,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -17,20 +17,22 @@ class _WhatsappPickPhotoState extends State<WhatsappPickPhoto> {
|
|||||||
GalleryMedia? gallery;
|
GalleryMedia? gallery;
|
||||||
List<MediaFile> selectedMedias = [];
|
List<MediaFile> selectedMedias = [];
|
||||||
List<CameraDescription>? cameras;
|
List<CameraDescription>? cameras;
|
||||||
CameraLensDirection cameraLensDirection = CameraLensDirection.front;
|
CameraLensDirection cameraLensDirection = CameraLensDirection.back;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
initCamera();
|
initCamera();
|
||||||
fetchMedias();
|
fetchMedias();
|
||||||
GalleryPicker.listenSelectedFiles.listen((medias) {
|
GalleryPicker.listenSelectedFiles.listen((medias) {
|
||||||
selectedMedias = medias;
|
selectedMedias = medias;
|
||||||
setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchMedias() async {
|
Future<void> fetchMedias() async {
|
||||||
gallery = await GalleryPicker.collectGallery;
|
gallery = await GalleryPicker.initializeGallery;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,211 +74,207 @@ class _WhatsappPickPhotoState extends State<WhatsappPickPhoto> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return PickerScaffold(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
body: BottomSheetLayout(
|
onSelect: (List<MediaFile> selectedMedias) {
|
||||||
onSelect: (List<MediaFile> selectedMedias) {
|
this.selectedMedias = selectedMedias;
|
||||||
this.selectedMedias = selectedMedias;
|
if (mounted) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
}
|
||||||
initSelectedMedia: selectedMedias,
|
},
|
||||||
config: Config(mode: Mode.dark),
|
initSelectedMedia: selectedMedias,
|
||||||
child: Stack(
|
config: Config(mode: Mode.dark),
|
||||||
children: [
|
body: Stack(
|
||||||
if (cameraController != null &&
|
children: [
|
||||||
cameraController!.value.isInitialized)
|
if (cameraController != null && cameraController!.value.isInitialized)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: MediaQuery.of(context).size.height,
|
height: MediaQuery.of(context).size.height,
|
||||||
child: CameraPreview(
|
child: CameraPreview(
|
||||||
cameraController!,
|
cameraController!,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (gallery != null && gallery!.recent != null)
|
),
|
||||||
Positioned(
|
if (gallery != null && gallery!.recent != null)
|
||||||
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,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 20,
|
bottom: 100,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
child: Row(
|
height: 65,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
child: ListView(
|
||||||
children: [
|
scrollDirection: Axis.horizontal,
|
||||||
TextButton(
|
children: [
|
||||||
onPressed: () {
|
for (var media in gallery!.recent!.files)
|
||||||
GalleryPicker.openSheet();
|
Padding(
|
||||||
},
|
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||||
child: Container(
|
child: GestureDetector(
|
||||||
decoration: BoxDecoration(
|
onTap: () {
|
||||||
color: Colors.black.withOpacity(0.5),
|
if (selectedMedias.isEmpty) {
|
||||||
shape: BoxShape.circle),
|
Navigator.push(
|
||||||
padding: const EdgeInsets.all(8),
|
context,
|
||||||
child: const Icon(
|
MaterialPageRoute(
|
||||||
Icons.image,
|
builder: (context) =>
|
||||||
size: 20,
|
MultipleMediasView([media])),
|
||||||
color: Colors.white,
|
);
|
||||||
),
|
} else {
|
||||||
),
|
selectedMedias
|
||||||
),
|
.any((element) => element.id == media.id)
|
||||||
GestureDetector(
|
? selectedMedias.removeWhere(
|
||||||
onTap: () async {
|
(element) => element.id == media.id)
|
||||||
setState(() {
|
: selectedMedias.add(media);
|
||||||
anyProcess = true;
|
setState(() {});
|
||||||
});
|
}
|
||||||
Future.delayed(const Duration(milliseconds: 100))
|
},
|
||||||
.then((value) => setState(() {
|
onLongPress: () {
|
||||||
anyProcess = false;
|
if (selectedMedias
|
||||||
}));
|
.any((element) => element.id == media.id)) {
|
||||||
},
|
selectedMedias.removeWhere(
|
||||||
onLongPressStart: (value) {
|
(element) => element.id == media.id);
|
||||||
setState(() {
|
} else {
|
||||||
isRecording = true;
|
selectedMedias.add(media);
|
||||||
anyProcess = true;
|
}
|
||||||
});
|
setState(() {});
|
||||||
},
|
},
|
||||||
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(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
width: 65,
|
||||||
color:
|
height: 65,
|
||||||
anyProcess ? Colors.red : Colors.transparent,
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
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: () {
|
GestureDetector(
|
||||||
if (cameraLensDirection ==
|
onTap: () async {
|
||||||
CameraLensDirection.front) {
|
setState(() {
|
||||||
cameraLensDirection = CameraLensDirection.back;
|
anyProcess = true;
|
||||||
} else {
|
});
|
||||||
cameraLensDirection = CameraLensDirection.front;
|
Future.delayed(const Duration(milliseconds: 100))
|
||||||
}
|
.then((value) => setState(() {
|
||||||
initCamera();
|
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(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withOpacity(0.5),
|
color: anyProcess ? Colors.red : Colors.transparent,
|
||||||
shape: BoxShape.circle),
|
shape: BoxShape.circle,
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.cameraswitch,
|
|
||||||
size: 20,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
bottom_sheet_bar:
|
bottom_sheet_scaffold:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: bottom_sheet_bar
|
name: bottom_sheet_scaffold
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.8"
|
version: "0.1.1"
|
||||||
camera:
|
camera:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -141,7 +141,7 @@ packages:
|
|||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.2.3"
|
version: "0.3.0"
|
||||||
get:
|
get:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -191,13 +191,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.5"
|
version: "0.1.5"
|
||||||
measure_size:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: measure_size
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "4.0.0"
|
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -205,6 +198,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.0"
|
||||||
|
page_transition:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: page_transition
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.9"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -379,5 +379,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.3"
|
version: "0.5.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.18.5 <3.0.0"
|
dart: ">=2.18.6 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.0.0"
|
||||||
|
@ -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<void> open() async {
|
|
||||||
await sheetController.expand();
|
|
||||||
}
|
|
||||||
|
|
||||||
void tapingStatus(bool value) {
|
|
||||||
appBarTapping = value;
|
|
||||||
if (galleryController == null) {
|
|
||||||
Get.find<PhoneGalleryController>().update();
|
|
||||||
} else {
|
|
||||||
galleryController!.update();
|
|
||||||
}
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> close() async {
|
|
||||||
isClosing = true;
|
|
||||||
update();
|
|
||||||
await sheetController.collapse();
|
|
||||||
isClosing = false;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void disposeController() {
|
|
||||||
isClosing = false;
|
|
||||||
appBarTapping = false;
|
|
||||||
GetInstance().delete<BottomSheetController>();
|
|
||||||
}
|
|
||||||
|
|
||||||
BottomSheetController(this.sheetController);
|
|
||||||
}
|
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -15,13 +16,22 @@ import 'picker_listener.dart';
|
|||||||
class PhoneGalleryController extends GetxController {
|
class PhoneGalleryController extends GetxController {
|
||||||
late Config config;
|
late Config config;
|
||||||
|
|
||||||
PhoneGalleryController(Config? config,
|
void configuration(Config? config,
|
||||||
{required this.onSelect,
|
{required dynamic Function(List<MediaFile>) onSelect,
|
||||||
required this.heroBuilder,
|
required Widget Function(String, MediaFile, BuildContext)? heroBuilder,
|
||||||
required this.isRecent,
|
required bool isRecent,
|
||||||
|
required bool startWithRecent,
|
||||||
required List<MediaFile>? initSelectedMedias,
|
required List<MediaFile>? initSelectedMedias,
|
||||||
required List<MediaFile>? extraRecentMedia,
|
required List<MediaFile>? extraRecentMedia,
|
||||||
required this.multipleMediasBuilder}) {
|
required Widget Function(List<MediaFile>, 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();
|
this.config = config ?? Config();
|
||||||
if (initSelectedMedias != null) {
|
if (initSelectedMedias != null) {
|
||||||
_selectedFiles = initSelectedMedias.map((e) => e).toList();
|
_selectedFiles = initSelectedMedias.map((e) => e).toList();
|
||||||
@ -32,16 +42,21 @@ class PhoneGalleryController extends GetxController {
|
|||||||
if (selectedFiles.isNotEmpty) {
|
if (selectedFiles.isNotEmpty) {
|
||||||
_pickerMode = true;
|
_pickerMode = true;
|
||||||
}
|
}
|
||||||
|
configurationCompleted = true;
|
||||||
}
|
}
|
||||||
bool isRecent;
|
|
||||||
Function(List<MediaFile> selectedMedias) onSelect;
|
late bool startWithRecent;
|
||||||
|
late bool isRecent;
|
||||||
|
bool permissionGranted = false;
|
||||||
|
bool configurationCompleted = false;
|
||||||
|
late Function(List<MediaFile> selectedMedias) onSelect;
|
||||||
Widget Function(String tag, MediaFile media, BuildContext context)?
|
Widget Function(String tag, MediaFile media, BuildContext context)?
|
||||||
heroBuilder;
|
heroBuilder;
|
||||||
Widget Function(List<MediaFile> medias, BuildContext context)?
|
Widget Function(List<MediaFile> medias, BuildContext context)?
|
||||||
multipleMediasBuilder;
|
multipleMediasBuilder;
|
||||||
GalleryAlbum? selectedAlbum;
|
GalleryMedia? _media;
|
||||||
List<GalleryAlbum> _galleryAlbums = [];
|
GalleryMedia? get media => _media;
|
||||||
List<GalleryAlbum> get galleryAlbums => _galleryAlbums;
|
List<GalleryAlbum> get galleryAlbums => _media == null ? [] : _media!.albums;
|
||||||
List<MediaFile> _selectedFiles = [];
|
List<MediaFile> _selectedFiles = [];
|
||||||
List<MediaFile>? _extraRecentMedia;
|
List<MediaFile>? _extraRecentMedia;
|
||||||
List<MediaFile> get selectedFiles => _selectedFiles;
|
List<MediaFile> get selectedFiles => _selectedFiles;
|
||||||
@ -50,6 +65,26 @@ class PhoneGalleryController extends GetxController {
|
|||||||
List<MediaFile>? get extraRecentMedia => _extraRecentMedia;
|
List<MediaFile>? get extraRecentMedia => _extraRecentMedia;
|
||||||
bool _pickerMode = false;
|
bool _pickerMode = false;
|
||||||
bool get pickerMode => _pickerMode;
|
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<MediaFile> media) {
|
void updateSelectedFiles(List<MediaFile> media) {
|
||||||
_selectedFiles = media.map((e) => e).toList();
|
_selectedFiles = media.map((e) => e).toList();
|
||||||
@ -69,11 +104,26 @@ class PhoneGalleryController extends GetxController {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeAlbum(GalleryAlbum? album) {
|
Future<void> changeAlbum(
|
||||||
selectedAlbum = album;
|
{required GalleryAlbum album,
|
||||||
|
required BuildContext context,
|
||||||
|
required PhoneGalleryController controller,
|
||||||
|
required bool singleMedia,
|
||||||
|
required bool isBottomSheet}) async {
|
||||||
_selectedFiles.clear();
|
_selectedFiles.clear();
|
||||||
update();
|
selectedAlbum = album;
|
||||||
updatePickerListener();
|
updatePickerListener();
|
||||||
|
await pageController.animateToPage(1,
|
||||||
|
duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> 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) {
|
void unselectMedia(MediaFile file) {
|
||||||
@ -99,10 +149,10 @@ class PhoneGalleryController extends GetxController {
|
|||||||
void switchPickerMode(bool value) {
|
void switchPickerMode(bool value) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
_selectedFiles.clear();
|
_selectedFiles.clear();
|
||||||
|
updatePickerListener();
|
||||||
}
|
}
|
||||||
_pickerMode = value;
|
_pickerMode = value;
|
||||||
update();
|
update();
|
||||||
updatePickerListener();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePickerListener() {
|
void updatePickerListener() {
|
||||||
@ -112,19 +162,33 @@ class PhoneGalleryController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool> promptPermissionSetting() async {
|
static Future<bool> promptPermissionSetting() async {
|
||||||
|
await PhoneGalleryController.requestStatus(Permission.storage);
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
await PhoneGalleryController.requestStatus(Permission.photos);
|
||||||
|
}
|
||||||
if (Platform.isIOS &&
|
if (Platform.isIOS &&
|
||||||
await Permission.storage.request().isGranted &&
|
await Permission.storage.isGranted &&
|
||||||
await Permission.photos.request().isGranted ||
|
await Permission.photos.isGranted ||
|
||||||
Platform.isAndroid && await Permission.storage.request().isGranted) {
|
Platform.isAndroid && await Permission.storage.isGranted) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> requestStatus(Permission permission) async {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
await permission.request();
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 500), () {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> initializeAlbums() async {
|
Future<void> initializeAlbums() async {
|
||||||
GalleryMedia? media = await PhoneGalleryController.collectGallery;
|
_media = await PhoneGalleryController.collectGallery;
|
||||||
if (media != null) {
|
if (_media != null) {
|
||||||
_galleryAlbums = media.albums;
|
|
||||||
if (_extraRecentMedia != null) {
|
if (_extraRecentMedia != null) {
|
||||||
GalleryAlbum? recentTmp = recent;
|
GalleryAlbum? recentTmp = recent;
|
||||||
if (recentTmp != null) {
|
if (recentTmp != null) {
|
||||||
@ -132,12 +196,24 @@ class PhoneGalleryController extends GetxController {
|
|||||||
recentTmp.files.any((file) => element.id == file.id));
|
recentTmp.files.any((file) => element.id == file.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
permissionGranted = true;
|
||||||
|
_isInitialized = true;
|
||||||
|
} else {
|
||||||
|
permissionGranted = false;
|
||||||
|
permissionListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
_isInitialized = true;
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void permissionListener() {
|
||||||
|
Timer.periodic(const Duration(seconds: 1), (timer) async {
|
||||||
|
if (await Permission.storage.isGranted) {
|
||||||
|
initializeAlbums();
|
||||||
|
timer.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static Future<GalleryMedia?> get collectGallery async {
|
static Future<GalleryMedia?> get collectGallery async {
|
||||||
if (await promptPermissionSetting()) {
|
if (await promptPermissionSetting()) {
|
||||||
List<GalleryAlbum> tempGalleryAlbums = [];
|
List<GalleryAlbum> tempGalleryAlbums = [];
|
||||||
@ -205,8 +281,8 @@ class PhoneGalleryController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GalleryAlbum? get recent {
|
GalleryAlbum? get recent {
|
||||||
return _galleryAlbums.isNotEmpty
|
return galleryAlbums.isNotEmpty
|
||||||
? _galleryAlbums.singleWhere((element) => element.album.name == "All")
|
? galleryAlbums.singleWhere((element) => element.album.name == "All")
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
//GalleryAlbum? get recent {
|
//GalleryAlbum? get recent {
|
||||||
@ -245,14 +321,13 @@ class PhoneGalleryController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isSelectedMedia(MediaFile file) {
|
bool isSelectedMedia(MediaFile file) {
|
||||||
return _selectedFiles.any((element) => element.medium.id == file.medium.id);
|
return _selectedFiles.any((element) => element.id == file.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void disposeController() {
|
void disposeController() {
|
||||||
_galleryAlbums = [];
|
_media = null;
|
||||||
_selectedFiles = [];
|
_selectedFiles = [];
|
||||||
_isInitialized = false;
|
_isInitialized = false;
|
||||||
selectedAlbum = null;
|
|
||||||
Get.delete<PhoneGalleryController>();
|
Get.delete<PhoneGalleryController>();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
15
lib/functions/color.dart
Normal file
15
lib/functions/color.dart
Normal file
@ -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();
|
||||||
|
}
|
@ -15,13 +15,13 @@ export 'user_widgets/gallery_picker_builder.dart';
|
|||||||
export 'user_widgets/photo_provider.dart';
|
export 'user_widgets/photo_provider.dart';
|
||||||
export 'user_widgets/video_provider.dart';
|
export 'user_widgets/video_provider.dart';
|
||||||
export 'user_widgets/media_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';
|
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:flutter/material.dart';
|
||||||
import 'package:gallery_picker/models/gallery_media.dart';
|
import 'package:gallery_picker/models/gallery_media.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import '../../controller/gallery_controller.dart';
|
import '../../controller/gallery_controller.dart';
|
||||||
import 'controller/bottom_sheet_controller.dart';
|
|
||||||
import 'controller/picker_listener.dart';
|
import 'controller/picker_listener.dart';
|
||||||
import 'models/config.dart';
|
import 'models/config.dart';
|
||||||
import 'models/media_file.dart';
|
import 'models/media_file.dart';
|
||||||
@ -43,9 +43,6 @@ class GalleryPicker {
|
|||||||
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
||||||
Get.find<PhoneGalleryController>().disposeController();
|
Get.find<PhoneGalleryController>().disposeController();
|
||||||
}
|
}
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
|
||||||
Get.find<BottomSheetController>().disposeController();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<MediaFile>?> pickMedia(
|
static Future<List<MediaFile>?> pickMedia(
|
||||||
@ -101,26 +98,35 @@ class GalleryPicker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> openSheet() async {
|
static Future<void> openSheet() async {
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
BottomSheetPanel.open();
|
||||||
await Get.find<BottomSheetController>().open();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> closeSheet() async {
|
static Future<void> closeSheet() async {
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
BottomSheetPanel.close();
|
||||||
await Get.find<BottomSheetController>().close();
|
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
||||||
|
Get.find<PhoneGalleryController>().resetBottomSheetView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool get isSheetOpened {
|
static bool get isSheetOpened {
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
return BottomSheetPanel.isOpen;
|
||||||
return Get.find<BottomSheetController>().sheetController.isExpanded;
|
}
|
||||||
} else {
|
|
||||||
return false;
|
static bool get isSheetExpanded {
|
||||||
}
|
return BottomSheetPanel.isExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get isSheetCollapsed {
|
||||||
|
return BottomSheetPanel.isCollapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<GalleryMedia?> get collectGallery async {
|
static Future<GalleryMedia?> get collectGallery async {
|
||||||
return await PhoneGalleryController.collectGallery;
|
return await PhoneGalleryController.collectGallery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<GalleryMedia?> get initializeGallery async {
|
||||||
|
final controller = Get.put(PhoneGalleryController());
|
||||||
|
await controller.initializeAlbums();
|
||||||
|
return controller.media;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import 'mode.dart';
|
|||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
late Widget selectIcon;
|
late Widget selectIcon;
|
||||||
|
Widget? permissionDeniedPage;
|
||||||
late Color backgroundColor,
|
late Color backgroundColor,
|
||||||
appbarColor,
|
appbarColor,
|
||||||
appbarIconColor,
|
appbarIconColor,
|
||||||
@ -33,6 +34,7 @@ class Config {
|
|||||||
TextStyle? unselectedMenuStyle,
|
TextStyle? unselectedMenuStyle,
|
||||||
TextStyle? textStyle,
|
TextStyle? textStyle,
|
||||||
TextStyle? appbarTextStyle,
|
TextStyle? appbarTextStyle,
|
||||||
|
this.permissionDeniedPage,
|
||||||
this.recents = "RECENTS",
|
this.recents = "RECENTS",
|
||||||
this.recent = "Recent",
|
this.recent = "Recent",
|
||||||
this.gallery = "GALLERY",
|
this.gallery = "GALLERY",
|
||||||
|
@ -10,7 +10,7 @@ import 'config.dart';
|
|||||||
|
|
||||||
class GalleryAlbum {
|
class GalleryAlbum {
|
||||||
late Album album;
|
late Album album;
|
||||||
late List<int>? thumbnail;
|
List<int>? thumbnail;
|
||||||
List<DateCategory> dateCategories = [];
|
List<DateCategory> dateCategories = [];
|
||||||
late AlbumType type;
|
late AlbumType type;
|
||||||
int get count =>
|
int get count =>
|
||||||
@ -49,8 +49,8 @@ class GalleryAlbum {
|
|||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
List<DateCategory> dateCategory = [];
|
List<DateCategory> dateCategory = [];
|
||||||
for (var medium in sortAlbumMediaDates((await album.listMedia()).items)) {
|
for (var medium in sortAlbumMediaDates((await album.listMedia()).items)) {
|
||||||
MediaFile mediaFile = MediaFile(medium: medium);
|
MediaFile mediaFile = MediaFile.medium(medium);
|
||||||
String name = getDateCategory(medium);
|
String name = getDateCategory(mediaFile);
|
||||||
if (dateCategory.any((element) => element.name == name)) {
|
if (dateCategory.any((element) => element.name == name)) {
|
||||||
dateCategory
|
dateCategory
|
||||||
.singleWhere((element) => element.name == name)
|
.singleWhere((element) => element.name == name)
|
||||||
@ -71,8 +71,9 @@ class GalleryAlbum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DateTime? get lastDate {
|
DateTime? get lastDate {
|
||||||
if (dateCategories.isNotEmpty) {
|
if (dateCategories.isNotEmpty &&
|
||||||
return dateCategories.first.files.first.medium.lastDate;
|
dateCategories.first.files.first.medium != null) {
|
||||||
|
return dateCategories.first.files.first.medium!.lastDate;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -81,23 +82,22 @@ class GalleryAlbum {
|
|||||||
List<MediaFile> get files =>
|
List<MediaFile> get files =>
|
||||||
dateCategories.expand((element) => element.files).toList();
|
dateCategories.expand((element) => element.files).toList();
|
||||||
|
|
||||||
String getDateCategory(Medium mediaFile) {
|
String getDateCategory(MediaFile media) {
|
||||||
Config config = GetInstance().isRegistered<PhoneGalleryController>()
|
Config config = GetInstance().isRegistered<PhoneGalleryController>()
|
||||||
? Get.find<PhoneGalleryController>().config
|
? Get.find<PhoneGalleryController>().config
|
||||||
: Config();
|
: Config();
|
||||||
if (daysBetween(mediaFile.lastDate!) <= 3) {
|
DateTime? lastDate = media.lastModified;
|
||||||
|
lastDate = lastDate ?? DateTime.now();
|
||||||
|
if (daysBetween(lastDate) <= 3) {
|
||||||
return config.recent;
|
return config.recent;
|
||||||
} else if (daysBetween(mediaFile.lastDate!) > 3 &&
|
} else if (daysBetween(lastDate) > 3 && daysBetween(lastDate) <= 7) {
|
||||||
daysBetween(mediaFile.lastDate!) <= 7) {
|
|
||||||
return config.lastWeek;
|
return config.lastWeek;
|
||||||
} else if (daysBetween(mediaFile.lastDate!) > 7 &&
|
} else if (daysBetween(lastDate) > 7 && daysBetween(lastDate) <= 31) {
|
||||||
daysBetween(mediaFile.lastDate!) <= 31) {
|
|
||||||
return config.lastMonth;
|
return config.lastMonth;
|
||||||
} else if (daysBetween(mediaFile.lastDate!) > 31 &&
|
} else if (daysBetween(lastDate) > 31 && daysBetween(lastDate) <= 365) {
|
||||||
daysBetween(mediaFile.lastDate!) <= 365) {
|
return DateFormat.MMMM().format(lastDate).toString();
|
||||||
return DateFormat.MMMM().format(mediaFile.lastDate!).toString();
|
|
||||||
} else {
|
} 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) {
|
} else if (b.lastDate == null) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return a.lastDate!.compareTo(b.lastDate!);
|
return b.lastDate!.compareTo(a.lastDate!);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return mediumList;
|
return mediumList;
|
||||||
@ -125,19 +125,19 @@ class GalleryAlbum {
|
|||||||
|
|
||||||
for (var category in dateCategories) {
|
for (var category in dateCategories) {
|
||||||
category.files.sort((a, b) {
|
category.files.sort((a, b) {
|
||||||
if (a.medium.lastDate == null) {
|
if (a.medium == null) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (b.medium.lastDate == null) {
|
} else if (b.medium == null) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return b.medium.lastDate!.compareTo(a.medium.lastDate!);
|
return b.medium!.lastDate!.compareTo(a.medium!.lastDate!);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addFile(MediaFile file) {
|
void addFile(MediaFile file) {
|
||||||
String name = getDateCategory(file.medium);
|
String name = getDateCategory(file);
|
||||||
if (dateCategories.any((element) => element.name == name)) {
|
if (dateCategories.any((element) => element.name == name)) {
|
||||||
dateCategories
|
dateCategories
|
||||||
.singleWhere((element) => element.name == name)
|
.singleWhere((element) => element.name == name)
|
||||||
|
@ -1,108 +1,66 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:photo_gallery/photo_gallery.dart';
|
import 'package:photo_gallery/photo_gallery.dart';
|
||||||
import 'package:video_thumbnail/video_thumbnail.dart';
|
import 'package:video_thumbnail/video_thumbnail.dart';
|
||||||
import '../controller/gallery_controller.dart';
|
|
||||||
|
|
||||||
enum MediaType { image, video }
|
enum MediaType { image, video }
|
||||||
|
|
||||||
class MediaFile {
|
class MediaFile {
|
||||||
late Medium medium;
|
Medium? _medium;
|
||||||
late MediaType type;
|
File? _file;
|
||||||
Uint8List? thumbnail;
|
Uint8List? thumbnail;
|
||||||
Uint8List? data;
|
late MediaType _type;
|
||||||
late String id;
|
late String _id;
|
||||||
bool thumbnailFailed = false;
|
bool get isVideo => _type == MediaType.video;
|
||||||
File? file;
|
bool get isImage => _type == MediaType.image;
|
||||||
bool _noMedium = false;
|
Medium? get medium => _medium;
|
||||||
bool get isVideo => type == MediaType.video;
|
MediaType get type => _type;
|
||||||
bool get isImage => type == MediaType.image;
|
String get id => _id;
|
||||||
|
File? get file => _file;
|
||||||
|
DateTime? get lastModified =>
|
||||||
|
_medium != null ? _medium!.modifiedDate : _file!.lastModifiedSync();
|
||||||
|
|
||||||
MediaFile({required this.medium}) {
|
MediaFile.medium(Medium medium) {
|
||||||
type = medium.mediumType == MediumType.video
|
_medium = medium;
|
||||||
|
_type = _medium!.mediumType == MediumType.video
|
||||||
? MediaType.video
|
? MediaType.video
|
||||||
: MediaType.image;
|
: MediaType.image;
|
||||||
id = medium.id;
|
_id = _medium!.id;
|
||||||
}
|
}
|
||||||
MediaFile.file({required this.id, required this.file, required this.type}) {
|
MediaFile.file(
|
||||||
_noMedium = true;
|
{required String id, required File file, required MediaType type}) {
|
||||||
medium = Medium(
|
_file = file;
|
||||||
id: id,
|
_id = id;
|
||||||
mediumType:
|
_type = type;
|
||||||
type == MediaType.image ? MediumType.image : MediumType.video);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List?> getThumbnail() async {
|
Future<Uint8List> getThumbnail({bool highQuality = true}) async {
|
||||||
if (thumbnail == null) {
|
if (_medium == null) {
|
||||||
try {
|
thumbnail = isVideo
|
||||||
if (_noMedium) {
|
? (await VideoThumbnail.thumbnailData(
|
||||||
thumbnail = isVideo
|
video: _file!.path,
|
||||||
? await VideoThumbnail.thumbnailData(
|
imageFormat: ImageFormat.JPEG,
|
||||||
video: file!.path,
|
quality: highQuality ? 100 : 20,
|
||||||
imageFormat: ImageFormat.JPEG,
|
))!
|
||||||
quality: 100,
|
: await getData();
|
||||||
)
|
} else {
|
||||||
: await getData();
|
thumbnail = Uint8List.fromList(
|
||||||
} else {
|
await _medium!.getThumbnail(highQuality: highQuality));
|
||||||
thumbnail =
|
|
||||||
Uint8List.fromList(await medium.getThumbnail(highQuality: true));
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
thumbnailFailed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return thumbnail;
|
return thumbnail!;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<File> getFile() async {
|
Future<File> getFile() async {
|
||||||
if (file == null) {
|
if (_medium != null) {
|
||||||
file = await medium.getFile();
|
return await _medium!.getFile();
|
||||||
return file!;
|
|
||||||
} else {
|
} else {
|
||||||
return file!;
|
return _file!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List> getData() async {
|
Future<Uint8List> getData() async {
|
||||||
if (file == null) {
|
_file ??= await getFile();
|
||||||
await getFile();
|
return _file!.readAsBytesSync();
|
||||||
}
|
|
||||||
data ??= await file!.readAsBytes();
|
|
||||||
|
|
||||||
return data!;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unselect({PhoneGalleryController? controller}) {
|
|
||||||
if (controller != null) {
|
|
||||||
controller.unselectMedia(this);
|
|
||||||
} else {
|
|
||||||
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
|
||||||
Get.find<PhoneGalleryController>().unselectMedia(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void select({PhoneGalleryController? controller}) {
|
|
||||||
if (controller != null) {
|
|
||||||
controller.selectMedia(this);
|
|
||||||
} else {
|
|
||||||
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
|
||||||
Get.find<PhoneGalleryController>().selectMedia(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool? isSelected({PhoneGalleryController? controller}) {
|
|
||||||
if (controller != null) {
|
|
||||||
return controller.isSelectedMedia(this);
|
|
||||||
} else {
|
|
||||||
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
|
||||||
return Get.find<PhoneGalleryController>().isSelectedMedia(this);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,45 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:transparent_image/transparent_image.dart';
|
||||||
import '../models/media_file.dart';
|
import '../models/media_file.dart';
|
||||||
|
|
||||||
class PhotoProvider extends StatefulWidget {
|
class PhotoProvider extends StatelessWidget {
|
||||||
final MediaFile media;
|
final MediaFile media;
|
||||||
final BoxFit fit;
|
final BoxFit fit;
|
||||||
final double? width, height;
|
final double? width, height;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
final Widget Function(BuildContext context)? onFailBuilder;
|
||||||
|
|
||||||
const PhotoProvider({
|
const PhotoProvider({
|
||||||
super.key,
|
super.key,
|
||||||
required this.media,
|
required this.media,
|
||||||
|
this.onFailBuilder,
|
||||||
this.fit = BoxFit.contain,
|
this.fit = BoxFit.contain,
|
||||||
|
this.backgroundColor,
|
||||||
this.width,
|
this.width,
|
||||||
this.height,
|
this.height,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
|
||||||
State<PhotoProvider> createState() => _PhotoProviderState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PhotoProviderState extends State<PhotoProvider> {
|
|
||||||
late MediaFile _media;
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
_media = widget.media;
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
initMedia();
|
|
||||||
});
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> initMedia() async {
|
|
||||||
await _media.getData();
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_media != widget.media) {
|
return FutureBuilder(
|
||||||
_media = widget.media;
|
future: media.getData(),
|
||||||
if (_media.data == null) {
|
builder: ((context, snapshot) {
|
||||||
initMedia();
|
return Container(
|
||||||
}
|
color: backgroundColor,
|
||||||
}
|
width: width,
|
||||||
return _media.data == null
|
height: height,
|
||||||
? SizedBox(
|
child: (snapshot.hasError && onFailBuilder != null)
|
||||||
width: widget.width,
|
? onFailBuilder!(context)
|
||||||
height: widget.height,
|
: (snapshot.hasData)
|
||||||
)
|
? FadeInImage(
|
||||||
: Image.memory(
|
fadeInDuration: const Duration(milliseconds: 200),
|
||||||
_media.data!,
|
fit: BoxFit.cover,
|
||||||
width: widget.width,
|
placeholder: MemoryImage(kTransparentImage),
|
||||||
height: widget.height,
|
image: MemoryImage(snapshot.data!),
|
||||||
fit: widget.fit,
|
)
|
||||||
);
|
: const SizedBox(),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:transparent_image/transparent_image.dart';
|
||||||
import '/models/gallery_album.dart';
|
import '/models/gallery_album.dart';
|
||||||
import '../models/mode.dart';
|
import '../models/mode.dart';
|
||||||
|
|
||||||
@ -58,9 +59,11 @@ class ThumbnailAlbum extends StatelessWidget {
|
|||||||
color: failIconColor,
|
color: failIconColor,
|
||||||
))
|
))
|
||||||
else if (album.thumbnail != null)
|
else if (album.thumbnail != null)
|
||||||
Image.memory(
|
FadeInImage(
|
||||||
Uint8List.fromList(album.thumbnail!),
|
image: MemoryImage(Uint8List.fromList(album.thumbnail!)),
|
||||||
|
fadeInDuration: const Duration(milliseconds: 200),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
placeholder: MemoryImage(kTransparentImage),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
const SizedBox(),
|
const SizedBox(),
|
||||||
|
@ -5,49 +5,83 @@ import 'package:transparent_image/transparent_image.dart';
|
|||||||
class ThumbnailMedia extends StatelessWidget {
|
class ThumbnailMedia extends StatelessWidget {
|
||||||
final MediaFile media;
|
final MediaFile media;
|
||||||
final bool noIcon;
|
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;
|
final Widget Function(MediaFile media, BuildContext context)? onErrorBuilder;
|
||||||
const ThumbnailMedia(
|
const ThumbnailMedia(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.media,
|
required this.media,
|
||||||
|
this.fit = BoxFit.cover,
|
||||||
this.onErrorBuilder,
|
this.onErrorBuilder,
|
||||||
|
this.radius = 0,
|
||||||
|
this.highQuality = true,
|
||||||
|
this.borderColor = Colors.transparent,
|
||||||
|
this.borderWidth = 0,
|
||||||
|
this.width,
|
||||||
|
this.height,
|
||||||
|
this.backgroundColor,
|
||||||
this.noIcon = false});
|
this.noIcon = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: media.thumbnail == null ? media.getThumbnail() : null,
|
future: media.thumbnail == null
|
||||||
|
? media.getThumbnail(highQuality: highQuality)
|
||||||
|
: null,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return Stack(
|
return Container(
|
||||||
fit: StackFit.passthrough,
|
width: width,
|
||||||
children: [
|
height: height,
|
||||||
if (media.thumbnailFailed && onErrorBuilder == null)
|
decoration: BoxDecoration(
|
||||||
Icon(
|
color: backgroundColor,
|
||||||
media.isImage
|
borderRadius: BorderRadius.circular(radius),
|
||||||
? Icons.image_not_supported
|
border: Border.all(color: borderColor, width: borderWidth)),
|
||||||
: Icons.videocam_off_rounded,
|
child: ClipRRect(
|
||||||
color: Colors.grey,
|
borderRadius: BorderRadius.circular(radius),
|
||||||
)
|
child: Stack(
|
||||||
else if (media.thumbnailFailed && onErrorBuilder == null)
|
fit: StackFit.passthrough,
|
||||||
onErrorBuilder!(media, context)
|
children: [
|
||||||
else if (media.thumbnail != null)
|
if (snapshot.hasError && onErrorBuilder == null)
|
||||||
FadeInImage(
|
Center(
|
||||||
fadeInDuration: const Duration(milliseconds: 200),
|
child: Icon(
|
||||||
fit: BoxFit.cover,
|
media.isImage
|
||||||
placeholder: MemoryImage(kTransparentImage),
|
? Icons.image_not_supported
|
||||||
image: MemoryImage(media.thumbnail!),
|
: Icons.videocam_off_rounded,
|
||||||
)
|
color: Colors.grey,
|
||||||
else
|
),
|
||||||
const SizedBox(),
|
)
|
||||||
if (media.thumbnail != null && !media.thumbnailFailed && !noIcon)
|
else if (snapshot.hasError && onErrorBuilder == null)
|
||||||
Positioned(
|
onErrorBuilder!(media, context)
|
||||||
bottom: 10,
|
else if (media.thumbnail != null)
|
||||||
left: 10,
|
FadeInImage(
|
||||||
child: Icon(
|
width: width,
|
||||||
media.isVideo ? Icons.video_camera_back : null,
|
height: height,
|
||||||
color: Colors.white,
|
fadeInDuration: const Duration(milliseconds: 200),
|
||||||
size: 20,
|
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,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,14 @@ import '../../../controller/gallery_controller.dart';
|
|||||||
class AlbumCategoriesView extends StatelessWidget {
|
class AlbumCategoriesView extends StatelessWidget {
|
||||||
final PhoneGalleryController controller;
|
final PhoneGalleryController controller;
|
||||||
final Config config;
|
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;
|
: config = controller.config;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -19,7 +26,12 @@ class AlbumCategoriesView extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
...controller.galleryAlbums.map(
|
...controller.galleryAlbums.map(
|
||||||
(album) => GestureDetector(
|
(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: [
|
child: Stack(fit: StackFit.passthrough, children: [
|
||||||
ThumbnailAlbum(
|
ThumbnailAlbum(
|
||||||
album: album,
|
album: album,
|
||||||
|
@ -1,56 +1,51 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gallery_picker/models/gallery_album.dart';
|
import 'package:gallery_picker/models/gallery_album.dart';
|
||||||
import '../../controller/bottom_sheet_controller.dart';
|
|
||||||
import '../../controller/gallery_controller.dart';
|
import '../../controller/gallery_controller.dart';
|
||||||
import '../gallery_picker_view/tappable_appbar.dart';
|
|
||||||
|
|
||||||
class AlbumAppBar extends StatelessWidget with PreferredSizeWidget {
|
class AlbumAppBar extends StatelessWidget with PreferredSizeWidget {
|
||||||
final PhoneGalleryController controller;
|
final PhoneGalleryController controller;
|
||||||
final BottomSheetController? bottomSheetController;
|
|
||||||
final GalleryAlbum album;
|
final GalleryAlbum album;
|
||||||
|
final bool isBottomSheet;
|
||||||
const AlbumAppBar(
|
const AlbumAppBar(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.bottomSheetController,
|
|
||||||
required this.album,
|
required this.album,
|
||||||
required this.controller});
|
required this.controller,
|
||||||
|
required this.isBottomSheet});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TappableAppbar(
|
return AppBar(
|
||||||
controller: bottomSheetController,
|
elevation: 0,
|
||||||
child: AppBar(
|
foregroundColor: controller.config.appbarIconColor,
|
||||||
elevation: 0,
|
backgroundColor: controller.config.appbarColor,
|
||||||
foregroundColor: controller.config.appbarIconColor,
|
leading: TextButton(
|
||||||
backgroundColor: controller.config.appbarColor,
|
onPressed: () async {
|
||||||
leading: TextButton(
|
controller.backToPicker();
|
||||||
onPressed: () {
|
},
|
||||||
controller.changeAlbum(null);
|
child: Icon(
|
||||||
},
|
Icons.arrow_back,
|
||||||
child: Icon(
|
color: controller.config.appbarIconColor,
|
||||||
Icons.arrow_back,
|
)),
|
||||||
color: controller.config.appbarIconColor,
|
title: getTitle(),
|
||||||
)),
|
actions: [
|
||||||
title: getTitle(),
|
!controller.pickerMode
|
||||||
actions: [
|
? TextButton(
|
||||||
!controller.pickerMode
|
onPressed: () {
|
||||||
? TextButton(
|
controller.switchPickerMode(true);
|
||||||
onPressed: () {
|
},
|
||||||
controller.switchPickerMode(true);
|
child: Icon(
|
||||||
},
|
Icons.check_box_outlined,
|
||||||
child: Icon(
|
color: controller.config.appbarIconColor,
|
||||||
Icons.check_box_outlined,
|
))
|
||||||
color: controller.config.appbarIconColor,
|
: const SizedBox()
|
||||||
))
|
],
|
||||||
: const SizedBox()
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getTitle() {
|
Widget getTitle() {
|
||||||
if (!controller.pickerMode && controller.selectedFiles.isEmpty) {
|
if (!controller.pickerMode && controller.selectedFiles.isEmpty) {
|
||||||
return Text(
|
return Text(
|
||||||
album.name!,
|
album.name ?? "Unnamed Album",
|
||||||
style: controller.config.appbarTextStyle,
|
style: controller.config.appbarTextStyle,
|
||||||
);
|
);
|
||||||
} else if (controller.pickerMode && controller.selectedFiles.isEmpty) {
|
} else if (controller.pickerMode && controller.selectedFiles.isEmpty) {
|
||||||
|
@ -7,14 +7,15 @@ import 'selected_medias_view.dart';
|
|||||||
class AlbumMediasView extends StatelessWidget {
|
class AlbumMediasView extends StatelessWidget {
|
||||||
final PhoneGalleryController controller;
|
final PhoneGalleryController controller;
|
||||||
final bool singleMedia;
|
final bool singleMedia;
|
||||||
final bool isCollapsedSheet;
|
final bool isBottomSheet;
|
||||||
const AlbumMediasView(
|
const AlbumMediasView(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.galleryAlbum,
|
required this.galleryAlbum,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.isCollapsedSheet,
|
required this.isBottomSheet,
|
||||||
required this.singleMedia});
|
required this.singleMedia});
|
||||||
final GalleryAlbum galleryAlbum;
|
final GalleryAlbum galleryAlbum;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(
|
return Stack(
|
||||||
@ -26,7 +27,7 @@ class AlbumMediasView extends StatelessWidget {
|
|||||||
category: category,
|
category: category,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
singleMedia: singleMedia,
|
singleMedia: singleMedia,
|
||||||
isCollapsedSheet: isCollapsedSheet,
|
isBottomSheet: isBottomSheet,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -35,6 +36,7 @@ class AlbumMediasView extends StatelessWidget {
|
|||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: SelectedMediasView(
|
child: SelectedMediasView(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
isBottomSheet: isBottomSheet,
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -2,38 +2,47 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:gallery_picker/views/album_view/album_appbar.dart';
|
import 'package:gallery_picker/views/album_view/album_appbar.dart';
|
||||||
import '../../../controller/gallery_controller.dart';
|
import '../../../controller/gallery_controller.dart';
|
||||||
import '../../../models/gallery_album.dart';
|
import '../../../models/gallery_album.dart';
|
||||||
import '../../controller/bottom_sheet_controller.dart';
|
|
||||||
import '../../models/config.dart';
|
|
||||||
import 'album_medias_view.dart';
|
import 'album_medias_view.dart';
|
||||||
|
|
||||||
class AlbumPage extends StatelessWidget {
|
class AlbumPage extends StatelessWidget {
|
||||||
final bool singleMedia;
|
final bool singleMedia;
|
||||||
final PhoneGalleryController controller;
|
final PhoneGalleryController controller;
|
||||||
final BottomSheetController? bottomSheetController;
|
final GalleryAlbum? album;
|
||||||
final GalleryAlbum album;
|
final bool isBottomSheet;
|
||||||
final bool isCollapsedSheet;
|
|
||||||
const AlbumPage(
|
const AlbumPage(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.album,
|
required this.album,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.singleMedia,
|
required this.singleMedia,
|
||||||
required this.isCollapsedSheet,
|
required this.isBottomSheet});
|
||||||
required this.bottomSheetController});
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Config config = controller.config;
|
return WillPopScope(
|
||||||
return Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: config.backgroundColor,
|
backgroundColor: controller.config.backgroundColor,
|
||||||
appBar: AlbumAppBar(
|
appBar: album != null
|
||||||
bottomSheetController: bottomSheetController,
|
? AlbumAppBar(
|
||||||
album: album,
|
album: album!,
|
||||||
controller: controller),
|
controller: controller,
|
||||||
body: AlbumMediasView(
|
isBottomSheet: isBottomSheet,
|
||||||
galleryAlbum: album,
|
)
|
||||||
controller: controller,
|
: null,
|
||||||
isCollapsedSheet: isCollapsedSheet,
|
body: album != null
|
||||||
singleMedia: singleMedia,
|
? 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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,13 @@ class DateCategoryWiew extends StatelessWidget {
|
|||||||
final PhoneGalleryController controller;
|
final PhoneGalleryController controller;
|
||||||
final bool singleMedia;
|
final bool singleMedia;
|
||||||
final DateCategory category;
|
final DateCategory category;
|
||||||
final bool isCollapsedSheet;
|
final bool isBottomSheet;
|
||||||
|
|
||||||
const DateCategoryWiew(
|
const DateCategoryWiew(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.category,
|
required this.category,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.isCollapsedSheet,
|
required this.isBottomSheet,
|
||||||
required this.singleMedia});
|
required this.singleMedia});
|
||||||
|
|
||||||
int getRowCount() {
|
int getRowCount() {
|
||||||
@ -45,14 +45,14 @@ class DateCategoryWiew extends StatelessWidget {
|
|||||||
size: MediaQuery.of(context).size.width,
|
size: MediaQuery.of(context).size.width,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
crossAxisCount: 4,
|
crossAxisCount: 4,
|
||||||
mainAxisSpacing: 1.0,
|
mainAxisSpacing: 3.0,
|
||||||
crossAxisSpacing: 1.0,
|
crossAxisSpacing: 3.0,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
...category.files.map(
|
...category.files.map(
|
||||||
(medium) => MediaView(medium,
|
(medium) => MediaView(medium,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
singleMedia: singleMedia,
|
singleMedia: singleMedia,
|
||||||
isCollapsedSheet: isCollapsedSheet),
|
isBottomSheet: isBottomSheet),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
|
import 'package:bottom_sheet_scaffold/bottom_sheet_scaffold.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../../controller/bottom_sheet_controller.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import '../../../controller/gallery_controller.dart';
|
import '../../../controller/gallery_controller.dart';
|
||||||
import '../../../models/media_file.dart';
|
import '../../../models/media_file.dart';
|
||||||
import '../thumbnail_media_file.dart';
|
import '../thumbnail_media_file.dart';
|
||||||
@ -9,89 +8,69 @@ class MediaView extends StatelessWidget {
|
|||||||
final MediaFile file;
|
final MediaFile file;
|
||||||
final PhoneGalleryController controller;
|
final PhoneGalleryController controller;
|
||||||
final bool singleMedia;
|
final bool singleMedia;
|
||||||
final bool isCollapsedSheet;
|
final bool isBottomSheet;
|
||||||
const MediaView(this.file,
|
const MediaView(this.file,
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.singleMedia,
|
required this.singleMedia,
|
||||||
required this.isCollapsedSheet});
|
required this.isBottomSheet});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(
|
return Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
ThumbnailMediaFile(
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (singleMedia) {
|
if (singleMedia) {
|
||||||
if (controller.heroBuilder != null) {
|
if (controller.heroBuilder != null) {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute<void>(builder: (BuildContext context) {
|
MaterialPageRoute<void>(builder: (BuildContext context) {
|
||||||
return controller.heroBuilder!(file.medium.id, file, context);
|
return controller.heroBuilder!(file.id, file, context);
|
||||||
}));
|
}));
|
||||||
} else {
|
|
||||||
controller.selectedFiles.add(file);
|
|
||||||
controller.onSelect(controller.selectedFiles);
|
|
||||||
controller.updatePickerListener();
|
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
|
||||||
Get.find<BottomSheetController>().close();
|
|
||||||
} else {
|
} else {
|
||||||
Navigator.pop(context);
|
controller.selectedFiles.add(file);
|
||||||
controller.disposeController();
|
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<void>(builder: (BuildContext context) {
|
|
||||||
return controller.heroBuilder!(file.medium.id, file, context);
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
controller.selectedFiles.add(file);
|
controller.selectMedia(file);
|
||||||
controller.onSelect(controller.selectedFiles);
|
|
||||||
controller.updatePickerListener();
|
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
|
||||||
Get.find<BottomSheetController>().close();
|
|
||||||
} else {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.disposeController();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
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(
|
onTap: () {
|
||||||
opacity: 0.5,
|
if (controller.pickerMode) {
|
||||||
child: Container(
|
if (controller.isSelectedMedia(file)) {
|
||||||
color: Colors.black,
|
controller.unselectMedia(file);
|
||||||
child: const Icon(
|
} else {
|
||||||
Icons.check,
|
controller.selectMedia(file);
|
||||||
color: Colors.white,
|
}
|
||||||
size: 45,
|
} else {
|
||||||
),
|
if (controller.heroBuilder != null) {
|
||||||
),
|
Navigator.of(context).push(
|
||||||
),
|
MaterialPageRoute<void>(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),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
62
lib/views/album_view/selected_media_thumbnail.dart
Normal file
62
lib/views/album_view/selected_media_thumbnail.dart
Normal file
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,15 @@
|
|||||||
|
import 'package:bottom_sheet_scaffold/bottom_sheet_scaffold.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:gallery_picker/views/thumbnail_media_file.dart';
|
||||||
import '../../controller/bottom_sheet_controller.dart';
|
|
||||||
import '../../controller/gallery_controller.dart';
|
import '../../controller/gallery_controller.dart';
|
||||||
import '../../models/config.dart';
|
import '../../models/config.dart';
|
||||||
|
|
||||||
class SelectedMediasView extends StatelessWidget {
|
class SelectedMediasView extends StatelessWidget {
|
||||||
final PhoneGalleryController controller;
|
final PhoneGalleryController controller;
|
||||||
final Config config;
|
final Config config;
|
||||||
SelectedMediasView({super.key, required this.controller})
|
final bool isBottomSheet;
|
||||||
|
SelectedMediasView(
|
||||||
|
{super.key, required this.controller, required this.isBottomSheet})
|
||||||
: config = controller.config;
|
: config = controller.config;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -27,37 +29,19 @@ class SelectedMediasView extends StatelessWidget {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
children: [
|
children: [
|
||||||
for (var selectedMedia in controller.selectedFiles)
|
for (var mediaFile in controller.selectedFiles)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 3.0, bottom: 3.0, right: 2),
|
top: 3.0, bottom: 3.0, right: 2),
|
||||||
child: !selectedMedia.thumbnailFailed
|
child: ThumbnailMediaFile(
|
||||||
? Container(
|
file: mediaFile,
|
||||||
decoration: BoxDecoration(
|
width: 50,
|
||||||
borderRadius: BorderRadius.circular(5),
|
height: 55,
|
||||||
image: DecorationImage(
|
radius: 5,
|
||||||
fit: BoxFit.fill,
|
noIcon: true,
|
||||||
image: Image.memory(
|
noSelectedIcon: true,
|
||||||
selectedMedia.thumbnail!,
|
failIconColor: controller.config.appbarIconColor,
|
||||||
fit: BoxFit.fill,
|
controller: controller),
|
||||||
).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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -69,7 +53,7 @@ class SelectedMediasView extends StatelessWidget {
|
|||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute<void>(builder: (BuildContext context) {
|
MaterialPageRoute<void>(builder: (BuildContext context) {
|
||||||
return controller.heroBuilder!(
|
return controller.heroBuilder!(
|
||||||
controller.selectedFiles[0].medium.id,
|
controller.selectedFiles[0].id,
|
||||||
controller.selectedFiles[0],
|
controller.selectedFiles[0],
|
||||||
context);
|
context);
|
||||||
}));
|
}));
|
||||||
@ -81,8 +65,8 @@ class SelectedMediasView extends StatelessWidget {
|
|||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
controller.onSelect(controller.selectedFiles);
|
controller.onSelect(controller.selectedFiles);
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
if (isBottomSheet) {
|
||||||
Get.find<BottomSheetController>().close();
|
BottomSheetPanel.close();
|
||||||
} else {
|
} else {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
controller.disposeController();
|
controller.disposeController();
|
||||||
|
@ -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<MediaFile>? initSelectedMedia;
|
|
||||||
final List<MediaFile>? extraRecentMedia;
|
|
||||||
final bool singleMedia;
|
|
||||||
final Function(List<MediaFile> selectedMedia) onSelect;
|
|
||||||
final Widget Function(String tag, MediaFile media, BuildContext context)?
|
|
||||||
heroBuilder;
|
|
||||||
final Widget Function(List<MediaFile> 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<PhoneGalleryController>()) {
|
|
||||||
if (initSelectedMedia != null) {
|
|
||||||
Get.find<PhoneGalleryController>()
|
|
||||||
.updateSelectedFiles(initSelectedMedia!);
|
|
||||||
}
|
|
||||||
if (extraRecentMedia != null) {
|
|
||||||
Get.find<PhoneGalleryController>()
|
|
||||||
.updateExtraRecentMedia(extraRecentMedia!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<BottomSheetLayout> createState() => _BottomSheetLayoutState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BottomSheetLayoutState extends State<BottomSheetLayout> {
|
|
||||||
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<BottomSheetController>(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<BottomSheetController>(
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
import 'package:bottom_sheet_bar/bottom_sheet_bar.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import '../../controller/bottom_sheet_controller.dart';
|
|
||||||
import '../../controller/gallery_controller.dart';
|
import '../../controller/gallery_controller.dart';
|
||||||
import '../../models/config.dart';
|
import '../../models/config.dart';
|
||||||
import '../../models/gallery_album.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_categories_view/album_categories_view.dart';
|
||||||
import '../album_view/album_page.dart';
|
import '../album_view/album_page.dart';
|
||||||
import '../album_view/album_medias_view.dart';
|
import '../album_view/album_medias_view.dart';
|
||||||
|
import 'permission_denied_view.dart';
|
||||||
import 'picker_appbar.dart';
|
import 'picker_appbar.dart';
|
||||||
import 'reload_gallery.dart';
|
import 'reload_gallery.dart';
|
||||||
|
|
||||||
@ -20,11 +19,10 @@ class GalleryPickerView extends StatefulWidget {
|
|||||||
final Widget Function(List<MediaFile> media, BuildContext context)?
|
final Widget Function(List<MediaFile> media, BuildContext context)?
|
||||||
multipleMediaBuilder;
|
multipleMediaBuilder;
|
||||||
final bool startWithRecent;
|
final bool startWithRecent;
|
||||||
final BottomSheetBarController? sheetController;
|
final bool isBottomSheet;
|
||||||
final List<MediaFile>? initSelectedMedia;
|
final List<MediaFile>? initSelectedMedia;
|
||||||
final List<MediaFile>? extraRecentMedia;
|
final List<MediaFile>? extraRecentMedia;
|
||||||
final bool singleMedia;
|
final bool singleMedia;
|
||||||
final bool isCollapsedSheet;
|
|
||||||
const GalleryPickerView(
|
const GalleryPickerView(
|
||||||
{super.key,
|
{super.key,
|
||||||
this.config,
|
this.config,
|
||||||
@ -32,8 +30,7 @@ class GalleryPickerView extends StatefulWidget {
|
|||||||
this.initSelectedMedia,
|
this.initSelectedMedia,
|
||||||
this.extraRecentMedia,
|
this.extraRecentMedia,
|
||||||
this.singleMedia = false,
|
this.singleMedia = false,
|
||||||
this.isCollapsedSheet = false,
|
this.isBottomSheet = false,
|
||||||
this.sheetController,
|
|
||||||
this.heroBuilder,
|
this.heroBuilder,
|
||||||
this.multipleMediaBuilder,
|
this.multipleMediaBuilder,
|
||||||
this.startWithRecent = false});
|
this.startWithRecent = false});
|
||||||
@ -44,43 +41,39 @@ class GalleryPickerView extends StatefulWidget {
|
|||||||
|
|
||||||
class _GalleryPickerState extends State<GalleryPickerView> {
|
class _GalleryPickerState extends State<GalleryPickerView> {
|
||||||
late PhoneGalleryController galleryController;
|
late PhoneGalleryController galleryController;
|
||||||
BottomSheetController? bottomSheetController;
|
|
||||||
late PageController _scrollController;
|
|
||||||
bool noPhotoSeleceted = true;
|
bool noPhotoSeleceted = true;
|
||||||
late Config config;
|
late Config config;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_scrollController =
|
|
||||||
PageController(initialPage: widget.startWithRecent ? 0 : 1);
|
|
||||||
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
||||||
galleryController = Get.find<PhoneGalleryController>();
|
galleryController = Get.find<PhoneGalleryController>();
|
||||||
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 {
|
} else {
|
||||||
galleryController = Get.put(PhoneGalleryController(widget.config,
|
galleryController = Get.put(PhoneGalleryController());
|
||||||
|
galleryController.configuration(widget.config,
|
||||||
onSelect: widget.onSelect,
|
onSelect: widget.onSelect,
|
||||||
|
startWithRecent: widget.startWithRecent,
|
||||||
heroBuilder: widget.heroBuilder,
|
heroBuilder: widget.heroBuilder,
|
||||||
multipleMediasBuilder: widget.multipleMediaBuilder,
|
multipleMediasBuilder: widget.multipleMediaBuilder,
|
||||||
initSelectedMedias: widget.initSelectedMedia,
|
initSelectedMedias: widget.initSelectedMedia,
|
||||||
extraRecentMedia: widget.extraRecentMedia,
|
extraRecentMedia: widget.extraRecentMedia,
|
||||||
isRecent: widget.startWithRecent));
|
isRecent: widget.startWithRecent);
|
||||||
config = galleryController.config;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.sheetController != null) {
|
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
|
||||||
bottomSheetController = Get.find<BottomSheetController>();
|
|
||||||
} else {
|
|
||||||
bottomSheetController =
|
|
||||||
Get.put(BottomSheetController(widget.sheetController!));
|
|
||||||
}
|
|
||||||
bottomSheetController!.galleryController = galleryController;
|
|
||||||
}
|
}
|
||||||
|
config = galleryController.config;
|
||||||
if (!galleryController.isInitialized) {
|
if (!galleryController.isInitialized) {
|
||||||
galleryController.initializeAlbums();
|
galleryController.initializeAlbums();
|
||||||
}
|
}
|
||||||
if (galleryController.isRecent) {
|
|
||||||
_scrollController = PageController(initialPage: 0);
|
|
||||||
}
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,144 +83,153 @@ class _GalleryPickerState extends State<GalleryPickerView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GalleryAlbum? selectedAlbum;
|
GalleryAlbum? selectedAlbum;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
double width = MediaQuery.of(context).size.width;
|
double width = MediaQuery.of(context).size.width;
|
||||||
return GetBuilder<PhoneGalleryController>(builder: (controller) {
|
return GetBuilder<PhoneGalleryController>(builder: (controller) {
|
||||||
if (controller.selectedAlbum == null && selectedAlbum != null) {
|
|
||||||
_scrollController = PageController(initialPage: 1);
|
|
||||||
}
|
|
||||||
selectedAlbum = controller.selectedAlbum;
|
|
||||||
return GetInstance().isRegistered<PhoneGalleryController>()
|
return GetInstance().isRegistered<PhoneGalleryController>()
|
||||||
? controller.selectedAlbum == null
|
? controller.permissionGranted
|
||||||
? Scaffold(
|
? PageView(
|
||||||
backgroundColor: config.backgroundColor,
|
controller: controller.pageController,
|
||||||
appBar: PickerAppBar(
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
controller: controller,
|
children: [
|
||||||
bottomSheetController: bottomSheetController,
|
Scaffold(
|
||||||
),
|
backgroundColor: config.backgroundColor,
|
||||||
body: Column(
|
appBar: PickerAppBar(
|
||||||
children: [
|
controller: controller,
|
||||||
Container(
|
isBottomSheet: widget.isBottomSheet,
|
||||||
width: width,
|
),
|
||||||
height: 48,
|
body: Column(
|
||||||
color: config.appbarColor,
|
children: [
|
||||||
child: Row(
|
Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
width: width,
|
||||||
children: [
|
height: 48,
|
||||||
Container(
|
color: config.appbarColor,
|
||||||
decoration: controller.isRecent
|
child: Row(
|
||||||
? BoxDecoration(
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
border: Border(
|
children: [
|
||||||
bottom: BorderSide(
|
Container(
|
||||||
color: config.underlineColor,
|
decoration: controller.isRecent
|
||||||
width: 3.0,
|
? BoxDecoration(
|
||||||
),
|
border: Border(
|
||||||
))
|
bottom: BorderSide(
|
||||||
: null,
|
color: config.underlineColor,
|
||||||
height: 48,
|
width: 3.0,
|
||||||
width: width / 2,
|
),
|
||||||
child: TextButton(
|
))
|
||||||
onPressed: () {
|
: null,
|
||||||
_scrollController.animateTo(0,
|
height: 48,
|
||||||
duration:
|
width: width / 2,
|
||||||
const Duration(milliseconds: 50),
|
child: TextButton(
|
||||||
curve: Curves.easeIn);
|
onPressed: () {
|
||||||
setState(() {
|
controller.pickerPageController
|
||||||
controller.isRecent = true;
|
.animateToPage(0,
|
||||||
controller.switchPickerMode(false);
|
duration: const Duration(
|
||||||
});
|
milliseconds: 50),
|
||||||
},
|
curve: Curves.easeIn);
|
||||||
child: Text(config.recents,
|
setState(() {
|
||||||
style: controller.isRecent
|
controller.isRecent = true;
|
||||||
? config.selectedMenuStyle
|
controller.switchPickerMode(false);
|
||||||
: config.unselectedMenuStyle)),
|
});
|
||||||
|
},
|
||||||
|
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
|
Expanded(
|
||||||
? BoxDecoration(
|
child: PageView(
|
||||||
border: Border(
|
controller: controller.pickerPageController,
|
||||||
bottom: BorderSide(
|
onPageChanged: (value) {
|
||||||
color: config.underlineColor,
|
if (value == 0) {
|
||||||
width: 3.0,
|
controller.isRecent = true;
|
||||||
),
|
controller.switchPickerMode(false);
|
||||||
))
|
} else {
|
||||||
: null,
|
|
||||||
height: 48,
|
|
||||||
width: width / 2,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
_scrollController.animateTo(width,
|
|
||||||
duration:
|
|
||||||
const Duration(milliseconds: 50),
|
|
||||||
curve: Curves.easeIn);
|
|
||||||
controller.isRecent = false;
|
controller.isRecent = false;
|
||||||
controller.switchPickerMode(false);
|
controller.switchPickerMode(false);
|
||||||
},
|
}
|
||||||
child: Text(
|
},
|
||||||
config.gallery,
|
scrollDirection: Axis.horizontal,
|
||||||
style: controller.isRecent
|
children: [
|
||||||
? config.unselectedMenuStyle
|
controller.isInitialized &&
|
||||||
: config.selectedMenuStyle,
|
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(
|
AlbumPage(
|
||||||
controller: _scrollController,
|
album: controller.selectedAlbum,
|
||||||
onPageChanged: (value) {
|
controller: controller,
|
||||||
if (value == 0) {
|
singleMedia: widget.singleMedia,
|
||||||
controller.isRecent = true;
|
isBottomSheet: widget.isBottomSheet)
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
|
: controller.config.permissionDeniedPage ??
|
||||||
|
PermissionDeniedView(
|
||||||
|
config: controller.config,
|
||||||
|
)
|
||||||
: ReloadGallery(
|
: ReloadGallery(
|
||||||
config,
|
config,
|
||||||
onpressed: () async {
|
onpressed: () async {
|
||||||
if (widget.sheetController != null) {
|
galleryController = Get.put(PhoneGalleryController());
|
||||||
bottomSheetController =
|
galleryController.configuration(widget.config,
|
||||||
Get.put(BottomSheetController(widget.sheetController!));
|
|
||||||
}
|
|
||||||
galleryController = Get.put(PhoneGalleryController(config,
|
|
||||||
onSelect: widget.onSelect,
|
onSelect: widget.onSelect,
|
||||||
|
startWithRecent: widget.startWithRecent,
|
||||||
heroBuilder: widget.heroBuilder,
|
heroBuilder: widget.heroBuilder,
|
||||||
|
multipleMediasBuilder: widget.multipleMediaBuilder,
|
||||||
initSelectedMedias: widget.initSelectedMedia,
|
initSelectedMedias: widget.initSelectedMedia,
|
||||||
extraRecentMedia: widget.extraRecentMedia,
|
extraRecentMedia: widget.extraRecentMedia,
|
||||||
multipleMediasBuilder: widget.multipleMediaBuilder,
|
isRecent: widget.startWithRecent);
|
||||||
isRecent: widget.startWithRecent));
|
if (!controller.isInitialized) {
|
||||||
await controller.initializeAlbums();
|
await controller.initializeAlbums();
|
||||||
if (bottomSheetController != null) {
|
|
||||||
bottomSheetController!.galleryController = galleryController;
|
|
||||||
}
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
|
48
lib/views/gallery_picker_view/permission_denied_view.dart
Normal file
48
lib/views/gallery_picker_view/permission_denied_view.dart
Normal file
@ -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"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,52 +1,45 @@
|
|||||||
|
import 'package:bottom_sheet_scaffold/bottom_sheet_scaffold.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import '../../controller/bottom_sheet_controller.dart';
|
|
||||||
import '../../controller/gallery_controller.dart';
|
import '../../controller/gallery_controller.dart';
|
||||||
import 'tappable_appbar.dart';
|
|
||||||
|
|
||||||
class PickerAppBar extends StatelessWidget with PreferredSizeWidget {
|
class PickerAppBar extends StatelessWidget with PreferredSizeWidget {
|
||||||
final PhoneGalleryController controller;
|
final PhoneGalleryController controller;
|
||||||
final BottomSheetController? bottomSheetController;
|
final bool isBottomSheet;
|
||||||
const PickerAppBar(
|
const PickerAppBar(
|
||||||
{super.key,
|
{super.key, required this.isBottomSheet, required this.controller});
|
||||||
required this.bottomSheetController,
|
|
||||||
required this.controller});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TappableAppbar(
|
return AppBar(
|
||||||
controller: bottomSheetController,
|
elevation: 0,
|
||||||
child: AppBar(
|
backgroundColor: controller.config.appbarColor,
|
||||||
elevation: 0,
|
leading: TextButton(
|
||||||
backgroundColor: controller.config.appbarColor,
|
onPressed: () async {
|
||||||
leading: TextButton(
|
if (isBottomSheet) {
|
||||||
onPressed: () async {
|
BottomSheetPanel.close();
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
} else {
|
||||||
bottomSheetController!.close();
|
Navigator.pop(context);
|
||||||
} else {
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
Navigator.pop(context);
|
controller.disposeController();
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
}
|
||||||
controller.disposeController();
|
},
|
||||||
}
|
child: Icon(
|
||||||
},
|
Icons.arrow_back,
|
||||||
child: Icon(
|
color: controller.config.appbarIconColor,
|
||||||
Icons.arrow_back,
|
)),
|
||||||
color: controller.config.appbarIconColor,
|
title: getTitle(),
|
||||||
)),
|
actions: [
|
||||||
title: getTitle(),
|
!controller.pickerMode && controller.isRecent
|
||||||
actions: [
|
? TextButton(
|
||||||
!controller.pickerMode && controller.isRecent
|
onPressed: () {
|
||||||
? TextButton(
|
controller.switchPickerMode(true);
|
||||||
onPressed: () {
|
},
|
||||||
controller.switchPickerMode(true);
|
child: Icon(
|
||||||
},
|
Icons.check_box_outlined,
|
||||||
child: Icon(
|
color: controller.config.appbarIconColor,
|
||||||
Icons.check_box_outlined,
|
))
|
||||||
color: controller.config.appbarIconColor,
|
: const SizedBox()
|
||||||
))
|
],
|
||||||
: const SizedBox()
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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<BottomSheetController>()
|
|
||||||
? GestureDetector(
|
|
||||||
onLongPressEnd: (a) {
|
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
|
||||||
controller!.tapingStatus(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPanCancel: () {},
|
|
||||||
onPanDown: (a) {
|
|
||||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
|
||||||
controller!.tapingStatus(true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: child,
|
|
||||||
)
|
|
||||||
: child;
|
|
||||||
}
|
|
||||||
}
|
|
136
lib/views/picker_scaffold.dart
Normal file
136
lib/views/picker_scaffold.dart
Normal file
@ -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<PhoneGalleryController>()) {
|
||||||
|
if (initSelectedMedia != null) {
|
||||||
|
Get.find<PhoneGalleryController>()
|
||||||
|
.updateSelectedFiles(initSelectedMedia!);
|
||||||
|
}
|
||||||
|
if (extraRecentMedia != null) {
|
||||||
|
Get.find<PhoneGalleryController>()
|
||||||
|
.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<Widget>? 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<MediaFile>? initSelectedMedia;
|
||||||
|
final List<MediaFile>? extraRecentMedia;
|
||||||
|
final bool singleMedia;
|
||||||
|
final Function(List<MediaFile> selectedMedia) onSelect;
|
||||||
|
final Widget Function(String tag, MediaFile media, BuildContext context)?
|
||||||
|
heroBuilder;
|
||||||
|
final Widget Function(List<MediaFile> 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<PhoneGalleryController>()) {
|
||||||
|
Get.find<PhoneGalleryController>().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,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../controller/gallery_controller.dart';
|
import '../controller/gallery_controller.dart';
|
||||||
|
import '../functions/color.dart';
|
||||||
import '../models/mode.dart';
|
import '../models/mode.dart';
|
||||||
import '/models/media_file.dart';
|
import '/models/media_file.dart';
|
||||||
import 'package:transparent_image/transparent_image.dart';
|
import 'package:transparent_image/transparent_image.dart';
|
||||||
@ -8,12 +9,29 @@ class ThumbnailMediaFile extends StatelessWidget {
|
|||||||
final MediaFile file;
|
final MediaFile file;
|
||||||
final Color failIconColor;
|
final Color failIconColor;
|
||||||
final PhoneGalleryController controller;
|
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(
|
const ThumbnailMediaFile(
|
||||||
{super.key,
|
{super.key,
|
||||||
|
this.fit = BoxFit.cover,
|
||||||
required this.file,
|
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.failIconColor,
|
||||||
required this.isCollapsedSheet,
|
|
||||||
required this.controller});
|
required this.controller});
|
||||||
|
|
||||||
Color adjustFailedBgColor() {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: file.thumbnail == null ? file.getThumbnail() : null,
|
future: file.thumbnail == null
|
||||||
|
? file.getThumbnail(highQuality: highQuality)
|
||||||
|
: null,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return Stack(
|
return GestureDetector(
|
||||||
fit: StackFit.passthrough,
|
onTap: onTap != null
|
||||||
children: [
|
? () {
|
||||||
if (file.thumbnailFailed)
|
onTap!();
|
||||||
Container(
|
}
|
||||||
color: adjustFailedBgColor(),
|
: null,
|
||||||
child: Icon(
|
onLongPress: onLongPress != null
|
||||||
file.isImage
|
? () {
|
||||||
? Icons.image_not_supported
|
onLongPress!();
|
||||||
: Icons.videocam_off_rounded,
|
}
|
||||||
size: 50,
|
: null,
|
||||||
color: failIconColor,
|
child: Container(
|
||||||
))
|
width: width,
|
||||||
else if (file.thumbnail != null &&
|
height: height,
|
||||||
!isCollapsedSheet &&
|
decoration: BoxDecoration(
|
||||||
controller.heroBuilder != null)
|
color: adjustFailedBgColor(),
|
||||||
Hero(
|
borderRadius: BorderRadius.circular(radius),
|
||||||
tag: file.medium.id,
|
border: Border.all(color: borderColor, width: borderWidth)),
|
||||||
child: FadeInImage(
|
child: ClipRRect(
|
||||||
fadeInDuration: const Duration(milliseconds: 200),
|
borderRadius: BorderRadius.circular(radius),
|
||||||
fit: BoxFit.cover,
|
child: Stack(
|
||||||
placeholder: MemoryImage(kTransparentImage),
|
fit: StackFit.passthrough,
|
||||||
image: MemoryImage(file.thumbnail!),
|
children: [
|
||||||
),
|
if (snapshot.hasError)
|
||||||
)
|
Center(
|
||||||
else if (file.thumbnail != null && controller.heroBuilder == null)
|
child: Icon(
|
||||||
FadeInImage(
|
file.isImage
|
||||||
fadeInDuration: const Duration(milliseconds: 200),
|
? Icons.image_not_supported
|
||||||
fit: BoxFit.cover,
|
: Icons.videocam_off_rounded,
|
||||||
placeholder: MemoryImage(kTransparentImage),
|
size: 50,
|
||||||
image: MemoryImage(file.thumbnail!),
|
color: failIconColor,
|
||||||
)
|
),
|
||||||
else
|
)
|
||||||
const SizedBox(),
|
else if (file.thumbnail != null &&
|
||||||
if (file.thumbnail != null && !file.thumbnailFailed)
|
controller.heroBuilder != null)
|
||||||
Positioned(
|
Hero(
|
||||||
bottom: 10,
|
tag: file.id,
|
||||||
left: 10,
|
child: FadeInImage(
|
||||||
child: Icon(
|
width: width,
|
||||||
file.isVideo ? Icons.video_camera_back : null,
|
height: height,
|
||||||
color: Colors.white,
|
fadeInDuration: const Duration(milliseconds: 200),
|
||||||
size: 20,
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
name: gallery_picker
|
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.
|
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
|
homepage: https://github.com/FlutterWay/gallery_picker
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
@ -18,7 +18,8 @@ dependencies:
|
|||||||
get: ^4.6.5
|
get: ^4.6.5
|
||||||
video_thumbnail: ^0.5.3
|
video_thumbnail: ^0.5.3
|
||||||
intl: ^0.18.0
|
intl: ^0.18.0
|
||||||
bottom_sheet_bar: ^2.3.8
|
bottom_sheet_scaffold: ^0.1.1
|
||||||
|
page_transition: ^2.0.9
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
Loading…
x
Reference in New Issue
Block a user