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