diff --git a/.flutter-plugins b/.flutter-plugins
index 0b32444..51be248 100644
--- a/.flutter-plugins
+++ b/.flutter-plugins
@@ -1,4 +1,9 @@
# This is a generated file; do not edit or check into version control.
+camera=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\camera-0.10.1\\
+camera_android=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\camera_android-0.10.2\\
+camera_avfoundation=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\camera_avfoundation-0.9.10\\
+camera_web=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\camera_web-0.3.1\\
+flutter_plugin_android_lifecycle=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\flutter_plugin_android_lifecycle-2.0.7\\
permission_handler=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\permission_handler-10.2.0\\
permission_handler_android=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\permission_handler_android-10.2.0\\
permission_handler_apple=C:\\src\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\permission_handler_apple-9.0.7\\
diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies
index b28d961..5e338ac 100644
--- a/.flutter-plugins-dependencies
+++ b/.flutter-plugins-dependencies
@@ -1 +1 @@
-{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"permission_handler_apple","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler_apple-9.0.7\\\\","native_build":true,"dependencies":[]},{"name":"photo_gallery","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\photo_gallery-1.1.1\\\\","native_build":true,"dependencies":[]},{"name":"video_player_avfoundation","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_avfoundation-2.3.8\\\\","native_build":true,"dependencies":[]},{"name":"video_thumbnail","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_thumbnail-0.5.3\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"permission_handler_android","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler_android-10.2.0\\\\","native_build":true,"dependencies":[]},{"name":"photo_gallery","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\photo_gallery-1.1.1\\\\","native_build":true,"dependencies":[]},{"name":"video_player_android","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_android-2.3.10\\\\","native_build":true,"dependencies":[]},{"name":"video_thumbnail","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_thumbnail-0.5.3\\\\","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[{"name":"permission_handler_windows","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler_windows-0.1.2\\\\","native_build":true,"dependencies":[]}],"web":[{"name":"video_player_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_web-2.0.13\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]},{"name":"photo_gallery","dependencies":[]},{"name":"video_player","dependencies":["video_player_android","video_player_avfoundation","video_player_web"]},{"name":"video_player_android","dependencies":[]},{"name":"video_player_avfoundation","dependencies":[]},{"name":"video_player_web","dependencies":[]},{"name":"video_thumbnail","dependencies":[]}],"date_created":"2022-12-25 04:38:44.497275","version":"3.3.9"}
\ No newline at end of file
+{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"camera_avfoundation","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\camera_avfoundation-0.9.10\\\\","native_build":true,"dependencies":[]},{"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":"camera_android","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\camera_android-0.10.2\\\\","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_plugin_android_lifecycle","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_plugin_android_lifecycle-2.0.7\\\\","native_build":true,"dependencies":[]},{"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":"camera_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\camera_web-0.3.1\\\\","dependencies":[]},{"name":"video_player_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\video_player_web-2.0.13\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"camera","dependencies":["camera_android","camera_avfoundation","camera_web","flutter_plugin_android_lifecycle"]},{"name":"camera_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"camera_avfoundation","dependencies":[]},{"name":"camera_web","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"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":"2022-12-29 08:39:33.547831","version":"3.3.9"}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41cc7d8..98a80ff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,3 @@
## 0.0.1
-* TODO: Describe initial release.
+* Init
diff --git a/README.md b/README.md
index 02fe8ec..0c8bc73 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,422 @@
-
-
-TODO: Put a short description of the package here that helps potential users
-know whether this package might be useful for them.
+
## Features
-TODO: List what your package can do. Maybe include images, gifs, or videos.
+[✔] Modern design
+
+[✔] Detailed documentation
+
+[✔] Pick a media file
+
+[✔] Pick multiple media files
+
+[✔] BottomSheet layout
+
+[✔] Fetch all media files from your phone
+
+[✔] Comprehensively customizable design (desitination page, hero destination page...)
+
+[✔] Gallery picker listener
+
+[✔] Thumbnail widgets for media files
+
+[✔] MediaProvider widgets to view video / image files
+
+[✔] Gallery picker StreamBuilder to update your design if selects any file in gallery picker (GalleryPickerBuilder)
+
+[✔] Ready-to-use widgets
+
+[✔] Examples provided (example/lib/examples)
+
+[✔] Null-safety
+
+
## Getting started
-TODO: List prerequisites and provide or point to information on how to
-start using the package.
+1) Update kotlin version to `1.6.0` and `classpath 'com.android.tools.build:gradle:7.0.4'` in your `build.gradle`
+2) In `android` set the `minSdkVersion` to `25` in your `build.gradle`
+
+#### Android
+Add uses-permission `android/app/src/main/AndroidManifest.xml` file
+ ```xml
+
+ ```
+#### Ios
+Add these configurations to your `ios/Runner/info.plist` file ??????????
+ ```xml
+ NSPhotoLibraryUsageDescription
+ Privacy - Photo Library Usage Description
+ NSMotionUsageDescription
+ Motion usage description
+ NSPhotoLibraryAddUsageDescription
+ NSPhotoLibraryAddUsageDescription
+ ```
## Usage
-TODO: Include short and useful examples for package users. Add longer examples
-to `/example` folder.
+Quick and simple usage example:
+
+### Pick Single File
```dart
-const like = 'sample';
+MediaFile? media = await GalleryPicker.pickMedias(context: context,singleMedia: true);
+```
+### Pick Multiple Files
+
+```dart
+List? medias = await GalleryPicker.pickMedias(context: context);
```
-## Additional information
+### Get All Media Files in Gallery
-TODO: Tell users more about the package: where to find more information, how to
-contribute to the package, how to file issues, what response they can expect
-from the package authors, and more.
+```dart
+GalleryMedia? allmedia = await GalleryPicker.collectGallery;
+```
+
+### Listen selected files inside gallery picker
+
+```dart
+Stream stream = GalleryPicker.listenSelectedFiles;
+```
+Dispose listener
+```dart
+GalleryPicker.disposeSelectedFilesListener();
+```
+
+### BottomSheetLayout
+
+Gallery Picker could also work as a bottom sheet. Wrap your scaffold's body with BottomSheetLayout
+
+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(
+ onSelect: (medias) {},
+ child: Column(
+ children: [
+```
+
+### Customizable destination page
+
+Within the Gallery Picker you can design a page that will be redirected after selecting any image(s).
+
+Note: There are two builder called multipleMediasBuilder and heroBuilder. If you designed both of them, multipleMediasBuilder will be shown after picking multiple media files, heroBuilder will be shown after picking a single media. Use given hero tag to view your Hero image. You can see a simple example below.
+
+There is an example at `example/lib/examples/pick_medias_with_builder.dart` to see how it could be done.
+
+```dart
+ GalleryPicker.pickMediasWithBuilder(
+ multipleMediasBuilder: ((medias, context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Flippers Page'),
+ ),
+ body: GridView.count(
+ crossAxisCount: 3,
+ mainAxisSpacing: 5,
+ crossAxisSpacing: 5,
+ children: [
+ for (var media in medias)
+ ThumbnailMedia(
+ media: media,
+ )
+ ],
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => MyHomePage(
+ title: "Selected Medias",
+ medias: medias,
+ )),
+ );
+ GalleryPicker.dispose();
+ },
+ child: const Icon(
+ Icons.send,
+ color: Colors.white,
+ ),
+ ),
+ );
+ }),
+ heroBuilder: (tag, media, context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Flippers Page'),
+ ),
+ body: Container(
+ color: Colors.lightBlueAccent,
+ padding: const EdgeInsets.all(16.0),
+ alignment: Alignment.topLeft,
+ child: Hero(
+ tag: tag,
+ child: Image.memory(media.thumbnail!),
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ backgroundColor: Colors.orange,
+ onPressed: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => MyHomePage(
+ title: "Selected Medias",
+ medias: [media],
+ )),
+ );
+ GalleryPicker.dispose();
+ },
+ child: const Icon(
+ Icons.send,
+ color: Colors.white,
+ ),
+ ),
+ );
+ },
+ context: context);
+```
+
+### Dispose Gallery picker
+
+```dart
+GalleryPicker.dispose();
+```
+
+## Customize your gallery picker
+
+A Config class is provided to user to customize your gallery picker. You can customize any feature you want and select appearance mode.
+
+#### Customizable appereance features
+```dart
+List? medias = await GalleryPicker.pickMedias(
+ context: context,
+ config: Config(
+ backgroundColor: Colors.white,
+ appbarColor: Colors.white,
+ bottomSheetColor: const Color.fromARGB(255, 247, 248, 250),
+ appbarIconColor: const Color.fromARGB(255, 130, 141, 148),
+ underlineColor: const Color.fromARGB(255, 20, 161, 131),
+ selectedMenuStyle: const TextStyle(color: Colors.black),
+ unselectedMenuStyle:
+ const TextStyle(color: Color.fromARGB(255, 102, 112, 117)),
+ textStyle: const TextStyle(
+ color: Color.fromARGB(255, 108, 115, 121),
+ fontWeight: FontWeight.bold),
+ appbarTextStyle: const TextStyle(color: Colors.black),
+ recents: "RECENTS",
+ gallery: "GALLERY",
+ lastMonth: "Last Month",
+ lastWeek: "Last Week",
+ tapPhotoSelect: "Tap photo to select",
+ selected: "Selected",
+ months: [
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December"
+ ],
+ selectIcon: Container(
+ width: 50,
+ height: 50,
+ decoration: const BoxDecoration(
+ shape: BoxShape.circle,
+ color: Color.fromARGB(255, 0, 168, 132),
+ ),
+ child: const Icon(
+ Icons.check,
+ color: Colors.white,
+ ),
+ ),
+ ),
+ )
+```
+
+#### Appearance Mode
+```dart
+List? medias = await GalleryPicker.pickMedias(
+ context: context,
+ config: Config(
+ mode: Mode.dark
+ ),
+ )
+```
+
+#### Give an initial selected media files
+
+```dart
+List? medias = await GalleryPicker.pickMedias(
+ context: context,
+ initSelectedMedias: this.selectedMedias,
+ )
+```
+#### Select your priority page
+
+There are two pages called "Recent" and "Gallery". You could change the initial page.
+
+```dart
+List? medias = await GalleryPicker.pickMedias(
+ context: context,
+ startWithRecent: true,
+ )
+```
+
+## MediaFile
+GalleryPicker returns MediaFile list. You can reach out features below.
+
+[✔] Medium
+[✔] Id
+[✔] MediumType
+[✔] Thumbnail
+[✔] Check with thumbnailFailed if fetching thumbnail fails
+[✔] Check with fileFailed if getting file fails
+[✔] File
+[✔] getThumbnail function
+[✔] getFile function
+[✔] getData function
+[✔] Check if the file selected in gallery picker
+
+## Ready-to-use widgets
+
+### ThumbnailMedia
+
+```dart
+ThumbnailMedia(
+ media: media,
+)
+```
+### ThumbnailAlbum
+
+```dart
+ThumbnailAlbum(
+ album: album,
+ failIconColor: failIconColor,
+ mode: mode,
+ backgroundColor: backgroundColor,
+)
+```
+### PhotoProvider
+
+```dart
+PhotoProvider(
+ media: media,
+)
+```
+### VideoProvider
+
+```dart
+VideoProvider(
+ media: media,
+)
+```
+### MediaProvider
+MediaProvider works with every media type
+
+```dart
+MediaProvider(
+ media: media,
+)
+```
+
+### GalleryPickerBuilder
+
+You can listen and update your design through this builder
+
+```dart
+GalleryPickerBuilder(
+ builder: (selectedFiles, context) {
+ return child
+ },
+)
+```
+### AlbumMediasView
+
+View all media files in the album sorted by its creation date
+
+```dart
+GalleryMedia? allmedia = await GalleryPicker.collectGallery;
+```
+
+```dart
+AlbumMediasView(
+ galleryAlbum: allmedia!.albums[0],
+ textStyle: textStyle,
+)
+```
+### AlbumCategoriesView
+
+View all album categories
+
+```dart
+GalleryMedia? allmedia = await GalleryPicker.collectGallery;
+```
+```dart
+AlbumCategoriesView(
+ albums: allmedia!.albums,
+ categoryBackgroundColor: categoryBackgroundColor,
+ categoryFailIconColor: categoryFailIconColor,
+ mode: mode,
+ onFocusChange: onFocusChange,
+ onHover: onHover,
+ onLongPress: onLongPress,
+ onPressed: onPressed,
+)
+```
+
+## Examples
+Check out our examples!
+### Standart Gallery Picker
+`example/lib/examples/gallery_picker_example.dart`
+### Pick Media Files With Destination Page
+`example/lib/examples/pick_medias_with_builder.dart`
+### BottomSheet Example
+`example/lib/examples/bottom_sheet_example.dart`
+### WhatsApp Pick Photo Page
+`example/lib/examples/whatsapp_pick_photo.dart`
+
+## This package was possible to create with:
+- The [photo_gallery](https://pub.dev/packages/photo_gallery) package
+- The [transparent_image](https://pub.dev/packages/transparent_image) package
+- The [get](https://pub.dev/packages/get) package
+- The [video_player](https://pub.dev/packages/video_player) package
+- The [intl](https://pub.dev/packages/intl) package
+- The [bottom_sheet_bar](https://pub.dev/packages/bottom_sheet_bar) package
+- The [platform_info](https://pub.dev/packages/platform_info) package
+- The [permission_handler](https://pub.dev/packages/permission_handler) package
\ No newline at end of file
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index 7604f91..cde9521 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -2,6 +2,7 @@
package="com.example.gallery_picker_example">
+
createState() => _BottomSheetExampleState();
+}
+
+class _BottomSheetExampleState extends State {
+ List selectedMedias = [];
+ int pageIndex = 0;
+ var controller = PageController(initialPage: 0);
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.white,
+ body: BottomSheetLayout(
+ onSelect: (List selectedMedias) {
+ this.selectedMedias = selectedMedias;
+ pageIndex = 0;
+ if (this.selectedMedias.isNotEmpty) {
+ Future.delayed(Duration(milliseconds: 500)).then((value) {
+ controller.animateToPage(0,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ });
+ }
+ setState(() {});
+ },
+ config: Config(mode: Mode.dark),
+ child: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Spacer(),
+ const Text(
+ 'These are your selected medias',
+ ),
+ const Divider(),
+ Expanded(
+ flex: 5,
+ child: Stack(children: [
+ if (selectedMedias.isNotEmpty)
+ PageView(
+ controller: controller,
+ children: [
+ for (var media in selectedMedias)
+ Center(
+ child: MediaProvider(
+ media: media,
+ ),
+ )
+ ],
+ ),
+ if (selectedMedias.isNotEmpty)
+ Align(
+ alignment: Alignment.centerRight,
+ child: TextButton(
+ onPressed: () {
+ if (pageIndex < selectedMedias.length - 1) {
+ pageIndex++;
+ controller.animateToPage(pageIndex,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ setState(() {});
+ }
+ },
+ child: const Icon(
+ Icons.chevron_right,
+ size: 100,
+ color: Colors.red,
+ )),
+ ),
+ if (selectedMedias.isNotEmpty)
+ Align(
+ alignment: Alignment.centerLeft,
+ child: TextButton(
+ onPressed: () {
+ if (pageIndex > 0) {
+ pageIndex--;
+ controller.animateToPage(pageIndex,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ setState(() {});
+ }
+ },
+ child: const Icon(
+ Icons.chevron_left,
+ size: 100,
+ color: Colors.red,
+ )),
+ ),
+ ]),
+ ),
+ SizedBox(
+ height: 65,
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: [
+ for (int i = 0; i < selectedMedias.length; i++)
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 5),
+ child: TextButton(
+ onPressed: () {
+ pageIndex = i;
+ 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],
+ )),
+ ),
+ )
+ ],
+ ),
+ ),
+ Spacer(
+ flex: 1,
+ ),
+ TextButton(
+ onPressed: () {
+ print("lol");
+ GalleryPicker.openSheet();
+ },
+ child: const Icon(
+ Icons.open_in_new,
+ size: 40,
+ ),
+ ),
+ Spacer(
+ flex: 1,
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/example/lib/examples/gallery_picker_example.dart b/example/lib/examples/gallery_picker_example.dart
new file mode 100644
index 0000000..26b692b
--- /dev/null
+++ b/example/lib/examples/gallery_picker_example.dart
@@ -0,0 +1,143 @@
+import 'package:flutter/material.dart';
+import 'package:gallery_picker/gallery_picker.dart';
+
+class GalleryPickerExample extends StatefulWidget {
+ const GalleryPickerExample({super.key});
+
+ @override
+ State createState() => _GalleryPickerExampleState();
+}
+
+class _GalleryPickerExampleState extends State {
+ List selectedMedias = [];
+ int pageIndex = 0;
+ var controller = PageController(initialPage: 0);
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text("Pick medias"),
+ ),
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Spacer(),
+ const Text(
+ 'These are your selected medias',
+ ),
+ const Divider(),
+ Expanded(
+ flex: 5,
+ child: Stack(children: [
+ if (selectedMedias.isNotEmpty)
+ PageView(
+ controller: controller,
+ children: [
+ for (var media in selectedMedias)
+ Center(
+ child: MediaProvider(
+ media: media,
+ ),
+ )
+ ],
+ ),
+ if (selectedMedias.isNotEmpty)
+ Align(
+ alignment: Alignment.centerRight,
+ child: TextButton(
+ onPressed: () {
+ if (pageIndex < selectedMedias.length - 1) {
+ pageIndex++;
+ controller.animateToPage(pageIndex,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ setState(() {});
+ }
+ },
+ child: const Icon(
+ Icons.chevron_right,
+ size: 100,
+ color: Colors.red,
+ )),
+ ),
+ if (selectedMedias.isNotEmpty)
+ Align(
+ alignment: Alignment.centerLeft,
+ child: TextButton(
+ onPressed: () {
+ if (pageIndex > 0) {
+ pageIndex--;
+ controller.animateToPage(pageIndex,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ setState(() {});
+ }
+ },
+ child: const Icon(
+ Icons.chevron_left,
+ size: 100,
+ color: Colors.red,
+ )),
+ ),
+ ]),
+ ),
+ SizedBox(
+ height: 65,
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: [
+ for (int i = 0; i < selectedMedias.length; i++)
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 5),
+ child: TextButton(
+ onPressed: () {
+ pageIndex = i;
+ 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: 2,
+ ),
+ ],
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: pickMedias,
+ tooltip: 'Increment',
+ child: const Icon(Icons.add),
+ ),
+ );
+ }
+
+ Future pickMedias() async {
+ List? medias = await GalleryPicker.pickMedias(
+ context: context,
+ config: Config(mode: Mode.dark),
+ );
+ if (medias != null) {
+ setState(() {
+ selectedMedias += medias;
+ });
+ }
+ }
+}
diff --git a/example/lib/examples/multiple_medias.dart b/example/lib/examples/multiple_medias.dart
new file mode 100644
index 0000000..2fc7abb
--- /dev/null
+++ b/example/lib/examples/multiple_medias.dart
@@ -0,0 +1,131 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:gallery_picker/gallery_picker.dart';
+
+import '../main.dart';
+
+class MultipleMediasView extends StatefulWidget {
+ final List medias;
+ const MultipleMediasView(this.medias, {super.key});
+
+ @override
+ State createState() => _MultipleMediasViewState();
+}
+
+class _MultipleMediasViewState extends State {
+ int pageIndex = 0;
+ var controller = PageController(initialPage: 0);
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text("Destination Page"),
+ ),
+ body: Column(children: [
+ const Spacer(),
+ Text("These are your selected medias"),
+ Divider(),
+ Expanded(
+ flex: 5,
+ child: Stack(children: [
+ PageView(
+ controller: controller,
+ children: [
+ for (var media in widget.medias)
+ Center(
+ child: MediaProvider(
+ media: media,
+ ),
+ )
+ ],
+ ),
+ Align(
+ alignment: Alignment.centerRight,
+ child: TextButton(
+ onPressed: () {
+ print(pageIndex);
+ if (pageIndex < widget.medias.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,
+ )),
+ ),
+ 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,
+ )),
+ ),
+ ]),
+ ),
+ const Divider(),
+ SizedBox(
+ height: 50,
+ child: ListView(scrollDirection: Axis.horizontal, children: [
+ for (int i = 0; i < widget.medias.length; i++)
+ TextButton(
+ onPressed: () {
+ pageIndex=i;
+ controller.animateToPage(i,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ setState(() {});
+ },
+ child: Container(
+ width: 50,
+ height: 50,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: i == pageIndex ? Colors.red : Colors.grey),
+ child: Text(
+ (i + 1).toString(),
+ style: TextStyle(fontSize: 16, color: Colors.black),
+ ),
+ ))
+ ]),
+ ),
+ const Spacer(),
+ ]),
+ floatingActionButton: FloatingActionButton(
+ backgroundColor: Colors.blue,
+ onPressed: () {
+ GalleryPicker.dispose();
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => MyHomePage(
+ title: "Selected Medias",
+ medias: widget.medias,
+ )),
+ );
+ },
+ child: const Icon(
+ Icons.send,
+ color: Colors.white,
+ ),
+ ),
+ );
+ }
+}
diff --git a/example/lib/examples/pick_medias_with_builder.dart b/example/lib/examples/pick_medias_with_builder.dart
new file mode 100644
index 0000000..0df5cb8
--- /dev/null
+++ b/example/lib/examples/pick_medias_with_builder.dart
@@ -0,0 +1,94 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:gallery_picker/gallery_picker.dart';
+
+import '../main.dart';
+import 'multiple_medias.dart';
+
+class PickMediasWithBuilder extends StatefulWidget {
+ const PickMediasWithBuilder({super.key});
+
+ @override
+ State createState() => _PickMediasWithBuilderState();
+}
+
+class _PickMediasWithBuilderState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text("Pick Medias With Builder"),
+ ),
+ body: Stack(
+ children: [
+ Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Spacer(),
+ TextButton(
+ onPressed: pickMediasWithBuilder,
+ child: Container(
+ width: 300,
+ height: 60,
+ decoration: BoxDecoration(
+ color: Colors.blue,
+ borderRadius: BorderRadius.circular(20)),
+ alignment: Alignment.center,
+ child: Text(
+ 'Pick Medias With Builder',
+ style: TextStyle(color: Colors.white),
+ ),
+ )),
+ const Spacer()
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ pickMediasWithBuilder() {
+ GalleryPicker.pickMediasWithBuilder(
+ multipleMediasBuilder: ((medias, context) {
+ return MultipleMediasView(medias);
+ }),
+ heroBuilder: (tag, media, context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Hero Page'),
+ ),
+ body: Center(
+ child: Hero(
+ tag: tag,
+ child: MediaProvider(
+ media: media,
+ width: MediaQuery.of(context).size.width - 50,
+ height: 300,
+ ),
+ )),
+ floatingActionButton: FloatingActionButton(
+ backgroundColor: Colors.blue,
+ onPressed: () {
+ GalleryPicker.dispose();
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => MyHomePage(
+ title: "Selected Medias",
+ medias: [media],
+ )),
+ );
+ },
+ child: const Icon(
+ Icons.send,
+ color: Colors.white,
+ ),
+ ),
+ );
+ },
+ context: context);
+ }
+}
diff --git a/example/lib/examples/whatsapp_pick_photo.dart b/example/lib/examples/whatsapp_pick_photo.dart
new file mode 100644
index 0000000..1353414
--- /dev/null
+++ b/example/lib/examples/whatsapp_pick_photo.dart
@@ -0,0 +1,284 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:gallery_picker/gallery_picker.dart';
+import 'package:camera/camera.dart';
+import 'package:gallery_picker_example/examples/multiple_medias.dart';
+
+class WhatsappPickPhoto extends StatefulWidget {
+ const WhatsappPickPhoto({super.key});
+
+ @override
+ State createState() => _WhatsappPickPhotoState();
+}
+
+class _WhatsappPickPhotoState extends State {
+ CameraController? cameraController;
+ GalleryMedia? gallery;
+ List selectedMedias = [];
+ List? cameras;
+ CameraLensDirection cameraLensDirection = CameraLensDirection.front;
+ @override
+ void initState() {
+ initCamera();
+ fetchMedias();
+ GalleryPicker.listenSelectedFiles.listen((medias) {
+ selectedMedias = medias;
+ setState(() {});
+ });
+ super.initState();
+ }
+
+ Future fetchMedias() async {
+ gallery = await GalleryPicker.collectGallery;
+ setState(() {});
+ }
+
+ Future initCamera() async {
+ print(cameraLensDirection);
+ cameraController = null;
+ setState(() {});
+ cameras ??= await availableCameras();
+ cameraController = CameraController(
+ cameras!.firstWhere(
+ (camera) => camera.lensDirection == cameraLensDirection),
+ ResolutionPreset.max);
+ cameraController!.initialize().then((_) {
+ if (!mounted) {
+ return;
+ }
+ setState(() {});
+ }).catchError((Object e) {
+ if (e is CameraException) {
+ switch (e.code) {
+ case 'CameraAccessDenied':
+ // Handle access errors here.
+ break;
+ default:
+ // Handle other errors here.
+ break;
+ }
+ }
+ });
+ }
+
+ bool isRecording = false;
+ bool anyProcess = false;
+ @override
+ void dispose() {
+ cameraController!.dispose();
+ GalleryPicker.disposeSelectedFilesListener();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.transparent,
+ body: BottomSheetLayout(
+ onSelect: (List selectedMedias) {
+ this.selectedMedias = selectedMedias;
+ setState(() {});
+ },
+ initSelectedMedias: 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!,
+ ),
+ ),
+ 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,
+ ),
+ )),
+ 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(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:
+ anyProcess ? Colors.red : Colors.transparent,
+ shape: BoxShape.circle,
+ ),
+ ),
+ ),
+ ),
+ TextButton(
+ onPressed: () {
+ if (cameraLensDirection ==
+ CameraLensDirection.front) {
+ cameraLensDirection = CameraLensDirection.back;
+ } else {
+ cameraLensDirection = CameraLensDirection.front;
+ }
+ initCamera();
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ color: Colors.black.withOpacity(0.5),
+ shape: BoxShape.circle),
+ padding: const EdgeInsets.all(8),
+ child: const Icon(
+ Icons.cameraswitch,
+ size: 20,
+ color: Colors.white,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ))
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/example/lib/main.dart b/example/lib/main.dart
index f9c1d1c..ddff7b0 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -1,6 +1,10 @@
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:gallery_picker/gallery_picker.dart';
-import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
+import 'package:gallery_picker_example/examples/pick_medias_with_builder.dart';
+import 'examples/gallery_picker_example.dart';
+import 'examples/multiple_medias.dart';
+import 'examples/whatsapp_pick_photo.dart';
void main() {
runApp(const MyApp());
@@ -9,30 +13,27 @@ void main() {
class MyApp extends StatelessWidget {
const MyApp({super.key});
- // This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
- primarySwatch: Colors.blue,
+ brightness: Brightness.light,
+ /* light theme settings */
),
- home: const MyHomePage(title: 'Flutter Demo Home Page'),
+ darkTheme: ThemeData(
+ brightness: Brightness.dark,
+ /* dark theme settings */
+ ),
+ themeMode: ThemeMode.dark,
+ home: const GalleryPickerExample(),
);
}
}
class MyHomePage extends StatefulWidget {
- const MyHomePage({super.key, required this.title});
-
- // This widget is the home page of your application. It is stateful, meaning
- // that it has a State object (defined below) that contains fields that affect
- // how it looks.
-
- // This class is the configuration for the state. It holds the values (in this
- // case the title) provided by the parent (in this case the App widget) and
- // used by the build method of the State. Fields in a Widget subclass are
- // always marked "final".
+ final List? medias;
+ const MyHomePage({super.key, required this.title, this.medias});
final String title;
@@ -41,213 +42,191 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State {
- int _counter = 0;
-
- void viewGalleryPicker() {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => GalleryPicker(
- onSelect: (selectedMedias) {
- print(selectedMedias.length);
- },
- heroBuilder: (tag, media, context) {
- return Scaffold(
- appBar: AppBar(
- title: const Text('Flippers Page'),
- ),
- body: Container(
- // The blue background emphasizes that it's a new route.
- color: Colors.lightBlueAccent,
- padding: const EdgeInsets.all(16.0),
- alignment: Alignment.topLeft,
- child: Hero(
- tag: tag,
- child: Image.memory(media.thumbnail!),
- ),
- ),
- floatingActionButton: FloatingActionButton(
- onPressed: () {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => MyHomePage(
- title: "messages",
- )),
- );
- disposeGalleryPicker();
- },
- child: Icon(Icons.send),
- ),
- );
- },
- multipleMediaBuilder: (medias, context) {
- return Scaffold(
- appBar: AppBar(
- title: const Text('Flippers Page'),
- ),
- body: GridView.count(
- crossAxisCount: 3,
- mainAxisSpacing: 5,
- crossAxisSpacing: 5,
- children: [
- for (var media in medias) Image.memory(media.thumbnail!)
- ],
- ),
- floatingActionButton: FloatingActionButton(
- onPressed: () {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => MyHomePage(
- title: "messages",
- )),
- );
- disposeGalleryPicker();
- },
- child: Icon(Icons.send),
- ),
- );
- },
- )),
- );
- }
-
- showBottomSheet() {
- showMaterialModalBottomSheet(
- context: context,
- builder: (context) => SingleChildScrollView(
- controller: ModalScrollController.of(context),
- child: SizedBox(
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.height,
- child: GalleryPicker(
- onSelect: (selectedMedias) {
- print(selectedMedias.length);
- },
- heroBuilder: (heroId, media, context) {
- return Scaffold(
- appBar: AppBar(
- title: const Text('Flippers Page'),
- ),
- body: Container(
- // The blue background emphasizes that it's a new route.
- color: Colors.lightBlueAccent,
- padding: const EdgeInsets.all(16.0),
- alignment: Alignment.topLeft,
- child: GestureDetector(
- onTap: () => Navigator.pop(context),
- child: Hero(
- tag: heroId,
- child: Image.memory(media.thumbnail!),
- ),
- ),
- ),
- floatingActionButton: FloatingActionButton(
- backgroundColor: Colors.orange,
- onPressed: () {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => MyHomePage(
- title: "messages",
- )),
- );
- disposeGalleryPicker();
- },
- child: Icon(
- Icons.send,
- color: Colors.white,
- ),
- ),
- );
- },
- multipleMediaBuilder: (medias, context) {
- return Scaffold(
- appBar: AppBar(
- title: const Text('Flippers Page'),
- ),
- body: GridView.count(
- crossAxisCount: 3,
- mainAxisSpacing: 5,
- crossAxisSpacing: 5,
- children: [
- for (var media in medias) Image.memory(media.thumbnail!)
- ],
- ),
- floatingActionButton: FloatingActionButton(
- onPressed: () {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => MyHomePage(
- title: "messages",
- )),
- );
- disposeGalleryPicker();
- },
- child: Icon(
- Icons.send,
- color: Colors.white,
- ),
- ),
- );
- },
- ),
- ),
- ),
- );
- }
+ List selectedMedias = [];
+ @override
+ void initState() {
+ if (widget.medias != null) {
+ selectedMedias = widget.medias!;
+ }
+ super.initState();
+ }
+
+ int pageIndex = 0;
+ var controller = PageController(initialPage: 0);
@override
Widget build(BuildContext context) {
- // This method is rerun every time setState is called, for instance as done
- // by the _incrementCounter method above.
- //
- // The Flutter framework has been optimized to make rerunning build methods
- // fast, so that you can just rebuild anything that needs updating rather
- // than having to individually change instances of widgets.
return Scaffold(
+
appBar: AppBar(
- // Here we take the value from the MyHomePage object that was created by
- // the App.build method, and use it to set our appbar title.
- title: Text(widget.title),
+ title: Text("Pick medias"),
),
- body: Stack(
- children: [
- Center(
- // Center is a layout widget. It takes a single child and positions it
- // in the middle of the parent.
- child: Column(
- // Column is also a layout widget. It takes a list of children and
- // arranges them vertically. By default, it sizes itself to fit its
- // children horizontally, and tries to be as tall as its parent.
- //
- // Invoke "debug painting" (press "p" in the console, choose the
- // "Toggle Debug Paint" action from the Flutter Inspector in Android
- // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
- // to see the wireframe for each widget.
- //
- // Column has various properties to control how it sizes itself and
- // how it positions its children. Here we use mainAxisAlignment to
- // center the children vertically; the main axis here is the vertical
- // axis because Columns are vertical (the cross axis would be
- // horizontal).
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- const Text(
- 'You have pushed the button this many times:',
- ),
- ],
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Spacer(),
+ const Text(
+ 'These are your selected medias',
),
- ),
- ],
+ const Divider(),
+ Expanded(
+ flex: 5,
+ child: Stack(children: [
+ if (selectedMedias.isNotEmpty)
+ PageView(
+ controller: controller,
+ children: [
+ for (var media in selectedMedias)
+ Center(
+ child: MediaProvider(
+ media: media,
+ ),
+ )
+ ],
+ ),
+ if (selectedMedias.isNotEmpty)
+ Align(
+ alignment: Alignment.centerRight,
+ child: TextButton(
+ onPressed: () {
+ if (pageIndex < selectedMedias.length - 1) {
+ pageIndex++;
+ controller.animateToPage(pageIndex,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ setState(() {});
+ }
+ },
+ child: const Icon(
+ Icons.chevron_right,
+ size: 100,
+ color: Colors.red,
+ )),
+ ),
+ if (selectedMedias.isNotEmpty)
+ Align(
+ alignment: Alignment.centerLeft,
+ child: TextButton(
+ onPressed: () {
+ if (pageIndex > 0) {
+ pageIndex--;
+ controller.animateToPage(pageIndex,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn);
+ setState(() {});
+ }
+ },
+ child: const Icon(
+ Icons.chevron_left,
+ size: 100,
+ color: Colors.red,
+ )),
+ ),
+ ]),
+ ),
+ SizedBox(
+ height: 65,
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: [
+ for (int i = 0; i < selectedMedias.length; i++)
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 5),
+ child: TextButton(
+ onPressed: () {
+ pageIndex = i;
+ 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: 2,
+ ),
+ ],
+ ),
),
floatingActionButton: FloatingActionButton(
- onPressed: showBottomSheet,
+ onPressed: pickMedias,
tooltip: 'Increment',
child: const Icon(Icons.add),
- ), // This trailing comma makes auto-formatting nicer for build methods.
+ ),
);
}
+
+ Future pickMedias() async {
+ List? medias = await GalleryPicker.pickMedias(
+ context: context,
+ initSelectedMedias: selectedMedias,
+ startWithRecent: true);
+ if (medias != null) {
+ setState(() {
+ this.selectedMedias += medias;
+ });
+ }
+ }
+
+ pickMediasWithBuilder() {
+ GalleryPicker.pickMediasWithBuilder(
+ multipleMediasBuilder: ((medias, context) {
+ return MultipleMediasView(medias);
+ }),
+ heroBuilder: (tag, media, context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Hero Page'),
+ ),
+ body: Center(
+ child: Hero(
+ tag: tag,
+ child: MediaProvider(
+ media: media,
+ width: MediaQuery.of(context).size.width - 50,
+ height: 300,
+ ),
+ )),
+ floatingActionButton: FloatingActionButton(
+ backgroundColor: Colors.blue,
+ onPressed: () {
+ GalleryPicker.dispose();
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => MyHomePage(
+ title: "Selected Medias",
+ medias: [media],
+ )),
+ );
+ },
+ child: const Icon(
+ Icons.send,
+ color: Colors.white,
+ ),
+ ),
+ );
+ },
+ context: context);
+ }
+
+ Future getGalleryMedia() async {
+ GalleryMedia? allmedia = await GalleryPicker.collectGallery;
+ }
}
diff --git a/example/pubspec.lock b/example/pubspec.lock
index a0a5213..31e25d4 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -15,6 +15,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
+ bottom_sheet_bar:
+ dependency: transitive
+ description:
+ name: bottom_sheet_bar
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.3.8"
+ camera:
+ dependency: transitive
+ description:
+ name: camera
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.10.1"
+ camera_android:
+ dependency: transitive
+ description:
+ name: camera_android
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.10.2"
+ camera_avfoundation:
+ dependency: transitive
+ description:
+ name: camera_avfoundation
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.9.10"
+ camera_platform_interface:
+ dependency: transitive
+ description:
+ name: camera_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.3.2"
+ camera_web:
+ dependency: transitive
+ description:
+ name: camera_web
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.3.1"
characters:
dependency: transitive
description:
@@ -36,6 +78,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0"
+ cross_file:
+ dependency: transitive
+ description:
+ name: cross_file
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.3.3+2"
csslib:
dependency: transitive
description:
@@ -69,6 +118,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
+ flutter_plugin_android_lifecycle:
+ dependency: transitive
+ description:
+ name: flutter_plugin_android_lifecycle
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.7"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -135,6 +191,13 @@ 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:
@@ -142,13 +205,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
- modal_bottom_sheet:
- dependency: transitive
- description:
- name: modal_bottom_sheet
- url: "https://pub.dartlang.org"
- source: hosted
- version: "2.1.2"
path:
dependency: transitive
description:
@@ -157,7 +213,7 @@ packages:
source: hosted
version: "1.8.2"
permission_handler:
- dependency: transitive
+ dependency: "direct main"
description:
name: permission_handler
url: "https://pub.dartlang.org"
@@ -205,6 +261,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
+ quiver:
+ dependency: transitive
+ description:
+ name: quiver
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.2.1"
sky_engine:
dependency: transitive
description: flutter
@@ -231,6 +294,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
+ stream_transform:
+ dependency: transitive
+ description:
+ name: stream_transform
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
string_scanner:
dependency: transitive
description:
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 1a358ab..8c7bec3 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -36,6 +36,7 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
+ permission_handler: ^10.2.0
dev_dependencies:
flutter_test:
diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart
index ea29f78..c468256 100644
--- a/example/test/widget_test.dart
+++ b/example/test/widget_test.dart
@@ -8,23 +8,21 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:gallery_picker_example/main.dart';
-
void main() {
- testWidgets('Counter increments smoke test', (WidgetTester tester) async {
- // Build our app and trigger a frame.
- await tester.pumpWidget(const MyApp());
-
- // Verify that our counter starts at 0.
- expect(find.text('0'), findsOneWidget);
- expect(find.text('1'), findsNothing);
-
- // Tap the '+' icon and trigger a frame.
- await tester.tap(find.byIcon(Icons.add));
- await tester.pump();
-
- // Verify that our counter has incremented.
- expect(find.text('0'), findsNothing);
- expect(find.text('1'), findsOneWidget);
- });
+ //testWidgets('Counter increments smoke test', (WidgetTester tester) async {
+ // // Build our app and trigger a frame.
+ // await tester.pumpWidget(const MyApp());
+//
+ // // Verify that our counter starts at 0.
+ // expect(find.text('0'), findsOneWidget);
+ // expect(find.text('1'), findsNothing);
+//
+ // // Tap the '+' icon and trigger a frame.
+ // await tester.tap(find.byIcon(Icons.add));
+ // await tester.pump();
+//
+ // // Verify that our counter has incremented.
+ // expect(find.text('0'), findsNothing);
+ // expect(find.text('1'), findsOneWidget);
+ //});
}
diff --git a/lib/controller/bottom_sheet_controller.dart b/lib/controller/bottom_sheet_controller.dart
new file mode 100644
index 0000000..53691fc
--- /dev/null
+++ b/lib/controller/bottom_sheet_controller.dart
@@ -0,0 +1,41 @@
+import 'package:bottom_sheet_bar/bottom_sheet_bar.dart';
+import 'package:get/get.dart';
+
+import 'gallery_controller.dart';
+
+class BottomSheetController extends GetxController {
+ BottomSheetBarController sheetController;
+ PhoneGalleryController? galleryController;
+ bool isClosing = false;
+ bool appBarTapping = false;
+
+ Future open() async {
+ await sheetController.expand();
+ }
+
+ void tapingStatus(bool value) {
+ appBarTapping = value;
+ if (galleryController == null) {
+ Get.find().update();
+ } else {
+ galleryController!.update();
+ }
+ update();
+ }
+
+ Future close() async {
+ isClosing = true;
+ update();
+ await sheetController.collapse();
+ isClosing = false;
+ update();
+ }
+
+ void disposeController() {
+ isClosing = false;
+ appBarTapping = false;
+ GetInstance().delete();
+ }
+
+ BottomSheetController(this.sheetController);
+}
diff --git a/lib/controller/gallery_controller.dart b/lib/controller/gallery_controller.dart
index 311c151..c28b3c8 100644
--- a/lib/controller/gallery_controller.dart
+++ b/lib/controller/gallery_controller.dart
@@ -1,14 +1,16 @@
+import 'dart:io';
import 'dart:typed_data';
-
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
+import 'package:permission_handler/permission_handler.dart';
import 'package:photo_gallery/photo_gallery.dart';
import '../models/config.dart';
-import '../views/hero_page.dart';
+import '../models/gallery_media.dart';
import '/models/gallery_album.dart';
import '/models/medium.dart';
import '../models/media_file.dart';
+import 'picker_listener.dart';
class PhoneGalleryController extends GetxController {
late Config config;
@@ -17,15 +19,19 @@ class PhoneGalleryController extends GetxController {
{required this.onSelect,
required this.heroBuilder,
required this.isRecent,
- required this.multipleMediaBuilder}) {
+ List? initSelectedMedias,
+ required this.multipleMediasBuilder}) {
this.config = config ?? Config();
+ if (initSelectedMedias != null) {
+ _selectedFiles = initSelectedMedias.map((e) => e).toList();
+ }
}
bool isRecent;
Function(List selectedMedias) onSelect;
Widget Function(String tag, MediaFile media, BuildContext context)?
heroBuilder;
Widget Function(List medias, BuildContext context)?
- multipleMediaBuilder;
+ multipleMediasBuilder;
GalleryAlbum? selectedAlbum;
List _galleryAlbums = [];
List get galleryAlbums => _galleryAlbums;
@@ -36,10 +42,19 @@ class PhoneGalleryController extends GetxController {
bool _pickerMode = false;
bool get pickerMode => _pickerMode;
+ void updateSelectedFiles(List medias) {
+ _selectedFiles = medias;
+ if(selectedFiles.isNotEmpty){
+ _pickerMode=true;
+ }
+ update();
+ }
+
void changeAlbum(GalleryAlbum? album) {
selectedAlbum = album;
- selectedFiles.clear();
+ _selectedFiles.clear();
update();
+ updatePickerListener();
}
void unselectMedia(MediaFile file) {
@@ -48,6 +63,7 @@ class PhoneGalleryController extends GetxController {
_pickerMode = false;
}
update();
+ updatePickerListener();
}
void selectMedia(MediaFile file) {
@@ -58,75 +74,106 @@ class PhoneGalleryController extends GetxController {
_pickerMode = true;
}
update();
+ updatePickerListener();
}
void switchPickerMode(bool value) {
if (!value) {
- selectedFiles.clear();
+ _selectedFiles.clear();
}
_pickerMode = value;
update();
+ updatePickerListener();
+ }
+
+ void updatePickerListener() {
+ if (GetInstance().isRegistered()) {
+ print(_selectedFiles.length);
+ Get.find().updateController(_selectedFiles);
+ }
+ }
+
+ static Future promptPermissionSetting() async {
+ if (Platform.isIOS &&
+ await Permission.storage.request().isGranted &&
+ await Permission.photos.request().isGranted ||
+ Platform.isAndroid && await Permission.storage.request().isGranted) {
+ return true;
+ }
+ return false;
}
Future initializeAlbums() async {
- List tempGalleryAlbums = [];
-
- List photoAlbums =
- await PhotoGallery.listAlbums(mediumType: MediumType.image);
- List videoAlbums =
- await PhotoGallery.listAlbums(mediumType: MediumType.video);
-
- for (var photoAlbum in photoAlbums) {
- GalleryAlbum entireGalleryAlbum = GalleryAlbum(album: photoAlbum);
- await entireGalleryAlbum.initialize();
- entireGalleryAlbum.setType = AlbumType.image;
- if (videoAlbums.any((element) => element.name == photoAlbum.name)) {
- Album videoAlbum = videoAlbums
- .singleWhere((element) => element.name == photoAlbum.name);
- GalleryAlbum videoGalleryAlbum = GalleryAlbum(album: videoAlbum);
- await videoGalleryAlbum.initialize();
- DateTime? lastPhotoDate = entireGalleryAlbum.lastDate;
- DateTime? lastVideoDate = videoGalleryAlbum.lastDate;
-
- if (lastPhotoDate == null) {
- try {
- entireGalleryAlbum.thumbnail = await videoAlbum.getThumbnail(highQuality: true);
- } catch (e) {
- print(e);
- }
- } else if (lastVideoDate == null) {
- } else {
- if (lastVideoDate.isBefore(lastPhotoDate)) {
- try {
- entireGalleryAlbum.thumbnail = await videoAlbum.getThumbnail(highQuality: true);
- } catch (e) {
- entireGalleryAlbum.thumbnail = null;
- print(e);
- }
- }
- }
- for (var file in videoGalleryAlbum.files) {
- entireGalleryAlbum.addFile(file);
- }
- entireGalleryAlbum.sort();
- entireGalleryAlbum.setType = AlbumType.mixed;
- videoAlbums.remove(videoAlbum);
- }
- tempGalleryAlbums.add(entireGalleryAlbum);
+ GalleryMedia? media = await PhoneGalleryController.collectGallery;
+ if (media != null) {
+ this._galleryAlbums = media.albums;
}
- for (var videoAlbum in videoAlbums) {
- print(videoAlbum.name);
- GalleryAlbum galleryVideoAlbum = GalleryAlbum(album: videoAlbum);
- await galleryVideoAlbum.initialize();
- galleryVideoAlbum.setType = AlbumType.video;
- tempGalleryAlbums.add(galleryVideoAlbum);
- }
-
- _galleryAlbums = tempGalleryAlbums;
_isInitialized = true;
update();
}
+ static Future get collectGallery async {
+ if (await promptPermissionSetting()) {
+ List tempGalleryAlbums = [];
+
+ List photoAlbums =
+ await PhotoGallery.listAlbums(mediumType: MediumType.image);
+ List videoAlbums =
+ await PhotoGallery.listAlbums(mediumType: MediumType.video);
+
+ for (var photoAlbum in photoAlbums) {
+ GalleryAlbum entireGalleryAlbum = GalleryAlbum(album: photoAlbum);
+ await entireGalleryAlbum.initialize();
+ entireGalleryAlbum.setType = AlbumType.image;
+ if (videoAlbums.any((element) => element.name == photoAlbum.name)) {
+ Album videoAlbum = videoAlbums
+ .singleWhere((element) => element.name == photoAlbum.name);
+ GalleryAlbum videoGalleryAlbum = GalleryAlbum(album: videoAlbum);
+ await videoGalleryAlbum.initialize();
+ DateTime? lastPhotoDate = entireGalleryAlbum.lastDate;
+ DateTime? lastVideoDate = videoGalleryAlbum.lastDate;
+
+ if (lastPhotoDate == null) {
+ try {
+ entireGalleryAlbum.thumbnail =
+ await videoAlbum.getThumbnail(highQuality: true);
+ } catch (e) {
+ print(e);
+ }
+ } else if (lastVideoDate == null) {
+ } else {
+ if (lastVideoDate.isAfter(lastPhotoDate)) {
+ try {
+ entireGalleryAlbum.thumbnail =
+ await videoAlbum.getThumbnail(highQuality: true);
+ } catch (e) {
+ entireGalleryAlbum.thumbnail = null;
+ print(e);
+ }
+ }
+ }
+ for (var file in videoGalleryAlbum.files) {
+ entireGalleryAlbum.addFile(file);
+ }
+ entireGalleryAlbum.sort();
+ entireGalleryAlbum.setType = AlbumType.mixed;
+ videoAlbums.remove(videoAlbum);
+ }
+ tempGalleryAlbums.add(entireGalleryAlbum);
+ }
+ for (var videoAlbum in videoAlbums) {
+ GalleryAlbum galleryVideoAlbum = GalleryAlbum(album: videoAlbum);
+ await galleryVideoAlbum.initialize();
+ galleryVideoAlbum.setType = AlbumType.video;
+ tempGalleryAlbums.add(galleryVideoAlbum);
+ }
+
+ return GalleryMedia(tempGalleryAlbums);
+ } else {
+ return null;
+ }
+ }
+
GalleryAlbum? get recent {
return _isInitialized
? _galleryAlbums.singleWhere((element) => element.album.name == "All")
@@ -147,6 +194,15 @@ class PhoneGalleryController extends GetxController {
}
bool isSelectedMedia(MediaFile file) {
- return _selectedFiles.any((element) => element == file);
+ return _selectedFiles.any((element) => element.medium.id == file.medium.id);
+ }
+
+ void disposeController() {
+ _galleryAlbums = [];
+ _selectedFiles = [];
+ _isInitialized = false;
+ selectedAlbum = null;
+ Get.delete();
+ update();
}
}
diff --git a/lib/controller/picker_listener.dart b/lib/controller/picker_listener.dart
new file mode 100644
index 0000000..932b01c
--- /dev/null
+++ b/lib/controller/picker_listener.dart
@@ -0,0 +1,24 @@
+import 'dart:async';
+
+import 'package:get/get.dart';
+
+import '../models/media_file.dart';
+
+class PickerListener extends GetxController {
+ StreamController> controller =
+ StreamController>();
+
+ Stream> get stream => controller.stream;
+
+ void updateController(List medias) {
+ print("len:${medias.length}");
+ controller.add(medias);
+ }
+
+ @override
+ void dispose() {
+ controller.close();
+ GetInstance().delete();
+ super.dispose();
+ }
+}
diff --git a/lib/gallery_picker.dart b/lib/gallery_picker.dart
index 92c91d6..97199f6 100644
--- a/lib/gallery_picker.dart
+++ b/lib/gallery_picker.dart
@@ -1,246 +1,118 @@
library gallery_picker;
export 'models/config.dart';
-
-import 'dart:io';
-import 'package:flutter/foundation.dart';
+export 'models/media_file.dart';
+export 'models/mode.dart';
+export 'models/medium.dart';
+export 'models/gallery_media.dart';
+export 'models/gallery_album.dart';
+export 'user_widgets/thumbnail_media.dart';
+export 'user_widgets/album_categories_view.dart';
+export 'user_widgets/album_medias.dart';
+export 'user_widgets/date_category_view.dart';
+export 'user_widgets/thumbnailAlbum.dart';
+export 'user_widgets/files_stream_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/gallery_picker_view/gallery_picker_view.dart';
import 'package:flutter/material.dart';
+import 'package:gallery_picker/models/gallery_media.dart';
import 'package:get/get.dart';
-import 'package:permission_handler/permission_handler.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';
-import 'views/album_category_view/gallery_categories_widget.dart';
-import 'views/album_view/gallery_category_view_page.dart';
-import 'views/album_view/gallery_category_view_widget.dart';
-import 'views/hero_page.dart';
+import 'views/gallery_picker_view/gallery_picker_view.dart';
-void disposeGalleryPicker() {
- GetInstance().delete();
-}
+class GalleryPicker {
+ static Stream> get listenSelectedFiles {
+ var controller = Get.put(PickerListener());
+ return controller.stream;
+ }
-class GalleryPicker extends StatefulWidget {
- Config? config;
- Function(List selectedMedias) onSelect;
- Widget Function(String tag, MediaFile media, BuildContext context)?
- heroBuilder;
- Widget Function(List medias, BuildContext context)?
- multipleMediaBuilder;
- bool startWithRecent;
- GalleryPicker(
- {super.key,
- this.config,
- required this.onSelect,
- this.heroBuilder,
- this.multipleMediaBuilder,
- this.startWithRecent = false});
+ static void disposeSelectedFilesListener() {
+ if (GetInstance().isRegistered()) {
+ Get.find().dispose();
+ }
+ }
- @override
- State createState() => _GalleryPickerState();
-}
-
-class _GalleryPickerState extends State {
- late PhoneGalleryController galleryController;
- late PageController _scrollController;
- bool noPhotoSeleceted = true;
- late Config config;
- @override
- void initState() {
- _scrollController =
- PageController(initialPage: widget.startWithRecent ? 0 : 1);
+ static void dispose() {
if (GetInstance().isRegistered()) {
- galleryController = Get.find();
- config = galleryController.config;
+ Get.find().disposeController();
+ }
+ if (GetInstance().isRegistered()) {
+ Get.find().disposeController();
+ }
+ }
+
+ static Future?> pickMedias(
+ {Config? config,
+ bool startWithRecent = false,
+ List? initSelectedMedias,
+ required BuildContext context}) async {
+ List? medias;
+ await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => GalleryPickerView(
+ onSelect: (mediasTmp) {
+ medias = mediasTmp;
+ },
+ config: config,
+ initSelectedMedias: initSelectedMedias,
+ startWithRecent: startWithRecent,
+ )),
+ );
+ return medias;
+ }
+
+ static Future pickMediasWithBuilder(
+ {Config? config,
+ required Widget Function(List medias, BuildContext context)?
+ multipleMediasBuilder,
+ Widget Function(String tag, MediaFile media, BuildContext context)?
+ heroBuilder,
+ List? initSelectedMedias,
+ bool startWithRecent = false,
+ required BuildContext context}) async {
+ await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => GalleryPickerView(
+ onSelect: (medias) {},
+ multipleMediasBuilder: multipleMediasBuilder,
+ heroBuilder: heroBuilder,
+ config: config,
+ initSelectedMedias: initSelectedMedias,
+ startWithRecent: startWithRecent,
+ )),
+ );
+ }
+
+ static Future openSheet() async {
+ if (GetInstance().isRegistered()) {
+ await Get.find().open();
+ }
+ }
+
+ static Future closeSheet() async {
+ if (GetInstance().isRegistered()) {
+ await Get.find().close();
+ }
+ }
+
+ static bool get isSheetOpened {
+ if (GetInstance().isRegistered()) {
+ return Get.find().sheetController.isExpanded;
} else {
- galleryController = Get.put(PhoneGalleryController(widget.config,
- onSelect: widget.onSelect,
- heroBuilder: widget.heroBuilder,
- multipleMediaBuilder: widget.multipleMediaBuilder,
- isRecent: widget.startWithRecent));
- config = galleryController.config;
- }
- if (!galleryController.isInitialized) {
- initializeGallery();
- }
- super.initState();
- }
-
- @override
- void dispose() {
- super.dispose();
- }
-
- Future initializeGallery() async {
- if (await _promptPermissionSetting()) {
- print("granted");
- await galleryController.initializeAlbums();
+ return false;
}
}
- Future _promptPermissionSetting() async {
- if (Platform.isIOS &&
- await Permission.storage.request().isGranted &&
- await Permission.photos.request().isGranted ||
- Platform.isAndroid && await Permission.storage.request().isGranted) {
- return true;
- }
- return false;
- }
-
- @override
- Widget build(BuildContext context) {
- double width = MediaQuery.of(context).size.width;
- double height = MediaQuery.of(context).size.height - 119;
- return GetBuilder(builder: (galleryController) {
- return galleryController.selectedAlbum == null
- ? Scaffold(
- backgroundColor: config.backgroundColor,
- appBar: AppBar(
- elevation: 0,
- backgroundColor: config.appbarColor,
- leading: TextButton(
- onPressed: () {
- galleryController.switchPickerMode(false);
- },
- child: Icon(
- Icons.arrow_back,
- color: config.appbarIconColor,
- )),
- title: getTitle(),
- actions: [
- !galleryController.pickerMode && galleryController.isRecent
- ? TextButton(
- onPressed: () {
- galleryController.switchPickerMode(true);
- },
- child: Icon(
- Icons.check_box_outlined,
- color: config.appbarIconColor,
- ))
- : const SizedBox()
- ],
- ),
- body: Column(
- children: [
- Container(
- width: width,
- height: 48,
- color: config.appbarColor,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- Container(
- decoration: galleryController.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(() {
- galleryController.isRecent = true;
- galleryController.switchPickerMode(false);
- });
- },
- child: Text(config.recents,
- style: galleryController.isRecent
- ? config.selectedMenuStyle
- : config.unselectedMenuStyle)),
- ),
- Container(
- decoration: !galleryController.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);
- galleryController.isRecent = false;
- galleryController.switchPickerMode(false);
- },
- child: Text(
- config.gallery,
- style: galleryController.isRecent
- ? config.unselectedMenuStyle
- : config.selectedMenuStyle,
- )),
- )
- ],
- ),
- ),
- Expanded(
- child: PageView(
- controller: _scrollController,
- onPageChanged: (value) {
- if (value == 0) {
- galleryController.isRecent = true;
- galleryController.switchPickerMode(false);
- } else {
- galleryController.isRecent = false;
- galleryController.switchPickerMode(false);
- }
- },
- scrollDirection: Axis.horizontal,
- children: [
- SizedBox(
- width: width,
- height: height - 48,
- child: galleryController.isInitialized
- ? GalleryCategoryViewWidget(
- galleryAlbum: galleryController.recent!,
- )
- : const Center(
- child: CircularProgressIndicator(
- color: Colors.grey,
- ))),
- SizedBox(
- width: width,
- height: height - 48,
- child: GalleryCategoriesWidget())
- ]),
- ),
- ],
- ),
- )
- : GalleryAlbumViewPage(
- album: galleryController.selectedAlbum!,
- );
- });
- }
-
- Widget getTitle() {
- if (galleryController.pickerMode &&
- galleryController.selectedFiles.isEmpty) {
- return Text(
- config.tapPhotoSelect,
- style: config.appbarTextStyle,
- );
- } else if (galleryController.pickerMode &&
- galleryController.selectedFiles.isNotEmpty) {
- return Text(
- "${galleryController.selectedFiles.length} ${config.selected}",
- style: config.appbarTextStyle,
- );
- } else {
- return const SizedBox();
- }
+ static Future get collectGallery async {
+ return await PhoneGalleryController.collectGallery;
}
}
diff --git a/lib/models/config.dart b/lib/models/config.dart
index 75b5980..d7f0598 100644
--- a/lib/models/config.dart
+++ b/lib/models/config.dart
@@ -15,7 +15,7 @@ class Config {
unselectedMenuStyle;
String recents, gallery, lastMonth, lastWeek, tapPhotoSelect, selected;
List months;
- Mode? mode;
+ Mode mode;
Config(
{Color? backgroundColor,
@@ -47,7 +47,7 @@ class Config {
"November",
"December"
],
- this.mode = Mode.dark,
+ this.mode = Mode.light,
Widget? selectIcon}) {
if (backgroundColor == null) {
this.backgroundColor = mode == Mode.dark
diff --git a/lib/models/gallery_album.dart b/lib/models/gallery_album.dart
index 3d71727..786e0b8 100644
--- a/lib/models/gallery_album.dart
+++ b/lib/models/gallery_album.dart
@@ -13,6 +13,12 @@ class GalleryAlbum {
int get count =>
dateCategories.expand((element) => element.files).toList().length;
String? get name => album.name;
+
+ List get medias {
+ return dateCategories
+ .expand((element) => element.files)
+ .toList();
+ }
late AlbumType type;
@@ -37,9 +43,9 @@ class GalleryAlbum {
Future initialize() async {
List dateCategory = [];
- for (var file in sortAlbumMediaDates((await album.listMedia()).items)) {
- MediaFile mediaFile = MediaFile(mediaFile: file);
- String name = getDateCategory(file);
+ for (var medium in sortAlbumMediaDates((await album.listMedia()).items)) {
+ MediaFile mediaFile = MediaFile(medium: medium);
+ String name = getDateCategory(medium);
if (dateCategory.any((element) => element.name == name)) {
dateCategory
.singleWhere((element) => element.name == name)
@@ -59,7 +65,7 @@ class GalleryAlbum {
DateTime? get lastDate {
if (dateCategories.isNotEmpty) {
- return dateCategories.first.files.first.mediaFile.lastDate;
+ return dateCategories.first.files.first.medium.lastDate;
} else {
return null;
}
@@ -69,9 +75,6 @@ class GalleryAlbum {
dateCategories.expand((element) => element.files).toList();
String getDateCategory(Medium mediaFile) {
- //print("Creation Date: " + mediaFile.creationDateae.toString());
- //print("Modified Date: " + mediaFile.modifiedDate!.toString());
- //print("-------------------------------------------------------");
if (daysBetween(mediaFile.lastDate!) <= 3) {
return "Recent";
} else if (daysBetween(mediaFile.lastDate!) > 3 &&
@@ -112,19 +115,19 @@ class GalleryAlbum {
for (var category in dateCategories) {
category.files.sort((a, b) {
- if (a.mediaFile.lastDate == null) {
+ if (a.medium.lastDate == null) {
return 1;
- } else if (b.mediaFile.lastDate == null) {
+ } else if (b.medium.lastDate == null) {
return -1;
} else {
- return b.mediaFile.lastDate!.compareTo(a.mediaFile.lastDate!);
+ return b.medium.lastDate!.compareTo(a.medium.lastDate!);
}
});
}
}
void addFile(MediaFile file) {
- String name = getDateCategory(file.mediaFile);
+ String name = getDateCategory(file.medium);
if (dateCategories.any((element) => element.name == name)) {
dateCategories
.singleWhere((element) => element.name == name)
diff --git a/lib/models/gallery_media.dart b/lib/models/gallery_media.dart
new file mode 100644
index 0000000..78b32d8
--- /dev/null
+++ b/lib/models/gallery_media.dart
@@ -0,0 +1,19 @@
+import 'gallery_album.dart';
+
+class GalleryMedia {
+ List albums;
+ GalleryAlbum? get recent {
+ return albums.singleWhere((element) => element.album.name == "All");
+ }
+
+ GalleryAlbum? getAlbum(String name) {
+ try {
+ return albums.singleWhere((element) => element.album.name == name);
+ } catch (e) {
+ print(e);
+ return null;
+ }
+ }
+
+ GalleryMedia(this.albums);
+}
diff --git a/lib/models/media_file.dart b/lib/models/media_file.dart
index fe05a08..b0d1c4a 100644
--- a/lib/models/media_file.dart
+++ b/lib/models/media_file.dart
@@ -1,3 +1,4 @@
+import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
@@ -6,32 +7,68 @@ import 'package:video_thumbnail/video_thumbnail.dart';
import '../controller/gallery_controller.dart';
class MediaFile {
- Medium mediaFile;
+ Medium medium;
MediumType? type;
Uint8List? thumbnail;
- bool thumbnailFailed=false;
-
- MediaFile({required this.mediaFile}) {
- type = mediaFile.mediumType;
+ Uint8List? data;
+ late String id;
+ bool thumbnailFailed = false;
+ File? file;
+ MediaFile({required this.medium}) {
+ type = medium.mediumType;
+ id = medium.id;
}
Future getThumbnail() async {
- print("zooooortlamaca");
try {
thumbnail =
- Uint8List.fromList(await mediaFile.getThumbnail(highQuality: true));
+ Uint8List.fromList(await medium.getThumbnail(highQuality: true));
} catch (e) {
thumbnailFailed = true;
}
}
- void unselect() {
- Get.find().unselectMedia(this);
+ Future getFile() async {
+ file = await medium.getFile();
+ return file!;
}
- void select() {
- Get.find().selectMedia(this);
+ Future getData() async {
+ if (file == null) {
+ await getFile();
+ }
+ data = await file!.readAsBytes();
+ return data!;
}
- bool get isSelected =>
- Get.find().isSelectedMedia(this);
+ void unselect({PhoneGalleryController? controller}) {
+ if (controller != null) {
+ controller.unselectMedia(this);
+ } else {
+ if (GetInstance().isRegistered()) {
+ Get.find().unselectMedia(this);
+ }
+ }
+ }
+
+ void select({PhoneGalleryController? controller}) {
+ if (controller != null) {
+ controller.selectMedia(this);
+ } else {
+ if (GetInstance().isRegistered()) {
+ Get.find().selectMedia(this);
+ }
+ }
+ }
+
+ bool? isSelected({PhoneGalleryController? controller}) {
+ if (controller != null) {
+ return controller.isSelectedMedia(this);
+ } else {
+ if (GetInstance().isRegistered()) {
+ return Get.find().isSelectedMedia(this);
+ } else {
+ return null;
+ }
+ }
+ }
}
diff --git a/lib/trash/album_page.dart b/lib/trash/album_page.dart
deleted file mode 100644
index 45380b5..0000000
--- a/lib/trash/album_page.dart
+++ /dev/null
@@ -1,70 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:photo_gallery/photo_gallery.dart';
-import 'package:transparent_image/transparent_image.dart';
-import '/trash/viewer_page.dart';
-
-class AlbumPage extends StatefulWidget {
- final Album album;
-
- AlbumPage(Album album) : album = album;
-
- @override
- State createState() => AlbumPageState();
-}
-
-class AlbumPageState extends State {
- List? _media;
-
- @override
- void initState() {
- super.initState();
- initAsync();
- }
-
- void initAsync() async {
- MediaPage mediaPage = await widget.album.listMedia();
- setState(() {
- _media = mediaPage.items;
- });
- }
-
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- home: Scaffold(
- appBar: AppBar(
- leading: IconButton(
- icon: const Icon(Icons.arrow_back_ios),
- onPressed: () => Navigator.of(context).pop(),
- ),
- title: Text(widget.album.name ?? "Unnamed Album"),
- ),
- body: GridView.count(
- crossAxisCount: 4,
- mainAxisSpacing: 1.0,
- crossAxisSpacing: 1.0,
- children: [
- ...?_media?.map(
- (medium) => GestureDetector(
- onTap: () => Navigator.of(context).push(MaterialPageRoute(
- builder: (context) => ViewerPage(medium))),
- child: Container(
- color: Colors.grey[300],
- child: FadeInImage(
- fit: BoxFit.cover,
- placeholder: MemoryImage(kTransparentImage),
- image: ThumbnailProvider(
- mediumId: medium.id,
- mediumType: medium.mediumType,
- highQuality: true,
- ),
- ),
- ),
- ),
- ),
- ],
- ),
- ),
- );
- }
-}
diff --git a/lib/trash/video_provider.dart b/lib/trash/video_provider.dart
deleted file mode 100644
index dc70858..0000000
--- a/lib/trash/video_provider.dart
+++ /dev/null
@@ -1,69 +0,0 @@
-import 'dart:io';
-
-import 'package:flutter/material.dart';
-import 'package:photo_gallery/photo_gallery.dart';
-import 'package:video_player/video_player.dart';
-
-class VideoProvider extends StatefulWidget {
- final String mediumId;
-
- const VideoProvider({
- required this.mediumId,
- });
-
- @override
- _VideoProviderState createState() => _VideoProviderState();
-}
-
-class _VideoProviderState extends State {
- VideoPlayerController? _controller;
- File? _file;
-
- @override
- void initState() {
- WidgetsBinding.instance.addPostFrameCallback((_) {
- initAsync();
- });
- super.initState();
- }
-
- Future initAsync() async {
- try {
- _file = await PhotoGallery.getFile(mediumId: widget.mediumId);
- _controller = VideoPlayerController.file(_file!);
- _controller?.initialize().then((_) {
- // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
- setState(() {});
- });
- } catch (e) {
- print("Failed : $e");
- }
- }
-
- @override
- Widget build(BuildContext context) {
- return _controller == null || !_controller!.value.isInitialized
- ? Container()
- : Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- AspectRatio(
- aspectRatio: _controller!.value.aspectRatio,
- child: VideoPlayer(_controller!),
- ),
- TextButton(
- onPressed: () {
- setState(() {
- _controller!.value.isPlaying
- ? _controller!.pause()
- : _controller!.play();
- });
- },
- child: Icon(
- _controller!.value.isPlaying ? Icons.pause : Icons.play_arrow,
- ),
- ),
- ],
- );
- }
-}
diff --git a/lib/trash/viewer_page.dart b/lib/trash/viewer_page.dart
deleted file mode 100644
index 6e34db9..0000000
--- a/lib/trash/viewer_page.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:photo_gallery/photo_gallery.dart';
-import '/trash/video_provider.dart';
-import 'package:transparent_image/transparent_image.dart';
-
-class ViewerPage extends StatelessWidget {
- final Medium medium;
-
- ViewerPage(Medium medium) : medium = medium;
-
- @override
- Widget build(BuildContext context) {
-
- DateTime? date = medium.creationDate ?? medium.modifiedDate;
-
- return MaterialApp(
- home: Scaffold(
- appBar: AppBar(
- leading: IconButton(
- onPressed: () => Navigator.of(context).pop(),
- icon: const Icon(Icons.arrow_back_ios),
- ),
- title: date != null ? Text(date.toLocal().toString()) : null,
- ),
- body: Container(
- alignment: Alignment.center,
- child: medium.mediumType == MediumType.image
- ? FadeInImage(
- fit: BoxFit.cover,
- placeholder: MemoryImage(kTransparentImage),
- image: PhotoProvider(mediumId: medium.id),
- )
- : VideoProvider(
- mediumId: medium.id,
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/user_widgets/album_categories_view.dart b/lib/user_widgets/album_categories_view.dart
new file mode 100644
index 0000000..4ecc901
--- /dev/null
+++ b/lib/user_widgets/album_categories_view.dart
@@ -0,0 +1,74 @@
+import 'package:flutter/material.dart';
+import 'package:gallery_picker/models/gallery_album.dart';
+import '../../user_widgets/thumbnailAlbum.dart';
+import 'package:get/get.dart';
+import 'package:photo_gallery/photo_gallery.dart';
+import '../../models/config.dart';
+import 'package:transparent_image/transparent_image.dart';
+import '../../../controller/gallery_controller.dart';
+import '../models/mode.dart';
+
+class AlbumCategoriesView extends StatelessWidget {
+ List albums;
+ Function(GalleryAlbum album)? onPressed;
+ Function(GalleryAlbum album, bool)? onHover;
+ Function(GalleryAlbum album)? onLongPress;
+ Function(GalleryAlbum album, bool)? onFocusChange;
+ final Color categoryFailIconColor, categoryBackgroundColor;
+ final Mode mode;
+ AlbumCategoriesView(
+ {super.key,
+ required this.albums,
+ required this.categoryBackgroundColor,
+ required this.categoryFailIconColor,
+ required this.mode,
+ this.onFocusChange,
+ this.onHover,
+ this.onLongPress,
+ this.onPressed});
+ @override
+ Widget build(BuildContext context) {
+ return LayoutBuilder(
+ builder: (context, constraints) {
+ return GridView.count(
+ crossAxisCount: 2,
+ mainAxisSpacing: 3.0,
+ crossAxisSpacing: 3.0,
+ children: [
+ ...albums.map(
+ (album) => TextButton(
+ onPressed: () {
+ if (onPressed != null) {
+ onPressed!(album);
+ }
+ },
+ onFocusChange: (value) {
+ if (onFocusChange != null) {
+ onFocusChange!(album, value);
+ }
+ },
+ onHover: (value) {
+ if (onHover != null) {
+ onHover!(album, value);
+ }
+ },
+ onLongPress: () {
+ if (onLongPress != null) {
+ onLongPress!(album);
+ }
+ },
+ child: Stack(fit: StackFit.passthrough, children: [
+ ThumbnailAlbum(
+ album: album,
+ failIconColor: categoryFailIconColor,
+ mode: mode,
+ backgroundColor: categoryBackgroundColor),
+ ]),
+ ),
+ ),
+ ],
+ );
+ },
+ );
+ }
+}
diff --git a/lib/user_widgets/album_medias.dart b/lib/user_widgets/album_medias.dart
new file mode 100644
index 0000000..4a23f07
--- /dev/null
+++ b/lib/user_widgets/album_medias.dart
@@ -0,0 +1,28 @@
+import 'package:flutter/material.dart';
+import '/models/gallery_album.dart';
+import 'date_category_view.dart';
+
+// ignore: must_be_immutable
+class AlbumMediasView extends StatelessWidget {
+ TextStyle? textStyle;
+ Widget Function(BuildContext)? onFileErrorBuilder;
+ AlbumMediasView(
+ {super.key, required this.galleryAlbum,this.textStyle});
+ GalleryAlbum galleryAlbum;
+ @override
+ Widget build(BuildContext context) {
+ return Stack(
+ children: [
+ ListView(
+ children: [
+ for (var category in galleryAlbum.dateCategories)
+ DateCategoryWiew(
+ category: category,
+ textStyle: textStyle,
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/user_widgets/date_category_view.dart b/lib/user_widgets/date_category_view.dart
new file mode 100644
index 0000000..e37719f
--- /dev/null
+++ b/lib/user_widgets/date_category_view.dart
@@ -0,0 +1,64 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import '../../../controller/gallery_controller.dart';
+import '../models/media_file.dart';
+import '../views/gridview_static.dart';
+import '/models/gallery_album.dart';
+import 'thumbnail_media.dart';
+
+class DateCategoryWiew extends StatelessWidget {
+ TextStyle? textStyle;
+ final Widget Function(MediaFile media, BuildContext context)?
+ onMediaErrorBuilder;
+ DateCategoryWiew(
+ {super.key,
+ required this.category,
+ this.textStyle,
+ this.onMediaErrorBuilder});
+ DateCategory category;
+
+ int getRowCount() {
+ if (category.files.length % 4 != 0) {
+ return category.files.length ~/ 4 + 1;
+ } else {
+ return category.files.length ~/ 4;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return LayoutBuilder(
+ builder: (context, constraints) {
+ return Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ category.name,
+ style: textStyle,
+ ),
+ ),
+ ),
+ GridViewStatic(
+ size: MediaQuery.of(context).size.width,
+ padding: EdgeInsets.zero,
+ crossAxisCount: 4,
+ mainAxisSpacing: 1.0,
+ crossAxisSpacing: 1.0,
+ children: [
+ ...category.files.map(
+ (medium) => ThumbnailMedia(
+ media: medium,
+ onErrorBuilder: onMediaErrorBuilder,
+ ),
+ ),
+ ],
+ ),
+ ],
+ );
+ },
+ );
+ }
+}
diff --git a/lib/user_widgets/files_stream_builder.dart b/lib/user_widgets/files_stream_builder.dart
new file mode 100644
index 0000000..0c3381d
--- /dev/null
+++ b/lib/user_widgets/files_stream_builder.dart
@@ -0,0 +1,24 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:get/get.dart';
+
+import '../controller/picker_listener.dart';
+import '../models/media_file.dart';
+
+class FilesStreamBuilder extends StatelessWidget {
+ final Widget Function(List? medias, BuildContext context) builder;
+ FilesStreamBuilder({super.key, required this.builder}) {
+ Get.put(PickerListener());
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return StreamBuilder(
+ stream: Get.find().stream,
+ builder: ((context, snapshot) {
+ print("snapshot:${snapshot.data}");
+ return builder(snapshot.data, context);
+ }));
+ }
+}
diff --git a/lib/user_widgets/media_provider.dart b/lib/user_widgets/media_provider.dart
new file mode 100644
index 0000000..ea7cccd
--- /dev/null
+++ b/lib/user_widgets/media_provider.dart
@@ -0,0 +1,26 @@
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:gallery_picker/gallery_picker.dart';
+import 'package:photo_gallery/photo_gallery.dart' as photo_gallery;
+
+class MediaProvider extends StatelessWidget {
+ final MediaFile media;
+ final double? width, height;
+ const MediaProvider(
+ {super.key, required this.media, this.width, this.height});
+
+ @override
+ Widget build(BuildContext context) {
+ return media.type == photo_gallery.MediumType.image
+ ? PhotoProvider(
+ media: media,
+ width: width,
+ height: height,
+ )
+ : VideoProvider(
+ media: media,
+ width: width,
+ height: height,
+ );
+ }
+}
diff --git a/lib/user_widgets/photo_provider.dart b/lib/user_widgets/photo_provider.dart
new file mode 100644
index 0000000..a61480c
--- /dev/null
+++ b/lib/user_widgets/photo_provider.dart
@@ -0,0 +1,67 @@
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:photo_gallery/photo_gallery.dart';
+import 'package:video_player/video_player.dart';
+
+import '../models/media_file.dart';
+
+class PhotoProvider extends StatefulWidget {
+ final MediaFile media;
+ final BoxFit fit;
+ final double? width, height;
+
+ const PhotoProvider({
+ super.key,
+ required this.media,
+ this.fit = BoxFit.contain,
+ this.width,
+ this.height,
+ });
+
+ @override
+ _PhotoProviderState createState() => _PhotoProviderState();
+}
+
+class _PhotoProviderState extends State {
+ VideoPlayerController? _controller;
+ late MediaFile media;
+ @override
+ void initState() {
+ media = widget.media;
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ initMedia();
+ });
+ super.initState();
+ }
+
+ Future initMedia() async {
+ await media.getData();
+ if (mounted) {
+ setState(() {});
+ }
+ }
+
+ bool anyProcess = false;
+ @override
+ Widget build(BuildContext context) {
+ if (media != widget.media) {
+ media = widget.media;
+ if (media.data == null) {
+ initMedia();
+ }
+ }
+ return media.data == null
+ ? Container(
+ width: widget.width,
+ height: widget.height,
+ )
+ : Image.memory(
+ media.data!,
+ width: widget.width,
+ height: widget.height,
+ fit: widget.fit,
+ );
+ }
+}
diff --git a/lib/views/thumbnailAlbum.dart b/lib/user_widgets/thumbnailAlbum.dart
similarity index 72%
rename from lib/views/thumbnailAlbum.dart
rename to lib/user_widgets/thumbnailAlbum.dart
index a38362a..e81498b 100644
--- a/lib/views/thumbnailAlbum.dart
+++ b/lib/user_widgets/thumbnailAlbum.dart
@@ -1,27 +1,22 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
-import 'package:gallery_picker/models/gallery_album.dart';
-import 'package:get/get.dart';
-import '../controller/gallery_controller.dart';
+import '/models/gallery_album.dart';
import '../models/config.dart';
import '../models/mode.dart';
-import '/models/media_file.dart';
-import 'package:photo_gallery/photo_gallery.dart';
-import 'package:transparent_image/transparent_image.dart';
class ThumbnailAlbum extends StatelessWidget {
final GalleryAlbum album;
- final Color failIconColor;
- final Config config = Get.find().config;
- ThumbnailAlbum({super.key, required this.album, required this.failIconColor});
+ final Color failIconColor, backgroundColor;
+ final Mode mode;
+ ThumbnailAlbum({super.key, required this.album, required this.failIconColor,required this.mode,required this.backgroundColor});
Color adjustFailedBgColor() {
- if (config.mode == Mode.dark) {
+ if (mode == Mode.dark) {
return lighten(
- config.backgroundColor,
+ backgroundColor,
);
} else {
- return darken(config.backgroundColor);
+ return darken(backgroundColor);
}
}
@@ -58,26 +53,12 @@ class ThumbnailAlbum extends StatelessWidget {
color: failIconColor,
))
else if (album.thumbnail != null)
- FadeInImage(
- fadeInDuration: const Duration(milliseconds: 200),
+ Image.memory(
+ Uint8List.fromList(album.thumbnail!),
fit: BoxFit.cover,
- placeholder: MemoryImage(kTransparentImage),
- image: MemoryImage(Uint8List.fromList(album.thumbnail!)),
)
else
const SizedBox(),
- Positioned(
- bottom: 10,
- left: 10,
- child: Icon(
- album.type == AlbumType.video
- ? Icons.video_camera_back
- : album.type == AlbumType.image
- ? Icons.image
- : Icons.folder,
- color: Colors.white,
- size: 20,
- )),
Opacity(
opacity: 0.5,
child: Container(
diff --git a/lib/user_widgets/thumbnail_media.dart b/lib/user_widgets/thumbnail_media.dart
new file mode 100644
index 0000000..22434dc
--- /dev/null
+++ b/lib/user_widgets/thumbnail_media.dart
@@ -0,0 +1,57 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import '../../controller/gallery_controller.dart';
+import '../../models/config.dart';
+import '../../models/mode.dart';
+import '/models/media_file.dart';
+import 'package:photo_gallery/photo_gallery.dart';
+import 'package:transparent_image/transparent_image.dart';
+
+class ThumbnailMedia extends StatelessWidget {
+ final MediaFile media;
+ final bool noIcon;
+ final Widget Function(MediaFile media,BuildContext context)? onErrorBuilder;
+ const ThumbnailMedia({super.key, required this.media, this.onErrorBuilder,this.noIcon=false});
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: media.thumbnail == null ? media.getThumbnail() : null,
+ builder: (context, snapshot) {
+ return Stack(
+ fit: StackFit.passthrough,
+ children: [
+ if (media.thumbnailFailed && onErrorBuilder == null)
+ Icon(
+ media.type == MediumType.image
+ ? 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.medium.mediumType == MediumType.video
+ ? Icons.video_camera_back
+ : null,
+ color: Colors.white,
+ size: 20,
+ )),
+ ],
+ );
+ });
+ }
+}
diff --git a/lib/user_widgets/video_provider.dart b/lib/user_widgets/video_provider.dart
new file mode 100644
index 0000000..cfc5309
--- /dev/null
+++ b/lib/user_widgets/video_provider.dart
@@ -0,0 +1,110 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:photo_gallery/photo_gallery.dart';
+import 'package:video_player/video_player.dart';
+
+import '../models/media_file.dart';
+
+class VideoProvider extends StatefulWidget {
+ final MediaFile media;
+ final double? width, height;
+
+ const VideoProvider({
+ required this.media,
+ this.width,
+ this.height,
+ });
+
+ @override
+ _VideoProviderState createState() => _VideoProviderState();
+}
+
+class _VideoProviderState extends State {
+ VideoPlayerController? _controller;
+ File? _file;
+ late MediaFile media;
+ @override
+ void initState() {
+ media = widget.media;
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ initMedia();
+ });
+ super.initState();
+ }
+
+ Future initMedia() async {
+ try {
+ if (media.file == null) {
+ _file = await media.getFile();
+ } else {
+ _file = media.file;
+ }
+ _controller = VideoPlayerController.file(_file!);
+ _controller?.initialize().then((_) {
+ setState(() {});
+ });
+ } catch (e) {
+ print("Failed : $e");
+ }
+ }
+
+ @override
+ void dispose() {
+ disposeController();
+ super.dispose();
+ }
+
+ void disposeController() {
+ if (_controller != null) {
+ _controller!.dispose();
+ _controller = null;
+ }
+ }
+
+ bool anyProcess = false;
+ @override
+ Widget build(BuildContext context) {
+ if (media != widget.media) {
+ media = widget.media;
+ disposeController();
+ initMedia();
+ }
+ return _controller == null || !_controller!.value.isInitialized
+ ? Container(
+ width: widget.width,
+ height: widget.height,
+ )
+ : SizedBox(
+ width: widget.width,
+ height: widget.height,
+ child: Stack(
+ children: [
+ AspectRatio(
+ aspectRatio: _controller!.value.aspectRatio,
+ child: Stack(children: [
+ VideoPlayer(_controller!),
+ Center(
+ child: TextButton(
+ onPressed: () {
+ anyProcess = true;
+ setState(() {
+ _controller!.value.isPlaying
+ ? _controller!.pause()
+ : _controller!.play();
+ });
+ },
+ child: Icon(
+ _controller!.value.isPlaying
+ ? Icons.pause
+ : Icons.play_arrow,
+ ),
+ ),
+ ),
+ ]),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/views/ThumbnailMedia.dart b/lib/views/ThumbnailMedia.dart
index c6a3cb5..33b5169 100644
--- a/lib/views/ThumbnailMedia.dart
+++ b/lib/views/ThumbnailMedia.dart
@@ -10,8 +10,8 @@ import 'package:transparent_image/transparent_image.dart';
class ThumbnailMedia extends StatelessWidget {
final MediaFile file;
final Color failIconColor;
- final Config config = Get.find().config;
- ThumbnailMedia({super.key, required this.file, required this.failIconColor});
+ final Config config;
+ ThumbnailMedia({super.key, required this.file, required this.failIconColor,required this.config});
Color adjustFailedBgColor() {
if (config.mode == Mode.dark) {
@@ -58,7 +58,7 @@ class ThumbnailMedia extends StatelessWidget {
))
else if (file.thumbnail != null)
Hero(
- tag: file.mediaFile.id,
+ tag: file.medium.id,
child: FadeInImage(
fadeInDuration: const Duration(milliseconds: 200),
fit: BoxFit.cover,
@@ -73,7 +73,7 @@ class ThumbnailMedia extends StatelessWidget {
bottom: 10,
left: 10,
child: Icon(
- file.mediaFile.mediumType == MediumType.video
+ file.medium.mediumType == MediumType.video
? Icons.video_camera_back
: null,
color: Colors.white,
diff --git a/lib/views/album_categories_view/album_categories_view.dart b/lib/views/album_categories_view/album_categories_view.dart
new file mode 100644
index 0000000..cb9f7eb
--- /dev/null
+++ b/lib/views/album_categories_view/album_categories_view.dart
@@ -0,0 +1,43 @@
+import 'package:flutter/material.dart';
+import '../../user_widgets/thumbnailAlbum.dart';
+import 'package:get/get.dart';
+import 'package:photo_gallery/photo_gallery.dart';
+import '../../models/config.dart';
+import '../album_view/album_page.dart';
+import 'package:transparent_image/transparent_image.dart';
+import '../../../controller/gallery_controller.dart';
+
+class AlbumCategoriesView extends StatelessWidget {
+ PhoneGalleryController controller;
+ late Config config;
+ AlbumCategoriesView(this.controller, {super.key}) {
+ config = controller.config;
+ }
+ @override
+ Widget build(BuildContext context) {
+ return LayoutBuilder(
+ builder: (context, constraints) {
+ return GridView.count(
+ crossAxisCount: 2,
+ mainAxisSpacing: 3.0,
+ crossAxisSpacing: 3.0,
+ children: [
+ ...controller.galleryAlbums.map(
+ (album) => GestureDetector(
+ onTap: () => controller.changeAlbum(album),
+ child: Stack(fit: StackFit.passthrough, children: [
+ ThumbnailAlbum(
+ album: album,
+ failIconColor: config.appbarIconColor,
+ backgroundColor: config.backgroundColor,
+ mode: config.mode,
+ ),
+ ]),
+ ),
+ ),
+ ],
+ );
+ },
+ );
+ }
+}
diff --git a/lib/views/album_category_view/gallery_categories_widget.dart b/lib/views/album_category_view/gallery_categories_widget.dart
deleted file mode 100644
index 4b477a9..0000000
--- a/lib/views/album_category_view/gallery_categories_widget.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:gallery_picker/views/thumbnailAlbum.dart';
-import 'package:get/get.dart';
-import 'package:photo_gallery/photo_gallery.dart';
-import '../../models/config.dart';
-import '../album_view/gallery_category_view_page.dart';
-import 'package:transparent_image/transparent_image.dart';
-import '../../../controller/gallery_controller.dart';
-
-class GalleryCategoriesWidget extends StatelessWidget {
- var galleryController = Get.find();
- late Config config;
- GalleryCategoriesWidget({super.key}) {
- config = galleryController.config;
- }
- @override
- Widget build(BuildContext context) {
- return Container(
- child: LayoutBuilder(
- builder: (context, constraints) {
- return GridView.count(
- crossAxisCount: 2,
- mainAxisSpacing: 3.0,
- crossAxisSpacing: 3.0,
- children: [
- ...galleryController.galleryAlbums.map(
- (album) => GestureDetector(
- onTap: () => galleryController.changeAlbum(album),
- child: Stack(fit: StackFit.passthrough, children: [
- ThumbnailAlbum(album: album, failIconColor: galleryController.config.appbarIconColor),
- ]),
- ),
- ),
- ],
- );
- },
- ),
- );
- }
-}
diff --git a/lib/views/album_view/album_appbar.dart b/lib/views/album_view/album_appbar.dart
new file mode 100644
index 0000000..33a7764
--- /dev/null
+++ b/lib/views/album_view/album_appbar.dart
@@ -0,0 +1,77 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:gallery_picker/models/gallery_album.dart';
+import 'package:get/get.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 {
+ PhoneGalleryController controller;
+ BottomSheetController? bottomSheetController;
+ GalleryAlbum album;
+ AlbumAppBar(
+ {super.key,
+ required this.bottomSheetController,
+ required this.album,
+ required this.controller});
+
+ @override
+ Widget build(BuildContext context) {
+ print("zort");
+ 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()
+ ],
+ ),
+ );
+ }
+
+ Widget getTitle() {
+ if (!controller.pickerMode && controller.selectedFiles.isEmpty) {
+ return Text(
+ album.album.name!,
+ style: controller.config.appbarTextStyle,
+ );
+ } else if (controller.pickerMode && controller.selectedFiles.isEmpty) {
+ return Text(
+ controller.config.tapPhotoSelect,
+ style: controller.config.appbarTextStyle,
+ );
+ } else if (controller.pickerMode && controller.selectedFiles.isNotEmpty) {
+ return Text(
+ "${controller.selectedFiles.length} ${controller.config.selected}",
+ style: controller.config.appbarTextStyle,
+ );
+ } else {
+ return const SizedBox();
+ }
+ }
+
+ @override
+ Size get preferredSize => Size.fromHeight(48);
+}
diff --git a/lib/views/album_view/album_medias_view.dart b/lib/views/album_view/album_medias_view.dart
new file mode 100644
index 0000000..b23aba7
--- /dev/null
+++ b/lib/views/album_view/album_medias_view.dart
@@ -0,0 +1,40 @@
+import 'package:flutter/material.dart';
+import '/models/gallery_album.dart';
+import '../../../controller/gallery_controller.dart';
+import 'date_category_view.dart';
+import 'selected_medias_view.dart';
+
+// ignore: must_be_immutable
+class AlbumMediasView extends StatelessWidget {
+ PhoneGalleryController controller;
+ bool singleMedia;
+ AlbumMediasView(
+ {super.key,
+ required this.galleryAlbum,
+ required this.controller,
+ required this.singleMedia});
+ GalleryAlbum galleryAlbum;
+ @override
+ Widget build(BuildContext context) {
+ return Stack(
+ children: [
+ ListView(
+ children: [
+ for (var category in galleryAlbum.dateCategories)
+ DateCategoryWiew(
+ category: category,
+ controller: controller,
+ singleMedia: singleMedia,
+ ),
+ ],
+ ),
+ if (controller.selectedFiles.isNotEmpty)
+ Align(
+ alignment: Alignment.bottomCenter,
+ child: SelectedMediasView(
+ controller: controller,
+ ))
+ ],
+ );
+ }
+}
diff --git a/lib/views/album_view/album_page.dart b/lib/views/album_view/album_page.dart
new file mode 100644
index 0000000..7b146ee
--- /dev/null
+++ b/lib/views/album_view/album_page.dart
@@ -0,0 +1,39 @@
+import 'package:flutter/material.dart';
+import 'package:gallery_picker/views/album_view/album_appbar.dart';
+import 'package:gallery_picker/views/gallery_picker_view/tappable_appbar.dart';
+import 'package:get/get.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';
+import 'selected_medias_view.dart';
+
+class AlbumPage extends StatelessWidget {
+ bool singleMedia;
+ AlbumPage(
+ {super.key,
+ required this.album,
+ required this.controller,
+ required this.singleMedia,
+ required this.bottomSheetController});
+ PhoneGalleryController controller;
+ BottomSheetController? bottomSheetController;
+ GalleryAlbum album;
+ @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,
+ singleMedia: singleMedia,
+ ),
+ );
+ }
+}
diff --git a/lib/views/album_view/date_category_widget.dart b/lib/views/album_view/date_category_view.dart
similarity index 72%
rename from lib/views/album_view/date_category_widget.dart
rename to lib/views/album_view/date_category_view.dart
index a6d6d6d..b9c9822 100644
--- a/lib/views/album_view/date_category_widget.dart
+++ b/lib/views/album_view/date_category_view.dart
@@ -5,10 +5,14 @@ import '../gridview_static.dart';
import '/models/gallery_album.dart';
import 'media_view.dart';
-class DateCategoryWidget extends StatelessWidget {
- DateCategoryWidget({super.key, required this.category}){
- print(category.name);
- }
+class DateCategoryWiew extends StatelessWidget {
+ PhoneGalleryController controller;
+ bool singleMedia;
+ DateCategoryWiew(
+ {super.key,
+ required this.category,
+ required this.controller,
+ required this.singleMedia});
DateCategory category;
int getRowCount() {
@@ -31,7 +35,7 @@ class DateCategoryWidget extends StatelessWidget {
alignment: Alignment.centerLeft,
child: Text(
category.name,
- style: Get.find().config.textStyle,
+ style: controller.config.textStyle,
),
),
),
@@ -43,7 +47,11 @@ class DateCategoryWidget extends StatelessWidget {
crossAxisSpacing: 1.0,
children: [
...category.files.map(
- (medium) => MediaView(medium),
+ (medium) => MediaView(
+ medium,
+ controller: controller,
+ singleMedia: singleMedia,
+ ),
),
],
),
diff --git a/lib/views/album_view/gallery_category_view_page.dart b/lib/views/album_view/gallery_category_view_page.dart
deleted file mode 100644
index 9871d87..0000000
--- a/lib/views/album_view/gallery_category_view_page.dart
+++ /dev/null
@@ -1,51 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:get/get.dart';
-import '../../../controller/gallery_controller.dart';
-import '../../../models/gallery_album.dart';
-import '../../models/config.dart';
-import 'gallery_category_view_widget.dart';
-
-class GalleryAlbumViewPage extends StatelessWidget {
- GalleryAlbumViewPage({super.key, required this.album});
- GalleryAlbum album;
- @override
- Widget build(BuildContext context) {
- return GetBuilder(builder: (controller) {
- Config config = controller.config;
- return Scaffold(
- backgroundColor: config.backgroundColor,
-
- appBar: AppBar(
- elevation: 0,
- foregroundColor: config.appbarIconColor,
- backgroundColor: config.appbarColor,
- leading: TextButton(
- onPressed: () {
- controller.changeAlbum(null);
- },
- child: Icon(
- Icons.arrow_back,
- color: config.appbarIconColor,
- )),
- title: Text(
- album.album.name!,
- style: config.appbarTextStyle,
- ),
- actions: [
- !Get.find().pickerMode
- ? TextButton(
- onPressed: () {
- Get.find().switchPickerMode(true);
- },
- child: Icon(
- Icons.check_box_outlined,
- color: config.appbarIconColor,
- ))
- : const SizedBox()
- ],
- ),
- body: GalleryCategoryViewWidget(galleryAlbum: album),
- );
- });
- }
-}
diff --git a/lib/views/album_view/gallery_category_view_widget.dart b/lib/views/album_view/gallery_category_view_widget.dart
deleted file mode 100644
index 3c3199a..0000000
--- a/lib/views/album_view/gallery_category_view_widget.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:get/get.dart';
-import '/models/gallery_album.dart';
-import '../../../controller/gallery_controller.dart';
-import 'date_category_widget.dart';
-import 'selected_item_viewer_widget.dart';
-
-// ignore: must_be_immutable
-class GalleryCategoryViewWidget extends StatelessWidget {
- GalleryCategoryViewWidget({super.key, required this.galleryAlbum});
- GalleryAlbum galleryAlbum;
- @override
- Widget build(BuildContext context) {
- return Stack(children: [
- ListView(
- children: [
- for (var category in galleryAlbum.dateCategories)
- DateCategoryWidget(
- category: category,
- ),
- ],
- ),
- if (Get.find().selectedFiles.isNotEmpty)
- Align(alignment: Alignment.bottomCenter, child: SelectedMediaWidget())
- ]);
- }
-}
diff --git a/lib/views/album_view/media_view.dart b/lib/views/album_view/media_view.dart
index e5c271f..077cad4 100644
--- a/lib/views/album_view/media_view.dart
+++ b/lib/views/album_view/media_view.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:transparent_image/transparent_image.dart';
-import '/views/ThumbnailMedia.dart';
+import '../../controller/bottom_sheet_controller.dart';
+import '../thumbnailMedia.dart';
import 'package:get/get.dart';
import 'package:photo_gallery/photo_gallery.dart';
import '../../../controller/gallery_controller.dart';
@@ -8,8 +9,10 @@ import '../../../models/media_file.dart';
class MediaView extends StatelessWidget {
final MediaFile file;
- var controller = Get.find();
- MediaView(this.file, {super.key});
+ PhoneGalleryController controller;
+ bool singleMedia;
+ MediaView(this.file,
+ {super.key, required this.controller, required this.singleMedia});
@override
Widget build(BuildContext context) {
return Stack(
@@ -17,31 +20,63 @@ class MediaView extends StatelessWidget {
children: [
GestureDetector(
onLongPress: () {
- file.select();
+ if (singleMedia) {
+ if (controller.heroBuilder != null) {
+ Navigator.of(context).push(
+ MaterialPageRoute(builder: (BuildContext context) {
+ return controller.heroBuilder!(file.medium.id, file, context);
+ }));
+ } else {
+ controller.selectedFiles.add(file);
+ controller.onSelect(controller.selectedFiles);
+ controller.updatePickerListener();
+ if (GetInstance().isRegistered()) {
+ Get.find().close();
+ } else {
+ Navigator.pop(context);
+ controller.disposeController();
+ }
+ }
+ } else {
+ file.select(controller: controller);
+ }
},
onTap: () {
if (controller.pickerMode) {
- file.isSelected ? file.unselect() : file.select();
+ file.isSelected(controller: controller)!
+ ? file.unselect(controller: controller)
+ : file.select(controller: controller);
} else {
if (controller.heroBuilder != null) {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) {
- return controller.heroBuilder!(file.mediaFile.id, file,context);
+ return controller.heroBuilder!(file.medium.id, file, context);
}));
} else {
- controller.onSelect([file]);
- Navigator.pop(context);
- GetInstance().delete();
+ controller.selectedFiles.add(file);
+ controller.onSelect(controller.selectedFiles);
+ controller.updatePickerListener();
+ if (GetInstance().isRegistered()) {
+ Get.find().close();
+ } else {
+ Navigator.pop(context);
+ controller.disposeController();
+ }
}
}
},
child: ThumbnailMedia(
- file: file, failIconColor: controller.config.appbarIconColor),
+ file: file,
+ failIconColor: controller.config.appbarIconColor,
+ config: controller.config,
+ ),
),
- if (file.isSelected)
+ if (file.isSelected(controller: controller)!)
GestureDetector(
onTap: () {
- file.isSelected ? file.unselect() : file.select();
+ file.isSelected(controller: controller)!
+ ? file.unselect(controller: controller)
+ : file.select(controller: controller);
},
child: Opacity(
opacity: 0.5,
diff --git a/lib/views/album_view/selected_item_viewer_widget.dart b/lib/views/album_view/selected_medias_view.dart
similarity index 64%
rename from lib/views/album_view/selected_item_viewer_widget.dart
rename to lib/views/album_view/selected_medias_view.dart
index 48ee5c5..b165702 100644
--- a/lib/views/album_view/selected_item_viewer_widget.dart
+++ b/lib/views/album_view/selected_medias_view.dart
@@ -1,14 +1,15 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:photo_gallery/photo_gallery.dart';
+import '../../controller/bottom_sheet_controller.dart';
import '../../controller/gallery_controller.dart';
import '../../models/config.dart';
-class SelectedMediaWidget extends StatelessWidget {
- var galleryController = Get.find();
+class SelectedMediasView extends StatelessWidget {
+ PhoneGalleryController controller;
late Config config;
- SelectedMediaWidget({super.key}) {
- config = galleryController.config;
+ SelectedMediasView({super.key, required this.controller}) {
+ config = controller.config;
}
@override
@@ -29,7 +30,7 @@ class SelectedMediaWidget extends StatelessWidget {
child: ListView(
scrollDirection: Axis.horizontal,
children: [
- for (var selectedMedia in galleryController.selectedFiles)
+ for (var selectedMedia in controller.selectedFiles)
Padding(
padding: const EdgeInsets.only(
top: 3.0, bottom: 3.0, right: 2),
@@ -39,9 +40,9 @@ class SelectedMediaWidget extends StatelessWidget {
image: DecorationImage(
fit: BoxFit.fill,
image: ThumbnailProvider(
- mediumId: selectedMedia.mediaFile.id,
+ mediumId: selectedMedia.medium.id,
mediumType:
- selectedMedia.mediaFile.mediumType,
+ selectedMedia.medium.mediumType,
highQuality: true,
))),
child: SizedBox(
@@ -55,25 +56,28 @@ class SelectedMediaWidget extends StatelessWidget {
),
TextButton(
onPressed: () {
- if (galleryController.selectedFiles.length == 1 &&
- galleryController.heroBuilder != null) {
+ if (controller.selectedFiles.length == 1 &&
+ controller.heroBuilder != null) {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) {
- return galleryController.heroBuilder!(
- galleryController.selectedFiles[0].mediaFile.id,
- galleryController.selectedFiles[0],context);
+ return controller.heroBuilder!(
+ controller.selectedFiles[0].medium.id,
+ controller.selectedFiles[0],
+ context);
}));
- } else if (galleryController.multipleMediaBuilder != null) {
+ } else if (controller.multipleMediasBuilder != null) {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) {
- return galleryController
- .multipleMediaBuilder!(galleryController.selectedFiles,context);
+ return controller.multipleMediasBuilder!(
+ controller.selectedFiles, context);
}));
} else {
- galleryController.onSelect(galleryController.selectedFiles);
- Navigator.pop(context);
- if (!galleryController.isRecent) {
+ controller.onSelect(controller.selectedFiles);
+ if (GetInstance().isRegistered()) {
+ Get.find().close();
+ } else {
Navigator.pop(context);
+ controller.disposeController();
}
}
},
diff --git a/lib/views/bottom_sheet.dart b/lib/views/bottom_sheet.dart
new file mode 100644
index 0000000..d962a26
--- /dev/null
+++ b/lib/views/bottom_sheet.dart
@@ -0,0 +1,154 @@
+import 'package:bottom_sheet_bar/bottom_sheet_bar.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:gallery_picker/controller/gallery_controller.dart';
+import '/gallery_picker.dart';
+import '/views/gallery_picker_view/gallery_picker_view.dart';
+import 'package:get/get.dart';
+import '../controller/bottom_sheet_controller.dart';
+
+class BottomSheetLayout extends StatefulWidget {
+ final Widget child;
+ final Config? config;
+ final List? initSelectedMedias;
+ final Function(List selectedMedias) onSelect;
+ final Widget Function(String tag, MediaFile media, BuildContext context)?
+ heroBuilder;
+ final Widget Function(List medias, BuildContext context)?
+ multipleMediasBuilder;
+ final bool startWithRecent;
+ BottomSheetLayout(
+ {super.key,
+ required this.child,
+ required this.onSelect,
+ this.config,
+ this.heroBuilder,
+ this.initSelectedMedias,
+ this.multipleMediasBuilder,
+ this.startWithRecent = true}){
+ if(initSelectedMedias!=null&&GetInstance().isRegistered()){
+ Get.find().updateSelectedFiles(initSelectedMedias!);
+ }
+ }
+
+ @override
+ State createState() => _BottomSheetLayoutState();
+}
+
+class _BottomSheetLayoutState extends State {
+ BuildContext? collapsedContext;
+ bool viewCollapsedPicker = false;
+ BottomSheetBarController bottomSheetBarController =
+ BottomSheetBarController();
+ late BottomSheetController controller;
+
+ @override
+ void initState() {
+ controller = Get.put(BottomSheetController(bottomSheetBarController));
+ super.initState();
+ }
+
+ check() async {
+ var sheetController = controller.sheetController;
+ if (collapsedContext != null) {
+ final RenderBox renderBox =
+ collapsedContext!.findRenderObject() as RenderBox;
+ if (renderBox.size.height > 200 &&
+ !sheetController.isExpanded &&
+ !viewCollapsedPicker) {
+ await Future.delayed(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(Duration(milliseconds: 10));
+ setState(() {});
+ }
+ }
+ }
+ }
+
+ @override
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return GetBuilder(builder: (controller) {
+ return BottomSheetBar(
+ willPopScope: true,
+ 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,
+ multipleMediasBuilder: widget.multipleMediasBuilder,
+ initSelectedMedias: widget.initSelectedMedias,
+ startWithRecent: widget.startWithRecent,
+ )
+ : Container(
+ width: MediaQuery.of(context).size.width,
+ height: MediaQuery.of(context).size.height,
+ color: Colors.transparent,
+ );
+ },
+ body: widget.child,
+ collapsed: GetBuilder(
+ builder: (controller) => ViewCollapsed(
+ picker: GalleryPickerView(
+ onSelect: widget.onSelect,
+ config: widget.config,
+ sheetController: bottomSheetBarController,
+ heroBuilder: widget.heroBuilder,
+ multipleMediasBuilder: widget.multipleMediasBuilder,
+ initSelectedMedias: widget.initSelectedMedias,
+ 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;
+ ViewCollapsed({
+ super.key,
+ required this.picker,
+ required this.onBuild,
+ required this.viewPicker,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ onBuild(context);
+ return Container(
+ height: 50,
+ color: Colors.transparent,
+ child: viewPicker ? picker : null,
+ );
+ }
+}
diff --git a/lib/views/gallery_picker_view/gallery_picker_view.dart b/lib/views/gallery_picker_view/gallery_picker_view.dart
new file mode 100644
index 0000000..42c2076
--- /dev/null
+++ b/lib/views/gallery_picker_view/gallery_picker_view.dart
@@ -0,0 +1,225 @@
+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/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 'picker_appbar.dart';
+import 'reload_gallery.dart';
+
+class GalleryPickerView extends StatefulWidget {
+ Config? config;
+ Function(List selectedMedias) onSelect;
+ Widget Function(String tag, MediaFile media, BuildContext context)?
+ heroBuilder;
+ Widget Function(List medias, BuildContext context)?
+ multipleMediasBuilder;
+ bool startWithRecent;
+ BottomSheetBarController? sheetController;
+ List? initSelectedMedias;
+ bool singleMedia;
+ GalleryPickerView(
+ {super.key,
+ this.config,
+ required this.onSelect,
+ this.initSelectedMedias,
+ this.singleMedia=false,
+ this.sheetController,
+ this.heroBuilder,
+ this.multipleMediasBuilder,
+ this.startWithRecent = false});
+
+ @override
+ State createState() => _GalleryPickerState();
+}
+
+class _GalleryPickerState extends State {
+ late PhoneGalleryController galleryController;
+ BottomSheetController? bottomSheetController;
+ late PageController _scrollController;
+ bool noPhotoSeleceted = true;
+ late Config config;
+ @override
+ void initState() {
+ _scrollController =
+ PageController(initialPage: widget.startWithRecent ? 0 : 1);
+ if (GetInstance().isRegistered()) {
+ galleryController = Get.find();
+ config = galleryController.config;
+ } else {
+ galleryController = Get.put(PhoneGalleryController(widget.config,
+ onSelect: widget.onSelect,
+ heroBuilder: widget.heroBuilder,
+ multipleMediasBuilder: widget.multipleMediasBuilder,
+ initSelectedMedias:widget.initSelectedMedias,
+ isRecent: widget.startWithRecent));
+ config = galleryController.config;
+ }
+
+ if (widget.sheetController != null) {
+ if (GetInstance().isRegistered()) {
+ bottomSheetController = Get.find();
+ } else {
+ bottomSheetController =
+ Get.put(BottomSheetController(widget.sheetController!));
+ }
+ bottomSheetController!.galleryController = galleryController;
+ }
+ if (!galleryController.isInitialized) {
+ galleryController.initializeAlbums();
+ }
+ if (galleryController.isRecent) {
+ _scrollController = PageController(initialPage: 0);
+ }
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ double width = MediaQuery.of(context).size.width;
+ return GetBuilder(builder: (controller) {
+ return GetInstance().isRegistered()
+ ? controller.selectedAlbum == null
+ ? Scaffold(
+ backgroundColor: config.backgroundColor,
+ appBar: PickerAppBar(
+ controller: controller,
+ bottomSheetController: bottomSheetController,
+ ),
+ body: Column(
+ children: [
+ Container(
+ width: width,
+ height: 48,
+ color: config.appbarColor,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ Container(
+ decoration: controller.isRecent
+ ? BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: config.underlineColor,
+ width: 3.0,
+ ),
+ ))
+ : null,
+ height: 48,
+ width: width / 2,
+ child: TextButton(
+ onPressed: () {
+ _scrollController.animateTo(0,
+ duration:
+ const Duration(milliseconds: 50),
+ curve: Curves.easeIn);
+ setState(() {
+ controller.isRecent = true;
+ controller.switchPickerMode(false);
+ });
+ },
+ child: Text(config.recents,
+ style: controller.isRecent
+ ? config.selectedMenuStyle
+ : config.unselectedMenuStyle)),
+ ),
+ 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);
+ controller.isRecent = false;
+ controller.switchPickerMode(false);
+ },
+ child: Text(
+ config.gallery,
+ style: controller.isRecent
+ ? config.unselectedMenuStyle
+ : config.selectedMenuStyle,
+ )),
+ )
+ ],
+ ),
+ ),
+ 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,
+ singleMedia:widget.singleMedia
+ )
+ : const Center(
+ child: CircularProgressIndicator(
+ color: Colors.grey,
+ )),
+ AlbumCategoriesView(controller)
+ ]),
+ ),
+ ],
+ ),
+ )
+ : AlbumPage(
+ controller: controller,
+ album: controller.selectedAlbum!,
+ singleMedia: widget.singleMedia,
+ bottomSheetController: bottomSheetController,
+ )
+ : ReloadGallery(
+ config,
+ onpressed: () async {
+ if (widget.sheetController != null) {
+ bottomSheetController =
+ Get.put(BottomSheetController(widget.sheetController!));
+ }
+ galleryController = Get.put(PhoneGalleryController(config,
+ onSelect: widget.onSelect,
+ heroBuilder: widget.heroBuilder,
+ initSelectedMedias:widget.initSelectedMedias,
+ multipleMediasBuilder: widget.multipleMediasBuilder,
+ isRecent: widget.startWithRecent));
+ await controller.initializeAlbums();
+ if (bottomSheetController != null) {
+ bottomSheetController!.galleryController = galleryController;
+ }
+ setState(() {});
+ },
+ );
+ });
+ }
+}
diff --git a/lib/views/gallery_picker_view/picker_appbar.dart b/lib/views/gallery_picker_view/picker_appbar.dart
new file mode 100644
index 0000000..eff3437
--- /dev/null
+++ b/lib/views/gallery_picker_view/picker_appbar.dart
@@ -0,0 +1,74 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.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 {
+ PhoneGalleryController controller;
+ BottomSheetController? bottomSheetController;
+ PickerAppBar(
+ {super.key,
+ required this.bottomSheetController,
+ required this.controller});
+
+ @override
+ Widget build(BuildContext context) {
+ return TappableAppbar(
+ controller: bottomSheetController,
+ child: AppBar(
+ elevation: 0,
+ backgroundColor: controller.config.appbarColor,
+ leading: TextButton(
+ onPressed: () async {
+ if (GetInstance().isRegistered()) {
+ bottomSheetController!.close();
+ } else {
+ Navigator.pop(context);
+ await Future.delayed(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()
+ ],
+ ),
+ );
+ }
+
+ Widget getTitle() {
+ if (controller.pickerMode && controller.selectedFiles.isEmpty) {
+ return Text(
+ controller.config.tapPhotoSelect,
+ style: controller.config.appbarTextStyle,
+ );
+ } else if (controller.pickerMode && controller.selectedFiles.isNotEmpty) {
+ return Text(
+ "${controller.selectedFiles.length} ${controller.config.selected}",
+ style: controller.config.appbarTextStyle,
+ );
+ } else {
+ return const SizedBox();
+ }
+ }
+
+ @override
+ Size get preferredSize => Size.fromHeight(48);
+}
diff --git a/lib/views/gallery_picker_view/reload_gallery.dart b/lib/views/gallery_picker_view/reload_gallery.dart
new file mode 100644
index 0000000..42c7d86
--- /dev/null
+++ b/lib/views/gallery_picker_view/reload_gallery.dart
@@ -0,0 +1,42 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import '/gallery_picker.dart';
+import 'package:get/get.dart';
+
+import '../../controller/gallery_controller.dart';
+
+class ReloadGallery extends StatelessWidget {
+ late Config config;
+ Function() onpressed;
+ ReloadGallery(Config? config, {super.key, required this.onpressed}) {
+ this.config = config ?? Config();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: config.backgroundColor,
+ body: Center(
+ child: TextButton(
+ style: ButtonStyle(
+ foregroundColor: MaterialStateProperty.all(Colors.blue),
+ ),
+ child: Container(
+ height: 50,
+ padding: EdgeInsets.symmetric(horizontal: 30, vertical: 10),
+ decoration: BoxDecoration(
+ color: Colors.blue, borderRadius: BorderRadius.circular(10)),
+ child: const Center(
+ child: Text(
+ "Reload Gallery",
+ style: TextStyle(color: Colors.white),
+ ),
+ )),
+ onPressed: () {
+ onpressed();
+ },
+ )),
+ );
+ }
+}
diff --git a/lib/views/gallery_picker_view/tappable_appbar.dart b/lib/views/gallery_picker_view/tappable_appbar.dart
new file mode 100644
index 0000000..7a9eb18
--- /dev/null
+++ b/lib/views/gallery_picker_view/tappable_appbar.dart
@@ -0,0 +1,32 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:get/get.dart';
+
+import '../../controller/bottom_sheet_controller.dart';
+
+class TappableAppbar extends StatelessWidget {
+ BottomSheetController? controller;
+ Widget child;
+ TappableAppbar({super.key, required this.controller,required this.child});
+
+ @override
+ Widget build(BuildContext context) {
+ return GetInstance().isRegistered()
+ ? GestureDetector(
+ onLongPressEnd: (a) {
+ if (GetInstance().isRegistered()) {
+ controller!.tapingStatus(false);
+ }
+ },
+ onPanCancel: () {},
+ onPanDown: (a) {
+ if (GetInstance().isRegistered()) {
+ controller!.tapingStatus(true);
+ }
+ },
+ child: child,
+ )
+ : child;
+ }
+}
diff --git a/lib/views/hero_page.dart b/lib/views/hero_page.dart
deleted file mode 100644
index 4165cad..0000000
--- a/lib/views/hero_page.dart
+++ /dev/null
@@ -1,20 +0,0 @@
-import 'package:flutter/src/widgets/container.dart';
-import 'package:flutter/src/widgets/framework.dart';
-
-import '../models/media_file.dart';
-
-class HeroPage extends StatelessWidget {
- List selectedMedias;
- String heroTag;
- Widget child;
- HeroPage(
- {super.key,
- required this.selectedMedias,
- required this.heroTag,
- required this.child});
-
- @override
- Widget build(BuildContext context) {
- return child;
- }
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index 7b94e6d..8a6f587 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,7 @@
name: gallery_picker
description: A new Flutter package project.
version: 0.0.1
-homepage:
+homepage: https://github.com/FlutterWay/gallery_picker
environment:
sdk: '>=2.18.5 <3.0.0'
@@ -14,11 +14,12 @@ dependencies:
photo_gallery: ^1.1.1
permission_handler: ^10.2.0
transparent_image: ^2.0.0
+ camera: ^0.10.1
video_player: ^2.4.10
get: ^4.6.5
video_thumbnail: ^0.5.3
intl: ^0.18.0
- modal_bottom_sheet: ^2.1.2
+ bottom_sheet_bar: ^2.3.8
dev_dependencies:
flutter_test:
sdk: flutter
diff --git a/test/gallery_picker_test.dart b/test/gallery_picker_test.dart
index e420195..132d080 100644
--- a/test/gallery_picker_test.dart
+++ b/test/gallery_picker_test.dart
@@ -1,7 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
-import 'package:gallery_picker/gallery_picker.dart';
-
void main() {
//test('adds one to input values', () {
// final calculator = Calculator();