upgrading package to 0.3.0

This commit is contained in:
Furkan 2023-01-20 09:58:21 +03:00
parent 24acdf8401
commit 3d84689c77
32 changed files with 1393 additions and 1226 deletions

View File

@ -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"}

View File

@ -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

View File

@ -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()
return PickerScaffold(
backgroundColor: Colors.transparent,
onSelect: (media) {},
child: Column(
children: [
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

View File

@ -15,9 +15,8 @@ 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;
@ -31,13 +30,14 @@ class _BottomSheetExampleState extends State<BottomSheetExample> {
setState(() {});
},
config: Config(mode: Mode.dark),
child: Center(
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(
@ -146,7 +146,6 @@ class _BottomSheetExampleState extends State<BottomSheetExample> {
],
),
),
),
);
}
}

View File

@ -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;
if (mounted) {
setState(() {});
}
});
super.initState();
}
Future<void> fetchMedias() async {
gallery = await GalleryPicker.collectGallery;
gallery = await GalleryPicker.initializeGallery;
setState(() {});
}
@ -72,19 +74,19 @@ 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;
if (mounted) {
setState(() {});
}
},
initSelectedMedia: selectedMedias,
config: Config(mode: Mode.dark),
child: Stack(
body: Stack(
children: [
if (cameraController != null &&
cameraController!.value.isInitialized)
if (cameraController != null && cameraController!.value.isInitialized)
SizedBox(
height: MediaQuery.of(context).size.height,
child: CameraPreview(
@ -113,8 +115,8 @@ class _WhatsappPickPhotoState extends State<WhatsappPickPhoto> {
MultipleMediasView([media])),
);
} else {
selectedMedias.any(
(element) => element.id == media.id)
selectedMedias
.any((element) => element.id == media.id)
? selectedMedias.removeWhere(
(element) => element.id == media.id)
: selectedMedias.add(media);
@ -150,8 +152,8 @@ class _WhatsappPickPhotoState extends State<WhatsappPickPhoto> {
media: media,
),
),
if (selectedMedias.any(
(element) => element.id == media.id))
if (selectedMedias
.any((element) => element.id == media.id))
Container(
color: Colors.black.withOpacity(0.3),
alignment: Alignment.center,
@ -237,14 +239,12 @@ class _WhatsappPickPhotoState extends State<WhatsappPickPhoto> {
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.circle,
border:
Border.all(width: 2, color: Colors.white)),
border: Border.all(width: 2, color: Colors.white)),
width: isRecording ? 80 : 65,
height: isRecording ? 80 : 65,
child: Container(
decoration: BoxDecoration(
color:
anyProcess ? Colors.red : Colors.transparent,
color: anyProcess ? Colors.red : Colors.transparent,
shape: BoxShape.circle,
),
),
@ -252,8 +252,7 @@ class _WhatsappPickPhotoState extends State<WhatsappPickPhoto> {
),
TextButton(
onPressed: () {
if (cameraLensDirection ==
CameraLensDirection.front) {
if (cameraLensDirection == CameraLensDirection.front) {
cameraLensDirection = CameraLensDirection.back;
} else {
cameraLensDirection = CameraLensDirection.front;
@ -277,7 +276,6 @@ class _WhatsappPickPhotoState extends State<WhatsappPickPhoto> {
))
],
),
),
);
}
}

View File

@ -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"

View File

@ -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);
}

View File

@ -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,10 +196,22 @@ class PhoneGalleryController extends GetxController {
recentTmp.files.any((file) => element.id == file.id));
}
}
permissionGranted = true;
_isInitialized = true;
} else {
permissionGranted = false;
permissionListener();
}
update();
}
_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 {
@ -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
View 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();
}

View File

@ -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;
}
}

View File

@ -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",

View File

@ -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)

View File

@ -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) {
Future<Uint8List> getThumbnail({bool highQuality = true}) async {
if (_medium == null) {
thumbnail = isVideo
? await VideoThumbnail.thumbnailData(
video: file!.path,
? (await VideoThumbnail.thumbnailData(
video: _file!.path,
imageFormat: ImageFormat.JPEG,
quality: 100,
)
quality: highQuality ? 100 : 20,
))!
: await getData();
} else {
thumbnail =
Uint8List.fromList(await medium.getThumbnail(highQuality: true));
thumbnail = Uint8List.fromList(
await _medium!.getThumbnail(highQuality: highQuality));
}
} catch (e) {
thumbnailFailed = true;
}
}
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();
}
}

View File

@ -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,
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!),
)
: Image.memory(
_media.data!,
width: widget.width,
height: widget.height,
fit: widget.fit,
: const SizedBox(),
);
}),
);
}
}

View File

@ -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(),

View File

@ -5,40 +5,72 @@ 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(
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 (media.thumbnailFailed && onErrorBuilder == null)
Icon(
if (snapshot.hasError && onErrorBuilder == null)
Center(
child: Icon(
media.isImage
? Icons.image_not_supported
: Icons.videocam_off_rounded,
color: Colors.grey,
),
)
else if (media.thumbnailFailed && onErrorBuilder == null)
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: BoxFit.cover,
fit: fit,
placeholder: MemoryImage(kTransparentImage),
image: MemoryImage(media.thumbnail!),
)
else
const SizedBox(),
if (media.thumbnail != null && !media.thumbnailFailed && !noIcon)
SizedBox(
width: width,
height: height,
),
if (media.thumbnail != null && !noIcon)
Positioned(
bottom: 10,
left: 10,
@ -48,6 +80,8 @@ class ThumbnailMedia extends StatelessWidget {
size: 20,
)),
],
),
),
);
});
}

View File

@ -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,

View File

@ -1,30 +1,26 @@
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(
return AppBar(
elevation: 0,
foregroundColor: controller.config.appbarIconColor,
backgroundColor: controller.config.appbarColor,
leading: TextButton(
onPressed: () {
controller.changeAlbum(null);
onPressed: () async {
controller.backToPicker();
},
child: Icon(
Icons.arrow_back,
@ -43,14 +39,13 @@ class AlbumAppBar extends StatelessWidget with PreferredSizeWidget {
))
: 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) {

View File

@ -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,
))
],
);

View File

@ -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,
return WillPopScope(
child: Scaffold(
backgroundColor: controller.config.backgroundColor,
appBar: album != null
? AlbumAppBar(
album: album!,
controller: controller,
isCollapsedSheet: isCollapsedSheet,
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;
});
}
}

View File

@ -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),
),
],
),

View File

@ -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,57 +8,59 @@ 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(
ThumbnailMediaFile(
onLongPress: () {
if (singleMedia) {
if (controller.heroBuilder != null) {
Navigator.of(context).push(
MaterialPageRoute<void>(builder: (BuildContext context) {
return controller.heroBuilder!(file.medium.id, file, context);
return controller.heroBuilder!(file.id, file, context);
}));
} else {
controller.selectedFiles.add(file);
controller.onSelect(controller.selectedFiles);
controller.updatePickerListener();
if (GetInstance().isRegistered<BottomSheetController>()) {
Get.find<BottomSheetController>().close();
if (isBottomSheet) {
BottomSheetPanel.close();
} else {
Navigator.pop(context);
controller.disposeController();
}
}
} else {
file.select(controller: controller);
controller.selectMedia(file);
}
},
onTap: () {
if (controller.pickerMode) {
file.isSelected(controller: controller)!
? file.unselect(controller: controller)
: file.select(controller: controller);
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.medium.id, file, context);
return controller.heroBuilder!(file.id, file, context);
}));
} else {
controller.selectedFiles.add(file);
controller.onSelect(controller.selectedFiles);
controller.updatePickerListener();
if (GetInstance().isRegistered<BottomSheetController>()) {
Get.find<BottomSheetController>().close();
if (isBottomSheet) {
BottomSheetPanel.close();
} else {
Navigator.pop(context);
controller.disposeController();
@ -67,31 +68,9 @@ class MediaView extends StatelessWidget {
}
}
},
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,
),
),
),
),
controller: controller),
],
);
}

View 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,
),
),
);
}
}

View File

@ -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();

View File

@ -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,
);
}
}

View File

@ -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 = Get.put(PhoneGalleryController(widget.config,
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;
isRecent: widget.startWithRecent);
}
if (widget.sheetController != null) {
if (GetInstance().isRegistered<BottomSheetController>()) {
bottomSheetController = Get.find<BottomSheetController>();
} else {
bottomSheetController =
Get.put(BottomSheetController(widget.sheetController!));
}
bottomSheetController!.galleryController = galleryController;
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 (!galleryController.isInitialized) {
galleryController.initializeAlbums();
}
if (galleryController.isRecent) {
_scrollController = PageController(initialPage: 0);
}
super.initState();
}
@ -90,21 +83,22 @@ 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(
? controller.permissionGranted
? PageView(
controller: controller.pageController,
physics: const NeverScrollableScrollPhysics(),
children: [
Scaffold(
backgroundColor: config.backgroundColor,
appBar: PickerAppBar(
controller: controller,
bottomSheetController: bottomSheetController,
isBottomSheet: widget.isBottomSheet,
),
body: Column(
children: [
@ -129,9 +123,10 @@ class _GalleryPickerState extends State<GalleryPickerView> {
width: width / 2,
child: TextButton(
onPressed: () {
_scrollController.animateTo(0,
duration:
const Duration(milliseconds: 50),
controller.pickerPageController
.animateToPage(0,
duration: const Duration(
milliseconds: 50),
curve: Curves.easeIn);
setState(() {
controller.isRecent = true;
@ -157,9 +152,10 @@ class _GalleryPickerState extends State<GalleryPickerView> {
width: width / 2,
child: TextButton(
onPressed: () {
_scrollController.animateTo(width,
duration:
const Duration(milliseconds: 50),
controller.pickerPageController
.animateToPage(1,
duration: const Duration(
milliseconds: 50),
curve: Curves.easeIn);
controller.isRecent = false;
controller.switchPickerMode(false);
@ -176,7 +172,7 @@ class _GalleryPickerState extends State<GalleryPickerView> {
),
Expanded(
child: PageView(
controller: _scrollController,
controller: controller.pickerPageController,
onPageChanged: (value) {
if (value == 0) {
controller.isRecent = true;
@ -188,46 +184,52 @@ class _GalleryPickerState extends State<GalleryPickerView> {
},
scrollDirection: Axis.horizontal,
children: [
controller.isInitialized
controller.isInitialized &&
controller.recent != null
? AlbumMediasView(
galleryAlbum: controller.recent!,
controller: controller,
isCollapsedSheet: widget.isCollapsedSheet,
isBottomSheet: widget.isBottomSheet,
singleMedia: widget.singleMedia)
: const Center(
child: CircularProgressIndicator(
color: Colors.grey,
)),
AlbumCategoriesView(controller)
AlbumCategoriesView(
controller: controller,
isBottomSheet: widget.isBottomSheet,
singleMedia: widget.singleMedia,
)
]),
),
],
),
)
: AlbumPage(
),
AlbumPage(
album: controller.selectedAlbum,
controller: controller,
album: controller.selectedAlbum!,
singleMedia: widget.singleMedia,
isCollapsedSheet: widget.isCollapsedSheet,
bottomSheetController: bottomSheetController,
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));
isRecent: widget.startWithRecent);
if (!controller.isInitialized) {
await controller.initializeAlbums();
if (bottomSheetController != null) {
bottomSheetController!.galleryController = galleryController;
}
setState(() {});
},

View 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"),
)
],
),
);
}
}

View File

@ -1,28 +1,22 @@
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(
return AppBar(
elevation: 0,
backgroundColor: controller.config.appbarColor,
leading: TextButton(
onPressed: () async {
if (GetInstance().isRegistered<BottomSheetController>()) {
bottomSheetController!.close();
if (isBottomSheet) {
BottomSheetPanel.close();
} else {
Navigator.pop(context);
await Future.delayed(const Duration(milliseconds: 500));
@ -46,7 +40,6 @@ class PickerAppBar extends StatelessWidget with PreferredSizeWidget {
))
: const SizedBox()
],
),
);
}

View File

@ -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;
}
}

View 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,
)),
),
);
}
}

View File

@ -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,61 +44,75 @@ 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(
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 (file.thumbnailFailed)
Container(
color: adjustFailedBgColor(),
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 &&
!isCollapsedSheet &&
controller.heroBuilder != null)
Hero(
tag: file.medium.id,
tag: file.id,
child: FadeInImage(
width: width,
height: height,
fadeInDuration: const Duration(milliseconds: 200),
fit: BoxFit.cover,
fit: fit,
placeholder: MemoryImage(kTransparentImage),
image: MemoryImage(file.thumbnail!),
),
)
else if (file.thumbnail != null && controller.heroBuilder == null)
else if (file.thumbnail != null &&
controller.heroBuilder == null)
FadeInImage(
width: width,
height: height,
fadeInDuration: const Duration(milliseconds: 200),
fit: BoxFit.cover,
fit: fit,
placeholder: MemoryImage(kTransparentImage),
image: MemoryImage(file.thumbnail!),
)
else
const SizedBox(),
if (file.thumbnail != null && !file.thumbnailFailed)
SizedBox(
width: width,
height: height,
),
if (!noIcon && file.thumbnail != null)
Positioned(
bottom: 10,
left: 10,
@ -89,7 +121,22 @@ class ThumbnailMediaFile extends StatelessWidget {
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,
),
),
),
],
),
),
),
);
});
}

View File

@ -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