init
This commit is contained in:
parent
4c936d4061
commit
448913f98c
@ -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\\
|
||||
|
@ -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"}
|
||||
{"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"}
|
@ -1,3 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
* Init
|
||||
|
429
README.md
429
README.md
@ -1,39 +1,422 @@
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
## modern_gallery_picker
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
|
||||
Gallery Picker is a flutter package that will allow you to pick media file(s), manage and navigate inside your gallery with modern tools and views.
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/developing-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/galleryPickerSlide.png" width="1200"/>
|
||||
|
||||
## 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
|
||||
|
||||
<div style="text-align: center">
|
||||
<table>
|
||||
<tr>
|
||||
<td style="text-align: center">
|
||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_light.gif" width="200"/>
|
||||
</td>
|
||||
<td style="text-align: center">
|
||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_dark.gif" width="200"/>
|
||||
</td>
|
||||
<td style="text-align: center">
|
||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/gallery_picker_destination.gif" width="200" />
|
||||
</td>
|
||||
<td style="text-align: center">
|
||||
<img src="https://raw.githubusercontent.com/FlutterWay/files/main/camera_page.gif" width="200" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
## 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
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
```
|
||||
#### Ios
|
||||
Add these configurations to your `ios/Runner/info.plist` file ??????????
|
||||
```xml
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Privacy - Photo Library Usage Description</string>
|
||||
<key>NSMotionUsageDescription</key>
|
||||
<string>Motion usage description</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>NSPhotoLibraryAddUsageDescription</string>
|
||||
```
|
||||
|
||||
## 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<MediaFile>? 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<MediaFile>? 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<MediaFile>? medias = await GalleryPicker.pickMedias(
|
||||
context: context,
|
||||
config: Config(
|
||||
mode: Mode.dark
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
#### Give an initial selected media files
|
||||
|
||||
```dart
|
||||
List<MediaFile>? 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<MediaFile>? 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
|
@ -2,6 +2,7 @@
|
||||
package="com.example.gallery_picker_example">
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<application
|
||||
android:label="gallery_picker_example"
|
||||
android:name="${applicationName}"
|
||||
|
154
example/lib/examples/bottom_sheet_example.dart
Normal file
154
example/lib/examples/bottom_sheet_example.dart
Normal file
@ -0,0 +1,154 @@
|
||||
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';
|
||||
|
||||
class BottomSheetExample extends StatefulWidget {
|
||||
const BottomSheetExample({super.key});
|
||||
|
||||
@override
|
||||
State<BottomSheetExample> createState() => _BottomSheetExampleState();
|
||||
}
|
||||
|
||||
class _BottomSheetExampleState extends State<BottomSheetExample> {
|
||||
List<MediaFile> selectedMedias = [];
|
||||
int pageIndex = 0;
|
||||
var controller = PageController(initialPage: 0);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: BottomSheetLayout(
|
||||
onSelect: (List<MediaFile> 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: <Widget>[
|
||||
const Spacer(),
|
||||
const Text(
|
||||
'These are your selected medias',
|
||||
),
|
||||
const Divider(),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Stack(children: [
|
||||
if (selectedMedias.isNotEmpty)
|
||||
PageView(
|
||||
controller: controller,
|
||||
children: [
|
||||
for (var media in selectedMedias)
|
||||
Center(
|
||||
child: MediaProvider(
|
||||
media: media,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (selectedMedias.isNotEmpty)
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (pageIndex < selectedMedias.length - 1) {
|
||||
pageIndex++;
|
||||
controller.animateToPage(pageIndex,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeIn);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.chevron_right,
|
||||
size: 100,
|
||||
color: Colors.red,
|
||||
)),
|
||||
),
|
||||
if (selectedMedias.isNotEmpty)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (pageIndex > 0) {
|
||||
pageIndex--;
|
||||
controller.animateToPage(pageIndex,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeIn);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.chevron_left,
|
||||
size: 100,
|
||||
color: Colors.red,
|
||||
)),
|
||||
),
|
||||
]),
|
||||
),
|
||||
SizedBox(
|
||||
height: 65,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
for (int i = 0; i < selectedMedias.length; i++)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
pageIndex = i;
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
143
example/lib/examples/gallery_picker_example.dart
Normal file
143
example/lib/examples/gallery_picker_example.dart
Normal file
@ -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<GalleryPickerExample> createState() => _GalleryPickerExampleState();
|
||||
}
|
||||
|
||||
class _GalleryPickerExampleState extends State<GalleryPickerExample> {
|
||||
List<MediaFile> 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: <Widget>[
|
||||
const Spacer(),
|
||||
const Text(
|
||||
'These are your selected medias',
|
||||
),
|
||||
const Divider(),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Stack(children: [
|
||||
if (selectedMedias.isNotEmpty)
|
||||
PageView(
|
||||
controller: controller,
|
||||
children: [
|
||||
for (var media in selectedMedias)
|
||||
Center(
|
||||
child: MediaProvider(
|
||||
media: media,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (selectedMedias.isNotEmpty)
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (pageIndex < selectedMedias.length - 1) {
|
||||
pageIndex++;
|
||||
controller.animateToPage(pageIndex,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeIn);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.chevron_right,
|
||||
size: 100,
|
||||
color: Colors.red,
|
||||
)),
|
||||
),
|
||||
if (selectedMedias.isNotEmpty)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (pageIndex > 0) {
|
||||
pageIndex--;
|
||||
controller.animateToPage(pageIndex,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeIn);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.chevron_left,
|
||||
size: 100,
|
||||
color: Colors.red,
|
||||
)),
|
||||
),
|
||||
]),
|
||||
),
|
||||
SizedBox(
|
||||
height: 65,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
for (int i = 0; i < selectedMedias.length; i++)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
pageIndex = i;
|
||||
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<void> pickMedias() async {
|
||||
List<MediaFile>? medias = await GalleryPicker.pickMedias(
|
||||
context: context,
|
||||
config: Config(mode: Mode.dark),
|
||||
);
|
||||
if (medias != null) {
|
||||
setState(() {
|
||||
selectedMedias += medias;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
131
example/lib/examples/multiple_medias.dart
Normal file
131
example/lib/examples/multiple_medias.dart
Normal file
@ -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<MediaFile> medias;
|
||||
const MultipleMediasView(this.medias, {super.key});
|
||||
|
||||
@override
|
||||
State<MultipleMediasView> createState() => _MultipleMediasViewState();
|
||||
}
|
||||
|
||||
class _MultipleMediasViewState extends State<MultipleMediasView> {
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
94
example/lib/examples/pick_medias_with_builder.dart
Normal file
94
example/lib/examples/pick_medias_with_builder.dart
Normal file
@ -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<PickMediasWithBuilder> createState() => _PickMediasWithBuilderState();
|
||||
}
|
||||
|
||||
class _PickMediasWithBuilderState extends State<PickMediasWithBuilder> {
|
||||
@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: <Widget>[
|
||||
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);
|
||||
}
|
||||
}
|
284
example/lib/examples/whatsapp_pick_photo.dart
Normal file
284
example/lib/examples/whatsapp_pick_photo.dart
Normal file
@ -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<WhatsappPickPhoto> createState() => _WhatsappPickPhotoState();
|
||||
}
|
||||
|
||||
class _WhatsappPickPhotoState extends State<WhatsappPickPhoto> {
|
||||
CameraController? cameraController;
|
||||
GalleryMedia? gallery;
|
||||
List<MediaFile> selectedMedias = [];
|
||||
List<CameraDescription>? cameras;
|
||||
CameraLensDirection cameraLensDirection = CameraLensDirection.front;
|
||||
@override
|
||||
void initState() {
|
||||
initCamera();
|
||||
fetchMedias();
|
||||
GalleryPicker.listenSelectedFiles.listen((medias) {
|
||||
selectedMedias = medias;
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> fetchMedias() async {
|
||||
gallery = await GalleryPicker.collectGallery;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> 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<MediaFile> 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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<MediaFile>? 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<MyHomePage> {
|
||||
int _counter = 0;
|
||||
List<MediaFile> selectedMedias = [];
|
||||
|
||||
void viewGalleryPicker() {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => GalleryPicker(
|
||||
onSelect: (selectedMedias) {
|
||||
print(selectedMedias.length);
|
||||
@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) {
|
||||
return Scaffold(
|
||||
|
||||
appBar: AppBar(
|
||||
title: Text("Pick medias"),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Spacer(),
|
||||
const Text(
|
||||
'These are your selected medias',
|
||||
),
|
||||
const Divider(),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Stack(children: [
|
||||
if (selectedMedias.isNotEmpty)
|
||||
PageView(
|
||||
controller: controller,
|
||||
children: [
|
||||
for (var media in selectedMedias)
|
||||
Center(
|
||||
child: MediaProvider(
|
||||
media: media,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (selectedMedias.isNotEmpty)
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (pageIndex < selectedMedias.length - 1) {
|
||||
pageIndex++;
|
||||
controller.animateToPage(pageIndex,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeIn);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.chevron_right,
|
||||
size: 100,
|
||||
color: Colors.red,
|
||||
)),
|
||||
),
|
||||
if (selectedMedias.isNotEmpty)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (pageIndex > 0) {
|
||||
pageIndex--;
|
||||
controller.animateToPage(pageIndex,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeIn);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.chevron_left,
|
||||
size: 100,
|
||||
color: Colors.red,
|
||||
)),
|
||||
),
|
||||
]),
|
||||
),
|
||||
SizedBox(
|
||||
height: 65,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
for (int i = 0; i < selectedMedias.length; i++)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
pageIndex = i;
|
||||
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<void> pickMedias() async {
|
||||
List<MediaFile>? 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('Flippers Page'),
|
||||
title: const Text('Hero 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,
|
||||
body: Center(
|
||||
child: Hero(
|
||||
tag: tag,
|
||||
child: Image.memory(media.thumbnail!),
|
||||
),
|
||||
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: "messages",
|
||||
title: "Selected Medias",
|
||||
medias: [media],
|
||||
)),
|
||||
);
|
||||
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(
|
||||
child: const 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
context: context);
|
||||
}
|
||||
|
||||
@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),
|
||||
),
|
||||
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: <Widget>[
|
||||
const Text(
|
||||
'You have pushed the button this many times:',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: showBottomSheet,
|
||||
tooltip: 'Increment',
|
||||
child: const Icon(Icons.add),
|
||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
||||
);
|
||||
Future<void> getGalleryMedia() async {
|
||||
GalleryMedia? allmedia = await GalleryPicker.collectGallery;
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
//});
|
||||
}
|
||||
|
41
lib/controller/bottom_sheet_controller.dart
Normal file
41
lib/controller/bottom_sheet_controller.dart
Normal file
@ -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<void> open() async {
|
||||
await sheetController.expand();
|
||||
}
|
||||
|
||||
void tapingStatus(bool value) {
|
||||
appBarTapping = value;
|
||||
if (galleryController == null) {
|
||||
Get.find<PhoneGalleryController>().update();
|
||||
} else {
|
||||
galleryController!.update();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
isClosing = true;
|
||||
update();
|
||||
await sheetController.collapse();
|
||||
isClosing = false;
|
||||
update();
|
||||
}
|
||||
|
||||
void disposeController() {
|
||||
isClosing = false;
|
||||
appBarTapping = false;
|
||||
GetInstance().delete<BottomSheetController>();
|
||||
}
|
||||
|
||||
BottomSheetController(this.sheetController);
|
||||
}
|
@ -1,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<MediaFile>? initSelectedMedias,
|
||||
required this.multipleMediasBuilder}) {
|
||||
this.config = config ?? Config();
|
||||
if (initSelectedMedias != null) {
|
||||
_selectedFiles = initSelectedMedias.map((e) => e).toList();
|
||||
}
|
||||
}
|
||||
bool isRecent;
|
||||
Function(List<MediaFile> selectedMedias) onSelect;
|
||||
Widget Function(String tag, MediaFile media, BuildContext context)?
|
||||
heroBuilder;
|
||||
Widget Function(List<MediaFile> medias, BuildContext context)?
|
||||
multipleMediaBuilder;
|
||||
multipleMediasBuilder;
|
||||
GalleryAlbum? selectedAlbum;
|
||||
List<GalleryAlbum> _galleryAlbums = [];
|
||||
List<GalleryAlbum> get galleryAlbums => _galleryAlbums;
|
||||
@ -36,10 +42,19 @@ class PhoneGalleryController extends GetxController {
|
||||
bool _pickerMode = false;
|
||||
bool get pickerMode => _pickerMode;
|
||||
|
||||
void updateSelectedFiles(List<MediaFile> 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,17 +74,46 @@ 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<PickerListener>()) {
|
||||
print(_selectedFiles.length);
|
||||
Get.find<PickerListener>().updateController(_selectedFiles);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> 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<void> initializeAlbums() async {
|
||||
GalleryMedia? media = await PhoneGalleryController.collectGallery;
|
||||
if (media != null) {
|
||||
this._galleryAlbums = media.albums;
|
||||
}
|
||||
_isInitialized = true;
|
||||
update();
|
||||
}
|
||||
|
||||
static Future<GalleryMedia?> get collectGallery async {
|
||||
if (await promptPermissionSetting()) {
|
||||
List<GalleryAlbum> tempGalleryAlbums = [];
|
||||
|
||||
List<Album> photoAlbums =
|
||||
@ -90,15 +135,17 @@ class PhoneGalleryController extends GetxController {
|
||||
|
||||
if (lastPhotoDate == null) {
|
||||
try {
|
||||
entireGalleryAlbum.thumbnail = await videoAlbum.getThumbnail(highQuality: true);
|
||||
entireGalleryAlbum.thumbnail =
|
||||
await videoAlbum.getThumbnail(highQuality: true);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
} else if (lastVideoDate == null) {
|
||||
} else {
|
||||
if (lastVideoDate.isBefore(lastPhotoDate)) {
|
||||
if (lastVideoDate.isAfter(lastPhotoDate)) {
|
||||
try {
|
||||
entireGalleryAlbum.thumbnail = await videoAlbum.getThumbnail(highQuality: true);
|
||||
entireGalleryAlbum.thumbnail =
|
||||
await videoAlbum.getThumbnail(highQuality: true);
|
||||
} catch (e) {
|
||||
entireGalleryAlbum.thumbnail = null;
|
||||
print(e);
|
||||
@ -115,16 +162,16 @@ class PhoneGalleryController extends GetxController {
|
||||
tempGalleryAlbums.add(entireGalleryAlbum);
|
||||
}
|
||||
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();
|
||||
return GalleryMedia(tempGalleryAlbums);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
GalleryAlbum? get recent {
|
||||
@ -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<PhoneGalleryController>();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
24
lib/controller/picker_listener.dart
Normal file
24
lib/controller/picker_listener.dart
Normal file
@ -0,0 +1,24 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../models/media_file.dart';
|
||||
|
||||
class PickerListener extends GetxController {
|
||||
StreamController<List<MediaFile>> controller =
|
||||
StreamController<List<MediaFile>>();
|
||||
|
||||
Stream<List<MediaFile>> get stream => controller.stream;
|
||||
|
||||
void updateController(List<MediaFile> medias) {
|
||||
print("len:${medias.length}");
|
||||
controller.add(medias);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller.close();
|
||||
GetInstance().delete<PickerListener>();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
@ -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<PhoneGalleryController>();
|
||||
class GalleryPicker {
|
||||
static Stream<List<MediaFile>> get listenSelectedFiles {
|
||||
var controller = Get.put(PickerListener());
|
||||
return controller.stream;
|
||||
}
|
||||
|
||||
class GalleryPicker extends StatefulWidget {
|
||||
Config? config;
|
||||
Function(List<MediaFile> selectedMedias) onSelect;
|
||||
Widget Function(String tag, MediaFile media, BuildContext context)?
|
||||
heroBuilder;
|
||||
Widget Function(List<MediaFile> medias, BuildContext context)?
|
||||
multipleMediaBuilder;
|
||||
bool startWithRecent;
|
||||
GalleryPicker(
|
||||
{super.key,
|
||||
this.config,
|
||||
required this.onSelect,
|
||||
this.heroBuilder,
|
||||
this.multipleMediaBuilder,
|
||||
this.startWithRecent = false});
|
||||
|
||||
@override
|
||||
State<GalleryPicker> createState() => _GalleryPickerState();
|
||||
static void disposeSelectedFilesListener() {
|
||||
if (GetInstance().isRegistered<PickerListener>()) {
|
||||
Get.find<PickerListener>().dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class _GalleryPickerState extends State<GalleryPicker> {
|
||||
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<PhoneGalleryController>()) {
|
||||
galleryController = Get.find<PhoneGalleryController>();
|
||||
config = galleryController.config;
|
||||
Get.find<PhoneGalleryController>().disposeController();
|
||||
}
|
||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
||||
Get.find<BottomSheetController>().disposeController();
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<MediaFile>?> pickMedias(
|
||||
{Config? config,
|
||||
bool startWithRecent = false,
|
||||
List<MediaFile>? initSelectedMedias,
|
||||
required BuildContext context}) async {
|
||||
List<MediaFile>? medias;
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => GalleryPickerView(
|
||||
onSelect: (mediasTmp) {
|
||||
medias = mediasTmp;
|
||||
},
|
||||
config: config,
|
||||
initSelectedMedias: initSelectedMedias,
|
||||
startWithRecent: startWithRecent,
|
||||
)),
|
||||
);
|
||||
return medias;
|
||||
}
|
||||
|
||||
static Future<void> pickMediasWithBuilder(
|
||||
{Config? config,
|
||||
required Widget Function(List<MediaFile> medias, BuildContext context)?
|
||||
multipleMediasBuilder,
|
||||
Widget Function(String tag, MediaFile media, BuildContext context)?
|
||||
heroBuilder,
|
||||
List<MediaFile>? 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<void> openSheet() async {
|
||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
||||
await Get.find<BottomSheetController>().open();
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> closeSheet() async {
|
||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
||||
await Get.find<BottomSheetController>().close();
|
||||
}
|
||||
}
|
||||
|
||||
static bool get isSheetOpened {
|
||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
||||
return Get.find<BottomSheetController>().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<void> initializeGallery() async {
|
||||
if (await _promptPermissionSetting()) {
|
||||
print("granted");
|
||||
await galleryController.initializeAlbums();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _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<PhoneGalleryController>(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<GalleryMedia?> get collectGallery async {
|
||||
return await PhoneGalleryController.collectGallery;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ class Config {
|
||||
unselectedMenuStyle;
|
||||
String recents, gallery, lastMonth, lastWeek, tapPhotoSelect, selected;
|
||||
List<String> 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
|
||||
|
@ -14,6 +14,12 @@ class GalleryAlbum {
|
||||
dateCategories.expand((element) => element.files).toList().length;
|
||||
String? get name => album.name;
|
||||
|
||||
List<MediaFile> get medias {
|
||||
return dateCategories
|
||||
.expand<MediaFile>((element) => element.files)
|
||||
.toList();
|
||||
}
|
||||
|
||||
late AlbumType type;
|
||||
|
||||
set setType(AlbumType type) {
|
||||
@ -37,9 +43,9 @@ class GalleryAlbum {
|
||||
|
||||
Future<void> initialize() async {
|
||||
List<DateCategory> 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)
|
||||
|
19
lib/models/gallery_media.dart
Normal file
19
lib/models/gallery_media.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'gallery_album.dart';
|
||||
|
||||
class GalleryMedia {
|
||||
List<GalleryAlbum> 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);
|
||||
}
|
@ -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;
|
||||
Uint8List? data;
|
||||
late String id;
|
||||
bool thumbnailFailed = false;
|
||||
|
||||
MediaFile({required this.mediaFile}) {
|
||||
type = mediaFile.mediumType;
|
||||
File? file;
|
||||
MediaFile({required this.medium}) {
|
||||
type = medium.mediumType;
|
||||
id = medium.id;
|
||||
}
|
||||
Future<void> 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() {
|
||||
Future<File> getFile() async {
|
||||
file = await medium.getFile();
|
||||
return file!;
|
||||
}
|
||||
|
||||
Future<Uint8List> getData() async {
|
||||
if (file == null) {
|
||||
await getFile();
|
||||
}
|
||||
data = await file!.readAsBytes();
|
||||
return data!;
|
||||
}
|
||||
|
||||
void unselect({PhoneGalleryController? controller}) {
|
||||
if (controller != null) {
|
||||
controller.unselectMedia(this);
|
||||
} else {
|
||||
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
||||
Get.find<PhoneGalleryController>().unselectMedia(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void select() {
|
||||
void select({PhoneGalleryController? controller}) {
|
||||
if (controller != null) {
|
||||
controller.selectMedia(this);
|
||||
} else {
|
||||
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
||||
Get.find<PhoneGalleryController>().selectMedia(this);
|
||||
}
|
||||
|
||||
bool get isSelected =>
|
||||
Get.find<PhoneGalleryController>().isSelectedMedia(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool? isSelected({PhoneGalleryController? controller}) {
|
||||
if (controller != null) {
|
||||
return controller.isSelectedMedia(this);
|
||||
} else {
|
||||
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
||||
return Get.find<PhoneGalleryController>().isSelectedMedia(this);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,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<StatefulWidget> createState() => AlbumPageState();
|
||||
}
|
||||
|
||||
class AlbumPageState extends State<AlbumPage> {
|
||||
List<Medium>? _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: <Widget>[
|
||||
...?_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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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<VideoProvider> {
|
||||
VideoPlayerController? _controller;
|
||||
File? _file;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
initAsync();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> 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: <Widget>[
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
74
lib/user_widgets/album_categories_view.dart
Normal file
74
lib/user_widgets/album_categories_view.dart
Normal file
@ -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<GalleryAlbum> 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: <Widget>[
|
||||
...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),
|
||||
]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
28
lib/user_widgets/album_medias.dart
Normal file
28
lib/user_widgets/album_medias.dart
Normal file
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
64
lib/user_widgets/date_category_view.dart
Normal file
64
lib/user_widgets/date_category_view.dart
Normal file
@ -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: <Widget>[
|
||||
...category.files.map(
|
||||
(medium) => ThumbnailMedia(
|
||||
media: medium,
|
||||
onErrorBuilder: onMediaErrorBuilder,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
24
lib/user_widgets/files_stream_builder.dart
Normal file
24
lib/user_widgets/files_stream_builder.dart
Normal file
@ -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<MediaFile>? medias, BuildContext context) builder;
|
||||
FilesStreamBuilder({super.key, required this.builder}) {
|
||||
Get.put(PickerListener());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder(
|
||||
stream: Get.find<PickerListener>().stream,
|
||||
builder: ((context, snapshot) {
|
||||
print("snapshot:${snapshot.data}");
|
||||
return builder(snapshot.data, context);
|
||||
}));
|
||||
}
|
||||
}
|
26
lib/user_widgets/media_provider.dart
Normal file
26
lib/user_widgets/media_provider.dart
Normal file
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
67
lib/user_widgets/photo_provider.dart
Normal file
67
lib/user_widgets/photo_provider.dart
Normal file
@ -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<PhotoProvider> {
|
||||
VideoPlayerController? _controller;
|
||||
late MediaFile media;
|
||||
@override
|
||||
void initState() {
|
||||
media = widget.media;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
initMedia();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> initMedia() async {
|
||||
await media.getData();
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
@ -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<PhoneGalleryController>().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(
|
57
lib/user_widgets/thumbnail_media.dart
Normal file
57
lib/user_widgets/thumbnail_media.dart
Normal file
@ -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,
|
||||
)),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
110
lib/user_widgets/video_provider.dart
Normal file
110
lib/user_widgets/video_provider.dart
Normal file
@ -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<VideoProvider> {
|
||||
VideoPlayerController? _controller;
|
||||
File? _file;
|
||||
late MediaFile media;
|
||||
@override
|
||||
void initState() {
|
||||
media = widget.media;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
initMedia();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> 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: <Widget>[
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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<PhoneGalleryController>().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,
|
||||
|
43
lib/views/album_categories_view/album_categories_view.dart
Normal file
43
lib/views/album_categories_view/album_categories_view.dart
Normal file
@ -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: <Widget>[
|
||||
...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,
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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<PhoneGalleryController>();
|
||||
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: <Widget>[
|
||||
...galleryController.galleryAlbums.map(
|
||||
(album) => GestureDetector(
|
||||
onTap: () => galleryController.changeAlbum(album),
|
||||
child: Stack(fit: StackFit.passthrough, children: [
|
||||
ThumbnailAlbum(album: album, failIconColor: galleryController.config.appbarIconColor),
|
||||
]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
77
lib/views/album_view/album_appbar.dart
Normal file
77
lib/views/album_view/album_appbar.dart
Normal file
@ -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);
|
||||
}
|
40
lib/views/album_view/album_medias_view.dart
Normal file
40
lib/views/album_view/album_medias_view.dart
Normal file
@ -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,
|
||||
))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
39
lib/views/album_view/album_page.dart
Normal file
39
lib/views/album_view/album_page.dart
Normal file
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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<PhoneGalleryController>().config.textStyle,
|
||||
style: controller.config.textStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -43,7 +47,11 @@ class DateCategoryWidget extends StatelessWidget {
|
||||
crossAxisSpacing: 1.0,
|
||||
children: <Widget>[
|
||||
...category.files.map(
|
||||
(medium) => MediaView(medium),
|
||||
(medium) => MediaView(
|
||||
medium,
|
||||
controller: controller,
|
||||
singleMedia: singleMedia,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
@ -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<PhoneGalleryController>(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<PhoneGalleryController>().pickerMode
|
||||
? TextButton(
|
||||
onPressed: () {
|
||||
Get.find<PhoneGalleryController>().switchPickerMode(true);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.check_box_outlined,
|
||||
color: config.appbarIconColor,
|
||||
))
|
||||
: const SizedBox()
|
||||
],
|
||||
),
|
||||
body: GalleryCategoryViewWidget(galleryAlbum: album),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
@ -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<PhoneGalleryController>().selectedFiles.isNotEmpty)
|
||||
Align(alignment: Alignment.bottomCenter, child: SelectedMediaWidget())
|
||||
]);
|
||||
}
|
||||
}
|
@ -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<PhoneGalleryController>();
|
||||
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<void>(builder: (BuildContext context) {
|
||||
return controller.heroBuilder!(file.medium.id, file, context);
|
||||
}));
|
||||
} else {
|
||||
controller.selectedFiles.add(file);
|
||||
controller.onSelect(controller.selectedFiles);
|
||||
controller.updatePickerListener();
|
||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
||||
Get.find<BottomSheetController>().close();
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
controller.disposeController();
|
||||
}
|
||||
}
|
||||
} 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<void>(builder: (BuildContext context) {
|
||||
return controller.heroBuilder!(file.mediaFile.id, file,context);
|
||||
return controller.heroBuilder!(file.medium.id, file, context);
|
||||
}));
|
||||
} else {
|
||||
controller.onSelect([file]);
|
||||
controller.selectedFiles.add(file);
|
||||
controller.onSelect(controller.selectedFiles);
|
||||
controller.updatePickerListener();
|
||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
||||
Get.find<BottomSheetController>().close();
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
GetInstance().delete<PhoneGalleryController>();
|
||||
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,
|
||||
|
@ -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<PhoneGalleryController>();
|
||||
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<void>(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<void>(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<BottomSheetController>()) {
|
||||
Get.find<BottomSheetController>().close();
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
controller.disposeController();
|
||||
}
|
||||
}
|
||||
},
|
154
lib/views/bottom_sheet.dart
Normal file
154
lib/views/bottom_sheet.dart
Normal file
@ -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<MediaFile>? initSelectedMedias;
|
||||
final Function(List<MediaFile> selectedMedias) onSelect;
|
||||
final Widget Function(String tag, MediaFile media, BuildContext context)?
|
||||
heroBuilder;
|
||||
final Widget Function(List<MediaFile> 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<PhoneGalleryController>()){
|
||||
Get.find<PhoneGalleryController>().updateSelectedFiles(initSelectedMedias!);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
State<BottomSheetLayout> createState() => _BottomSheetLayoutState();
|
||||
}
|
||||
|
||||
class _BottomSheetLayoutState extends State<BottomSheetLayout> {
|
||||
BuildContext? collapsedContext;
|
||||
bool viewCollapsedPicker = false;
|
||||
BottomSheetBarController bottomSheetBarController =
|
||||
BottomSheetBarController();
|
||||
late BottomSheetController controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
controller = Get.put(BottomSheetController(bottomSheetBarController));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
check() async {
|
||||
var sheetController = controller.sheetController;
|
||||
if (collapsedContext != null) {
|
||||
final RenderBox renderBox =
|
||||
collapsedContext!.findRenderObject() as RenderBox;
|
||||
if (renderBox.size.height > 200 &&
|
||||
!sheetController.isExpanded &&
|
||||
!viewCollapsedPicker) {
|
||||
await Future.delayed(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<BottomSheetController>(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<BottomSheetController>(
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
225
lib/views/gallery_picker_view/gallery_picker_view.dart
Normal file
225
lib/views/gallery_picker_view/gallery_picker_view.dart
Normal file
@ -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<MediaFile> selectedMedias) onSelect;
|
||||
Widget Function(String tag, MediaFile media, BuildContext context)?
|
||||
heroBuilder;
|
||||
Widget Function(List<MediaFile> medias, BuildContext context)?
|
||||
multipleMediasBuilder;
|
||||
bool startWithRecent;
|
||||
BottomSheetBarController? sheetController;
|
||||
List<MediaFile>? 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<GalleryPickerView> createState() => _GalleryPickerState();
|
||||
}
|
||||
|
||||
class _GalleryPickerState extends State<GalleryPickerView> {
|
||||
late PhoneGalleryController galleryController;
|
||||
BottomSheetController? bottomSheetController;
|
||||
late PageController _scrollController;
|
||||
bool noPhotoSeleceted = true;
|
||||
late Config config;
|
||||
@override
|
||||
void initState() {
|
||||
_scrollController =
|
||||
PageController(initialPage: widget.startWithRecent ? 0 : 1);
|
||||
if (GetInstance().isRegistered<PhoneGalleryController>()) {
|
||||
galleryController = Get.find<PhoneGalleryController>();
|
||||
config = galleryController.config;
|
||||
} 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>()) {
|
||||
bottomSheetController = Get.find<BottomSheetController>();
|
||||
} 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<PhoneGalleryController>(builder: (controller) {
|
||||
return GetInstance().isRegistered<PhoneGalleryController>()
|
||||
? controller.selectedAlbum == null
|
||||
? Scaffold(
|
||||
backgroundColor: config.backgroundColor,
|
||||
appBar: PickerAppBar(
|
||||
controller: controller,
|
||||
bottomSheetController: bottomSheetController,
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: width,
|
||||
height: 48,
|
||||
color: config.appbarColor,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Container(
|
||||
decoration: controller.isRecent
|
||||
? BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: config.underlineColor,
|
||||
width: 3.0,
|
||||
),
|
||||
))
|
||||
: null,
|
||||
height: 48,
|
||||
width: width / 2,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
_scrollController.animateTo(0,
|
||||
duration:
|
||||
const Duration(milliseconds: 50),
|
||||
curve: Curves.easeIn);
|
||||
setState(() {
|
||||
controller.isRecent = true;
|
||||
controller.switchPickerMode(false);
|
||||
});
|
||||
},
|
||||
child: Text(config.recents,
|
||||
style: controller.isRecent
|
||||
? config.selectedMenuStyle
|
||||
: config.unselectedMenuStyle)),
|
||||
),
|
||||
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(() {});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
74
lib/views/gallery_picker_view/picker_appbar.dart
Normal file
74
lib/views/gallery_picker_view/picker_appbar.dart
Normal file
@ -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>()) {
|
||||
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);
|
||||
}
|
42
lib/views/gallery_picker_view/reload_gallery.dart
Normal file
42
lib/views/gallery_picker_view/reload_gallery.dart
Normal file
@ -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<Color>(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();
|
||||
},
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
32
lib/views/gallery_picker_view/tappable_appbar.dart
Normal file
32
lib/views/gallery_picker_view/tappable_appbar.dart
Normal file
@ -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<BottomSheetController>()
|
||||
? GestureDetector(
|
||||
onLongPressEnd: (a) {
|
||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
||||
controller!.tapingStatus(false);
|
||||
}
|
||||
},
|
||||
onPanCancel: () {},
|
||||
onPanDown: (a) {
|
||||
if (GetInstance().isRegistered<BottomSheetController>()) {
|
||||
controller!.tapingStatus(true);
|
||||
}
|
||||
},
|
||||
child: child,
|
||||
)
|
||||
: child;
|
||||
}
|
||||
}
|
@ -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<MediaFile> selectedMedias;
|
||||
String heroTag;
|
||||
Widget child;
|
||||
HeroPage(
|
||||
{super.key,
|
||||
required this.selectedMedias,
|
||||
required this.heroTag,
|
||||
required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return child;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user