Simplify the state management for selected files
This commit is contained in:
parent
605b9d5da6
commit
02f7e801c6
8 changed files with 69 additions and 62 deletions
22
lib/models/selected_files.dart
Normal file
22
lib/models/selected_files.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
|
||||
class SelectedFiles extends ChangeNotifier {
|
||||
final files = Set<File>();
|
||||
|
||||
void toggleSelection(File file) {
|
||||
if (files.contains(file)) {
|
||||
files.remove(file);
|
||||
} else {
|
||||
files.add(file);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearAll() {
|
||||
files.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@ import 'package:photos/events/local_photos_updated_event.dart';
|
|||
import 'package:photos/models/device_folder.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/file_repository.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/ui/gallery.dart';
|
||||
import 'package:photos/ui/gallery_app_bar_widget.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class DeviceFolderPage extends StatefulWidget {
|
||||
final DeviceFolder folder;
|
||||
|
@ -18,7 +18,7 @@ class DeviceFolderPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _DeviceFolderPageState extends State<DeviceFolderPage> {
|
||||
final logger = Logger("DeviceFolderPageState");
|
||||
final _selectedFiles = SelectedFiles();
|
||||
|
||||
@override
|
||||
Widget build(Object context) {
|
||||
|
@ -26,12 +26,13 @@ class _DeviceFolderPageState extends State<DeviceFolderPage> {
|
|||
syncLoader: () => _getFilteredFiles(FileRepository.instance.files),
|
||||
reloadEvent: Bus.instance.on<LocalPhotosUpdatedEvent>(),
|
||||
tagPrefix: "device_folder",
|
||||
selectedFiles: _selectedFiles,
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: GalleryAppBarWidget(
|
||||
gallery,
|
||||
GalleryAppBarType.local_folder,
|
||||
widget.folder.name,
|
||||
_selectedFiles,
|
||||
widget.folder.thumbnail.deviceFolder,
|
||||
),
|
||||
body: gallery,
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/face_search_manager.dart';
|
||||
import 'package:photos/models/face.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/ui/gallery.dart';
|
||||
import 'package:photos/ui/gallery_app_bar_widget.dart';
|
||||
|
||||
class FaceSearchResultsPage extends StatelessWidget {
|
||||
final FaceSearchManager _faceSearchManager = FaceSearchManager.instance;
|
||||
final Face face;
|
||||
final selectedFiles = SelectedFiles();
|
||||
|
||||
FaceSearchResultsPage(this.face, {Key key}) : super(key: key);
|
||||
|
||||
|
@ -16,12 +18,13 @@ class FaceSearchResultsPage extends StatelessWidget {
|
|||
asyncLoader: (offset, limit) =>
|
||||
_faceSearchManager.getFaceSearchResults(face, offset, limit),
|
||||
tagPrefix: "face_search_results",
|
||||
selectedFiles: selectedFiles,
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: GalleryAppBarWidget(
|
||||
gallery,
|
||||
GalleryAppBarType.search_results,
|
||||
"Search results",
|
||||
selectedFiles,
|
||||
),
|
||||
body: Container(
|
||||
child: gallery,
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/events/event.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/ui/detail_page.dart';
|
||||
import 'package:photos/ui/loading_widget.dart';
|
||||
import 'package:photos/ui/sync_indicator.dart';
|
||||
|
@ -21,7 +22,7 @@ class Gallery extends StatefulWidget {
|
|||
// should have done the job.
|
||||
final Stream<Event> reloadEvent;
|
||||
final Future<void> Function() onRefresh;
|
||||
final fileSelectionChangeListeners = new List<Function(Set<File>)>();
|
||||
final SelectedFiles selectedFiles;
|
||||
final String tagPrefix;
|
||||
|
||||
Gallery({
|
||||
|
@ -29,19 +30,15 @@ class Gallery extends StatefulWidget {
|
|||
this.asyncLoader,
|
||||
this.reloadEvent,
|
||||
this.onRefresh,
|
||||
this.tagPrefix,
|
||||
@required this.selectedFiles,
|
||||
@required this.tagPrefix,
|
||||
});
|
||||
|
||||
_GalleryState state;
|
||||
|
||||
@override
|
||||
_GalleryState createState() {
|
||||
state = _GalleryState();
|
||||
return state;
|
||||
}
|
||||
|
||||
void clearSelection() {
|
||||
state.clearSelection();
|
||||
return _GalleryState();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +53,6 @@ class _GalleryState extends State<Gallery> {
|
|||
bool _requiresLoad = false;
|
||||
bool _hasLoadedAll = false;
|
||||
double _scrollOffset = 0;
|
||||
Set<File> _selectedFiles = Set<File>();
|
||||
List<File> _files;
|
||||
RefreshController _refreshController = RefreshController();
|
||||
|
||||
|
@ -70,13 +66,10 @@ class _GalleryState extends State<Gallery> {
|
|||
});
|
||||
});
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void clearSelection() {
|
||||
setState(() {
|
||||
_selectedFiles = Set<File>();
|
||||
widget.selectedFiles.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -204,7 +197,7 @@ class _GalleryState extends State<Gallery> {
|
|||
Widget _buildFile(BuildContext context, File file) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (_selectedFiles.isNotEmpty) {
|
||||
if (widget.selectedFiles.files.isNotEmpty) {
|
||||
_selectFile(file);
|
||||
} else {
|
||||
_routeToDetailPage(file, context);
|
||||
|
@ -217,7 +210,7 @@ class _GalleryState extends State<Gallery> {
|
|||
child: Container(
|
||||
margin: const EdgeInsets.all(2.0),
|
||||
decoration: BoxDecoration(
|
||||
border: _selectedFiles.contains(file)
|
||||
border: widget.selectedFiles.files.contains(file)
|
||||
? Border.all(width: 4.0, color: Colors.blue)
|
||||
: null,
|
||||
),
|
||||
|
@ -230,16 +223,7 @@ class _GalleryState extends State<Gallery> {
|
|||
}
|
||||
|
||||
void _selectFile(File file) {
|
||||
setState(() {
|
||||
if (_selectedFiles.contains(file)) {
|
||||
_selectedFiles.remove(file);
|
||||
} else {
|
||||
_selectedFiles.add(file);
|
||||
}
|
||||
for (final listener in widget.fileSelectionChangeListeners) {
|
||||
listener.call(_selectedFiles);
|
||||
}
|
||||
});
|
||||
widget.selectedFiles.toggleSelection(file);
|
||||
}
|
||||
|
||||
void _routeToDetailPage(File file, BuildContext context) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:photos/db/file_db.dart';
|
|||
import 'package:photos/events/remote_sync_event.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/file_repository.dart';
|
||||
import 'package:photos/ui/gallery.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/ui/setup_page.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:photos/ui/share_folder_widget.dart';
|
||||
|
@ -23,15 +23,15 @@ enum GalleryAppBarType {
|
|||
|
||||
class GalleryAppBarWidget extends StatefulWidget
|
||||
implements PreferredSizeWidget {
|
||||
final Gallery gallery;
|
||||
final GalleryAppBarType type;
|
||||
final String title;
|
||||
final SelectedFiles selectedFiles;
|
||||
final String path;
|
||||
|
||||
GalleryAppBarWidget(
|
||||
this.gallery,
|
||||
this.type,
|
||||
this.title, [
|
||||
this.title,
|
||||
this.selectedFiles, [
|
||||
this.path,
|
||||
]);
|
||||
|
||||
|
@ -45,7 +45,6 @@ class GalleryAppBarWidget extends StatefulWidget
|
|||
class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
||||
bool _hasSyncErrors = false;
|
||||
StreamSubscription<RemoteSyncEvent> _subscription;
|
||||
Set<File> _selectedFiles = Set<File>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -54,18 +53,15 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
_hasSyncErrors = !event.success;
|
||||
});
|
||||
});
|
||||
if (widget.gallery != null)
|
||||
widget.gallery.fileSelectionChangeListeners.add((selectedFiles) {
|
||||
setState(() {
|
||||
_selectedFiles = selectedFiles;
|
||||
});
|
||||
});
|
||||
widget.selectedFiles.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_selectedFiles.isEmpty) {
|
||||
if (widget.selectedFiles.files.isEmpty) {
|
||||
return AppBar(
|
||||
title: Text(widget.title),
|
||||
actions: _getDefaultActions(context),
|
||||
|
@ -79,7 +75,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
_clearSelectedFiles();
|
||||
},
|
||||
),
|
||||
title: Text(_selectedFiles.length.toString()),
|
||||
title: Text(widget.selectedFiles.files.length.toString()),
|
||||
actions: _getActions(context),
|
||||
);
|
||||
}
|
||||
|
@ -118,7 +114,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
|
||||
List<Widget> _getActions(BuildContext context) {
|
||||
List<Widget> actions = List<Widget>();
|
||||
if (_selectedFiles.isNotEmpty) {
|
||||
if (widget.selectedFiles.files.isNotEmpty) {
|
||||
if (widget.type != GalleryAppBarType.remote_folder &&
|
||||
widget.type != GalleryAppBarType.search_results) {
|
||||
actions.add(IconButton(
|
||||
|
@ -139,7 +135,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
}
|
||||
|
||||
void _shareSelected(BuildContext context) {
|
||||
shareMultiple(context, _selectedFiles.toList());
|
||||
shareMultiple(context, widget.selectedFiles.files.toList());
|
||||
}
|
||||
|
||||
void _showDeleteSheet(BuildContext context) {
|
||||
|
@ -173,10 +169,10 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
_deleteSelected(BuildContext context, bool deleteEverywhere) async {
|
||||
final dialog = createProgressDialog(context, "Deleting...");
|
||||
await dialog.show();
|
||||
await PhotoManager.editor
|
||||
.deleteWithIds(_selectedFiles.map((p) => p.localId).toList());
|
||||
await PhotoManager.editor.deleteWithIds(
|
||||
widget.selectedFiles.files.map((p) => p.localId).toList());
|
||||
|
||||
for (File file in _selectedFiles) {
|
||||
for (File file in widget.selectedFiles.files) {
|
||||
deleteEverywhere
|
||||
? await FileDB.instance.markForDeletion(file)
|
||||
: await FileDB.instance.delete(file);
|
||||
|
@ -188,10 +184,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
}
|
||||
|
||||
void _clearSelectedFiles() {
|
||||
widget.gallery.clearSelection();
|
||||
setState(() {
|
||||
_selectedFiles.clear();
|
||||
});
|
||||
widget.selectedFiles.clearAll();
|
||||
}
|
||||
|
||||
void _openSyncConfiguration(BuildContext context) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:photos/events/local_photos_updated_event.dart';
|
|||
import 'package:photos/models/filters/important_items_filter.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/file_repository.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/photo_sync_manager.dart';
|
||||
import 'package:photos/ui/device_folders_gallery_widget.dart';
|
||||
import 'package:photos/ui/gallery.dart';
|
||||
|
@ -34,10 +35,10 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
final _logger = Logger("HomeWidgetState");
|
||||
final _remoteFolderGalleryWidget = RemoteFolderGalleryWidget();
|
||||
final _deviceFolderGalleryWidget = DeviceFolderGalleryWidget();
|
||||
final _selectedFiles = SelectedFiles();
|
||||
|
||||
ShakeDetector _detector;
|
||||
int _selectedNavBarItem = 0;
|
||||
Set<File> _selectedPhotos = HashSet<File>();
|
||||
StreamSubscription<LocalPhotosUpdatedEvent>
|
||||
_localPhotosUpdatedEventSubscription;
|
||||
|
||||
|
@ -61,9 +62,9 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
var gallery = _getMainGalleryWidget();
|
||||
return Scaffold(
|
||||
appBar: GalleryAppBarWidget(
|
||||
gallery,
|
||||
GalleryAppBarType.homepage,
|
||||
widget.title,
|
||||
_selectedFiles,
|
||||
"/",
|
||||
),
|
||||
bottomNavigationBar: _buildBottomNavigationBar(),
|
||||
|
@ -108,6 +109,7 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
reloadEvent: Bus.instance.on<LocalPhotosUpdatedEvent>(),
|
||||
onRefresh: PhotoSyncManager.instance.sync,
|
||||
tagPrefix: "home_gallery",
|
||||
selectedFiles: _selectedFiles,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -149,12 +151,6 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
return filteredPhotos;
|
||||
}
|
||||
|
||||
void _clearSelectedPhotos() {
|
||||
setState(() {
|
||||
_selectedPhotos.clear();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_detector.stopListening();
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:photos/models/location.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/file_repository.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/ui/gallery.dart';
|
||||
|
||||
class ViewPort {
|
||||
|
@ -30,6 +31,8 @@ class LocationSearchResultsPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _LocationSearchResultsPageState extends State<LocationSearchResultsPage> {
|
||||
final _selectedFiles = SelectedFiles();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -40,6 +43,7 @@ class _LocationSearchResultsPageState extends State<LocationSearchResultsPage> {
|
|||
child: Gallery(
|
||||
syncLoader: _getResult,
|
||||
tagPrefix: "location_search",
|
||||
selectedFiles: _selectedFiles,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:photos/db/file_db.dart';
|
||||
import 'package:photos/folder_service.dart';
|
||||
import 'package:photos/models/folder.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/ui/gallery.dart';
|
||||
import 'package:photos/ui/gallery_app_bar_widget.dart';
|
||||
|
||||
|
@ -15,6 +16,8 @@ class RemoteFolderPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _RemoteFolderPageState extends State<RemoteFolderPage> {
|
||||
final _selectedFiles = SelectedFiles();
|
||||
|
||||
@override
|
||||
Widget build(Object context) {
|
||||
var gallery = Gallery(
|
||||
|
@ -22,12 +25,13 @@ class _RemoteFolderPageState extends State<RemoteFolderPage> {
|
|||
FileDB.instance.getAllInFolder(widget.folder.id, offset, limit),
|
||||
onRefresh: () => FolderSharingService.instance.syncDiff(widget.folder),
|
||||
tagPrefix: "remote_folder",
|
||||
selectedFiles: _selectedFiles,
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: GalleryAppBarWidget(
|
||||
gallery,
|
||||
GalleryAppBarType.remote_folder,
|
||||
widget.folder.name,
|
||||
_selectedFiles,
|
||||
widget.folder.deviceFolder,
|
||||
),
|
||||
body: gallery,
|
||||
|
|
Loading…
Add table
Reference in a new issue