Compare commits
1 Commits
swipe_to_s
...
swipe_to_s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
933a56ab8f |
@@ -1,7 +1,10 @@
|
||||
import "dart:async";
|
||||
import 'dart:math' show max;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ui/huge_listview/draggable_scrollbar.dart';
|
||||
import "package:photos/ui/viewer/gallery/component/multiple_groups_gallery_view.dart";
|
||||
import "package:photos/utils/debouncer.dart";
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
typedef HugeListViewItemBuilder<T> = Widget Function(
|
||||
@@ -94,6 +97,14 @@ class HugeListViewState<T> extends State<HugeListView<T>> {
|
||||
final listener = ItemPositionsListener.create();
|
||||
int lastIndexJump = -1;
|
||||
dynamic error;
|
||||
final scrollOffsetController = ScrollOffsetController();
|
||||
final scrollController = ScrollController();
|
||||
double currentScrollOffset = 0.0;
|
||||
StreamSubscription<double>? shouldScrollGalleryEventSubscription;
|
||||
final debouncer = Debouncer(
|
||||
const Duration(milliseconds: 100),
|
||||
executionInterval: const Duration(milliseconds: 100),
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -104,9 +115,32 @@ class HugeListViewState<T> extends State<HugeListView<T>> {
|
||||
: null;
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (shouldScrollGalleryEventSubscription != null) {
|
||||
shouldScrollGalleryEventSubscription!.cancel();
|
||||
}
|
||||
shouldScrollGalleryEventSubscription =
|
||||
SwipeToSelectGalleryScroll.of(context)
|
||||
.streamController
|
||||
.stream
|
||||
.listen((factor) {
|
||||
debouncer.run(() async {
|
||||
final double newOffset = (factor * 70);
|
||||
await scrollOffsetController.animateScroll(
|
||||
offset: newOffset,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
listener.itemPositions.removeListener(_sendScroll);
|
||||
shouldScrollGalleryEventSubscription?.cancel();
|
||||
SwipeToSelectGalleryScroll.of(context).streamController.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -136,61 +170,69 @@ class HugeListViewState<T> extends State<HugeListView<T>> {
|
||||
return widget.emptyResultBuilder!(context);
|
||||
}
|
||||
|
||||
return widget.isScrollablePositionedList
|
||||
? DraggableScrollbar(
|
||||
key: scrollKey,
|
||||
totalCount: widget.totalCount,
|
||||
initialScrollIndex: widget.startIndex,
|
||||
onChange: (position) {
|
||||
final int currentIndex = _currentFirst();
|
||||
final int floorIndex = (position * widget.totalCount).floor();
|
||||
final int cielIndex = (position * widget.totalCount).ceil();
|
||||
int nextIndexToJump;
|
||||
if (floorIndex != currentIndex && floorIndex > currentIndex) {
|
||||
nextIndexToJump = floorIndex;
|
||||
} else if (cielIndex != currentIndex &&
|
||||
cielIndex < currentIndex) {
|
||||
nextIndexToJump = floorIndex;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (lastIndexJump != nextIndexToJump) {
|
||||
lastIndexJump = nextIndexToJump;
|
||||
widget.controller?.jumpTo(index: nextIndexToJump);
|
||||
}
|
||||
},
|
||||
labelTextBuilder: widget.labelTextBuilder,
|
||||
backgroundColor: widget.thumbBackgroundColor,
|
||||
drawColor: widget.thumbDrawColor,
|
||||
heightScrollThumb: widget.thumbHeight,
|
||||
bottomSafeArea: widget.bottomSafeArea,
|
||||
currentFirstIndex: _currentFirst(),
|
||||
isEnabled: widget.isDraggableScrollbarEnabled,
|
||||
padding: widget.thumbPadding,
|
||||
child: ScrollablePositionedList.builder(
|
||||
physics: widget.disableScroll
|
||||
? const NeverScrollableScrollPhysics()
|
||||
: const BouncingScrollPhysics(),
|
||||
itemScrollController: widget.controller,
|
||||
itemPositionsListener: listener,
|
||||
return NotificationListener<ScrollNotification>(
|
||||
onNotification: (notification) {
|
||||
currentScrollOffset = notification.metrics.pixels;
|
||||
return false;
|
||||
},
|
||||
child: widget.isScrollablePositionedList
|
||||
? DraggableScrollbar(
|
||||
key: scrollKey,
|
||||
totalCount: widget.totalCount,
|
||||
initialScrollIndex: widget.startIndex,
|
||||
onChange: (position) {
|
||||
final int currentIndex = _currentFirst();
|
||||
final int floorIndex = (position * widget.totalCount).floor();
|
||||
final int cielIndex = (position * widget.totalCount).ceil();
|
||||
int nextIndexToJump;
|
||||
if (floorIndex != currentIndex && floorIndex > currentIndex) {
|
||||
nextIndexToJump = floorIndex;
|
||||
} else if (cielIndex != currentIndex &&
|
||||
cielIndex < currentIndex) {
|
||||
nextIndexToJump = floorIndex;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (lastIndexJump != nextIndexToJump) {
|
||||
lastIndexJump = nextIndexToJump;
|
||||
widget.controller?.jumpTo(index: nextIndexToJump);
|
||||
}
|
||||
},
|
||||
labelTextBuilder: widget.labelTextBuilder,
|
||||
backgroundColor: widget.thumbBackgroundColor,
|
||||
drawColor: widget.thumbDrawColor,
|
||||
heightScrollThumb: widget.thumbHeight,
|
||||
bottomSafeArea: widget.bottomSafeArea,
|
||||
currentFirstIndex: _currentFirst(),
|
||||
isEnabled: widget.isDraggableScrollbarEnabled,
|
||||
padding: widget.thumbPadding,
|
||||
child: ScrollablePositionedList.builder(
|
||||
physics: widget.disableScroll
|
||||
? const NeverScrollableScrollPhysics()
|
||||
: const BouncingScrollPhysics(),
|
||||
scrollOffsetController: scrollOffsetController,
|
||||
itemScrollController: widget.controller,
|
||||
itemPositionsListener: listener,
|
||||
initialScrollIndex: widget.startIndex,
|
||||
itemCount: max(widget.totalCount, 0),
|
||||
itemBuilder: (context, index) {
|
||||
return ExcludeSemantics(
|
||||
child: widget.itemBuilder(context, index),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: max(widget.totalCount, 0),
|
||||
controller: scrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return ExcludeSemantics(
|
||||
child: widget.itemBuilder(context, index),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: max(widget.totalCount, 0),
|
||||
itemBuilder: (context, index) {
|
||||
return ExcludeSemantics(
|
||||
child: widget.itemBuilder(context, index),
|
||||
);
|
||||
},
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
/// Jump to the [position] in the list. [position] is between 0.0 (first item) and 1.0 (last item), practically currentIndex / totalCount.
|
||||
|
||||
@@ -14,6 +14,7 @@ import "package:photos/ui/viewer/gallery/component/group/group_header_widget.dar
|
||||
import "package:photos/ui/viewer/gallery/component/group/type.dart";
|
||||
import 'package:photos/ui/viewer/gallery/gallery.dart';
|
||||
import "package:photos/ui/viewer/gallery/state/gallery_context_state.dart";
|
||||
import "package:photos/ui/viewer/gallery/swipe_to_select_helper.dart";
|
||||
|
||||
class LazyGroupGallery extends StatefulWidget {
|
||||
final List<EnteFile> files;
|
||||
@@ -233,21 +234,41 @@ class _LazyGroupGalleryState extends State<LazyGroupGallery> {
|
||||
),
|
||||
],
|
||||
),
|
||||
_shouldRender!
|
||||
? GroupGallery(
|
||||
photoGridSize: widget.photoGridSize,
|
||||
widget.selectedFiles != null
|
||||
? SwipeToSelectHelper(
|
||||
files: _filesInGroup,
|
||||
tag: widget.tag,
|
||||
asyncLoader: widget.asyncLoader,
|
||||
selectedFiles: widget.selectedFiles,
|
||||
limitSelectionToOne: widget.limitSelectionToOne,
|
||||
selectedFiles: widget.selectedFiles!,
|
||||
child: _shouldRender!
|
||||
? GroupGallery(
|
||||
photoGridSize: widget.photoGridSize,
|
||||
files: _filesInGroup,
|
||||
tag: widget.tag,
|
||||
asyncLoader: widget.asyncLoader,
|
||||
selectedFiles: widget.selectedFiles,
|
||||
limitSelectionToOne: widget.limitSelectionToOne,
|
||||
)
|
||||
// todo: perf eval should we have separate PlaceHolder for Groups
|
||||
// instead of creating a large cached view
|
||||
: PlaceHolderGridViewWidget(
|
||||
_filesInGroup.length,
|
||||
widget.photoGridSize,
|
||||
),
|
||||
)
|
||||
// todo: perf eval should we have separate PlaceHolder for Groups
|
||||
// instead of creating a large cached view
|
||||
: PlaceHolderGridViewWidget(
|
||||
_filesInGroup.length,
|
||||
widget.photoGridSize,
|
||||
),
|
||||
: _shouldRender!
|
||||
? GroupGallery(
|
||||
photoGridSize: widget.photoGridSize,
|
||||
files: _filesInGroup,
|
||||
tag: widget.tag,
|
||||
asyncLoader: widget.asyncLoader,
|
||||
selectedFiles: widget.selectedFiles,
|
||||
limitSelectionToOne: widget.limitSelectionToOne,
|
||||
)
|
||||
// todo: perf eval should we have separate PlaceHolder for Groups
|
||||
// instead of creating a large cached view
|
||||
: PlaceHolderGridViewWidget(
|
||||
_filesInGroup.length,
|
||||
widget.photoGridSize,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import "dart:async";
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:intl/intl.dart";
|
||||
import "package:logging/logging.dart";
|
||||
@@ -69,92 +71,119 @@ class MultipleGroupsGalleryView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final gType = GalleryContextState.of(context)!.type;
|
||||
return HugeListView<List<EnteFile>>(
|
||||
controller: itemScroller,
|
||||
startIndex: 0,
|
||||
totalCount: groupedFiles.length,
|
||||
isDraggableScrollbarEnabled: groupedFiles.length > 10,
|
||||
disableScroll: disableScroll,
|
||||
isScrollablePositionedList: isScrollablePositionedList,
|
||||
waitBuilder: (_) {
|
||||
return const EnteLoadingWidget();
|
||||
},
|
||||
emptyResultBuilder: (_) {
|
||||
final List<Widget> children = [];
|
||||
if (header != null) {
|
||||
children.add(header!);
|
||||
}
|
||||
children.add(
|
||||
Expanded(
|
||||
child: emptyState,
|
||||
),
|
||||
);
|
||||
if (footer != null) {
|
||||
children.add(footer!);
|
||||
}
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
Widget gallery;
|
||||
gallery = LazyGroupGallery(
|
||||
groupedFiles[index],
|
||||
index,
|
||||
reloadEvent,
|
||||
removalEventTypes,
|
||||
asyncLoader,
|
||||
selectedFiles,
|
||||
tagPrefix,
|
||||
Bus.instance
|
||||
.on<GalleryIndexUpdatedEvent>()
|
||||
.where((event) => event.tag == tagPrefix)
|
||||
.map((event) => event.index),
|
||||
enableFileGrouping,
|
||||
showSelectAllByDefault,
|
||||
logTag: logTag,
|
||||
photoGridSize: LocalSettings.instance.getPhotoGridSize(),
|
||||
limitSelectionToOne: limitSelectionToOne,
|
||||
);
|
||||
if (header != null && index == 0) {
|
||||
gallery = Column(children: [header!, gallery]);
|
||||
}
|
||||
if (footer != null && index == groupedFiles.length - 1) {
|
||||
gallery = Column(children: [gallery, footer!]);
|
||||
}
|
||||
return gallery;
|
||||
},
|
||||
labelTextBuilder: (int index) {
|
||||
try {
|
||||
final EnteFile file = groupedFiles[index][0];
|
||||
if (gType == GroupType.size) {
|
||||
return file.fileSize != null
|
||||
? convertBytesToReadableFormat(file.fileSize!)
|
||||
: "";
|
||||
return SwipeToSelectGalleryScroll(
|
||||
child: HugeListView<List<EnteFile>>(
|
||||
controller: itemScroller,
|
||||
startIndex: 0,
|
||||
totalCount: groupedFiles.length,
|
||||
isDraggableScrollbarEnabled: groupedFiles.length > 10,
|
||||
disableScroll: disableScroll,
|
||||
isScrollablePositionedList: isScrollablePositionedList,
|
||||
waitBuilder: (_) {
|
||||
return const EnteLoadingWidget();
|
||||
},
|
||||
emptyResultBuilder: (_) {
|
||||
final List<Widget> children = [];
|
||||
if (header != null) {
|
||||
children.add(header!);
|
||||
}
|
||||
|
||||
return DateFormat.yMMM(Localizations.localeOf(context).languageCode)
|
||||
.format(
|
||||
DateTime.fromMicrosecondsSinceEpoch(
|
||||
file.creationTime!,
|
||||
children.add(
|
||||
Expanded(
|
||||
child: emptyState,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
logger.severe("label text builder failed", e);
|
||||
return "";
|
||||
}
|
||||
},
|
||||
thumbBackgroundColor:
|
||||
Theme.of(context).colorScheme.galleryThumbBackgroundColor,
|
||||
thumbDrawColor: Theme.of(context).colorScheme.galleryThumbDrawColor,
|
||||
thumbPadding: header != null
|
||||
? const EdgeInsets.only(top: 60)
|
||||
: const EdgeInsets.all(0),
|
||||
bottomSafeArea: scrollBottomSafeArea,
|
||||
firstShown: (int firstIndex) {
|
||||
Bus.instance.fire(GalleryIndexUpdatedEvent(tagPrefix, firstIndex));
|
||||
},
|
||||
if (footer != null) {
|
||||
children.add(footer!);
|
||||
}
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
Widget gallery;
|
||||
gallery = LazyGroupGallery(
|
||||
groupedFiles[index],
|
||||
index,
|
||||
reloadEvent,
|
||||
removalEventTypes,
|
||||
asyncLoader,
|
||||
selectedFiles,
|
||||
tagPrefix,
|
||||
Bus.instance
|
||||
.on<GalleryIndexUpdatedEvent>()
|
||||
.where((event) => event.tag == tagPrefix)
|
||||
.map((event) => event.index),
|
||||
enableFileGrouping,
|
||||
showSelectAllByDefault,
|
||||
logTag: logTag,
|
||||
photoGridSize: LocalSettings.instance.getPhotoGridSize(),
|
||||
limitSelectionToOne: limitSelectionToOne,
|
||||
);
|
||||
if (header != null && index == 0) {
|
||||
gallery = Column(children: [header!, gallery]);
|
||||
}
|
||||
if (footer != null && index == groupedFiles.length - 1) {
|
||||
gallery = Column(children: [gallery, footer!]);
|
||||
}
|
||||
return gallery;
|
||||
},
|
||||
labelTextBuilder: (int index) {
|
||||
try {
|
||||
final EnteFile file = groupedFiles[index][0];
|
||||
if (gType == GroupType.size) {
|
||||
return file.fileSize != null
|
||||
? convertBytesToReadableFormat(file.fileSize!)
|
||||
: "";
|
||||
}
|
||||
|
||||
return DateFormat.yMMM(Localizations.localeOf(context).languageCode)
|
||||
.format(
|
||||
DateTime.fromMicrosecondsSinceEpoch(
|
||||
file.creationTime!,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
logger.severe("label text builder failed", e);
|
||||
return "";
|
||||
}
|
||||
},
|
||||
thumbBackgroundColor:
|
||||
Theme.of(context).colorScheme.galleryThumbBackgroundColor,
|
||||
thumbDrawColor: Theme.of(context).colorScheme.galleryThumbDrawColor,
|
||||
thumbPadding: header != null
|
||||
? const EdgeInsets.only(top: 60)
|
||||
: const EdgeInsets.all(0),
|
||||
bottomSafeArea: scrollBottomSafeArea,
|
||||
firstShown: (int firstIndex) {
|
||||
Bus.instance.fire(GalleryIndexUpdatedEvent(tagPrefix, firstIndex));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SwipeToSelectGalleryScroll extends InheritedWidget {
|
||||
final StreamController<double> streamController = StreamController();
|
||||
|
||||
SwipeToSelectGalleryScroll({
|
||||
required Widget child,
|
||||
Key? key,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
static SwipeToSelectGalleryScroll? maybeOf(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<SwipeToSelectGalleryScroll>();
|
||||
}
|
||||
|
||||
static SwipeToSelectGalleryScroll of(BuildContext context) {
|
||||
final SwipeToSelectGalleryScroll? result = maybeOf(context);
|
||||
assert(result != null, 'No SwipeToSelectGalleryScroll found in context');
|
||||
return result!;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(SwipeToSelectGalleryScroll oldWidget) {
|
||||
return streamController != oldWidget.streamController;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import "package:photos/ui/viewer/gallery/component/multiple_groups_gallery_view.
|
||||
import 'package:photos/ui/viewer/gallery/empty_state.dart';
|
||||
import "package:photos/ui/viewer/gallery/state/gallery_context_state.dart";
|
||||
import "package:photos/ui/viewer/gallery/state/selection_state.dart";
|
||||
import "package:photos/ui/viewer/gallery/swipe_to_select_helper.dart";
|
||||
import "package:photos/utils/debouncer.dart";
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
@@ -259,31 +258,27 @@ class GalleryState extends State<Gallery> {
|
||||
sortOrderAsc: _sortOrderAsc,
|
||||
inSelectionMode: widget.inSelectionMode,
|
||||
type: widget.groupType,
|
||||
child: SwipeToSelectHelper(
|
||||
files: currentGroupedFiles.expand((element) => element).toList(),
|
||||
child: MultipleGroupsGalleryView(
|
||||
itemScroller: _itemScroller,
|
||||
groupedFiles: currentGroupedFiles,
|
||||
disableScroll: widget.disableScroll,
|
||||
emptyState: widget.emptyState,
|
||||
asyncLoader: widget.asyncLoader,
|
||||
removalEventTypes: widget.removalEventTypes,
|
||||
tagPrefix: widget.tagPrefix,
|
||||
scrollBottomSafeArea: widget.scrollBottomSafeArea,
|
||||
limitSelectionToOne: widget.limitSelectionToOne,
|
||||
enableFileGrouping:
|
||||
widget.enableFileGrouping && widget.groupType.showGroupHeader(),
|
||||
logTag: _logTag,
|
||||
logger: _logger,
|
||||
reloadEvent: widget.reloadEvent,
|
||||
header: widget.header,
|
||||
footer: widget.footer,
|
||||
selectedFiles: widget.selectedFiles,
|
||||
child: MultipleGroupsGalleryView(
|
||||
itemScroller: _itemScroller,
|
||||
groupedFiles: currentGroupedFiles,
|
||||
disableScroll: widget.disableScroll,
|
||||
emptyState: widget.emptyState,
|
||||
asyncLoader: widget.asyncLoader,
|
||||
removalEventTypes: widget.removalEventTypes,
|
||||
tagPrefix: widget.tagPrefix,
|
||||
scrollBottomSafeArea: widget.scrollBottomSafeArea,
|
||||
limitSelectionToOne: widget.limitSelectionToOne,
|
||||
enableFileGrouping:
|
||||
widget.enableFileGrouping && widget.groupType.showGroupHeader(),
|
||||
logTag: _logTag,
|
||||
logger: _logger,
|
||||
reloadEvent: widget.reloadEvent,
|
||||
header: widget.header,
|
||||
footer: widget.footer,
|
||||
selectedFiles: widget.selectedFiles,
|
||||
showSelectAllByDefault: widget.showSelectAllByDefault &&
|
||||
widget.groupType.showGroupHeader(),
|
||||
isScrollablePositionedList: widget.isScrollablePositionedList,
|
||||
),
|
||||
showSelectAllByDefault:
|
||||
widget.showSelectAllByDefault && widget.groupType.showGroupHeader(),
|
||||
isScrollablePositionedList: widget.isScrollablePositionedList,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,11 @@ import "package:logging/logging.dart";
|
||||
import "package:photos/models/file/file.dart";
|
||||
import "package:photos/models/selected_files.dart";
|
||||
import "package:photos/ui/viewer/gallery/component/group/lazy_group_gallery.dart";
|
||||
import "package:photos/ui/viewer/gallery/component/multiple_groups_gallery_view.dart";
|
||||
|
||||
class SwipeToSelectHelper extends StatefulWidget {
|
||||
final List<EnteFile> files;
|
||||
final SelectedFiles? selectedFiles;
|
||||
final SelectedFiles selectedFiles;
|
||||
final Widget child;
|
||||
const SwipeToSelectHelper({
|
||||
required this.files,
|
||||
@@ -26,26 +27,24 @@ class _SwipeToSelectHelperState extends State<SwipeToSelectHelper> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.selectedFiles == null
|
||||
? widget.child
|
||||
: LastSelectedFileByDragging(
|
||||
filesInGroup: widget.files,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return SelectionGesturesEventProvider(
|
||||
selectedFiles: widget.selectedFiles!,
|
||||
files: widget.files,
|
||||
child: GroupGalleryGlobalKey(
|
||||
globalKey: _groupGalleryGlobalKey,
|
||||
child: SizedBox(
|
||||
key: _groupGalleryGlobalKey,
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
return LastSelectedFileByDragging(
|
||||
filesInGroup: widget.files,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return SelectionGesturesEventProvider(
|
||||
selectedFiles: widget.selectedFiles,
|
||||
files: widget.files,
|
||||
child: GroupGalleryGlobalKey(
|
||||
globalKey: _groupGalleryGlobalKey,
|
||||
child: SizedBox(
|
||||
key: _groupGalleryGlobalKey,
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +103,15 @@ class SelectionGesturesEventProvider extends StatefulWidget {
|
||||
class _SelectionGesturesEventProviderState
|
||||
extends State<SelectionGesturesEventProvider> {
|
||||
late SelectionGesturesEvent selectionGesturesEvent;
|
||||
late SwipeToSelectGalleryScroll swipeToSelectGalleryScroll;
|
||||
bool _isFingerOnScreenSinceLongPress = false;
|
||||
bool _isDragging = false;
|
||||
int prevSelectedFileIndex = -1;
|
||||
int currentSelectedFileIndex = -1;
|
||||
final _logger = Logger("PointerProvider");
|
||||
static const kUpThreshold = 180.0;
|
||||
static const kDownThreshold = 240.0;
|
||||
static const kSelectionSheetBuffer = 120.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -167,10 +170,13 @@ class _SelectionGesturesEventProviderState
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final heightOfScreen = MediaQuery.sizeOf(context).height;
|
||||
|
||||
return SelectionGesturesEvent(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
selectionGesturesEvent = SelectionGesturesEvent.of(context);
|
||||
swipeToSelectGalleryScroll = SwipeToSelectGalleryScroll.of(context);
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
selectionGesturesEvent.onTapStreamController
|
||||
@@ -195,6 +201,8 @@ class _SelectionGesturesEventProviderState
|
||||
(event.localDelta.dx.abs() > 0 &&
|
||||
event.localDelta.dy.abs() > 0)) {
|
||||
onDragToSelect(event.localPosition);
|
||||
|
||||
sinkScrollEvent(event.position.dy, heightOfScreen);
|
||||
}
|
||||
},
|
||||
onPointerDown: (event) {
|
||||
@@ -221,6 +229,28 @@ class _SelectionGesturesEventProviderState
|
||||
selectionGesturesEvent.moveOffsetStreamController.add(offset);
|
||||
_isDragging = true;
|
||||
}
|
||||
|
||||
void sinkScrollEvent(double yGlobalPos, double heightOfScreen) {
|
||||
final pixelsBeyondThresholdDown =
|
||||
yGlobalPos - (heightOfScreen - kDownThreshold);
|
||||
final pixelsBeyondThresholdUp = yGlobalPos - kUpThreshold;
|
||||
|
||||
if (pixelsBeyondThresholdUp < 0) {
|
||||
print(
|
||||
"up with strength: ${pixelsBeyondThresholdUp / kUpThreshold}",
|
||||
);
|
||||
swipeToSelectGalleryScroll.streamController.sink
|
||||
.add(pixelsBeyondThresholdUp / kUpThreshold);
|
||||
}
|
||||
if (pixelsBeyondThresholdDown > 0) {
|
||||
print(
|
||||
"down with strength: ${(pixelsBeyondThresholdDown + kSelectionSheetBuffer) / kDownThreshold}",
|
||||
);
|
||||
swipeToSelectGalleryScroll.streamController.sink.add(
|
||||
(pixelsBeyondThresholdDown + kSelectionSheetBuffer) / kDownThreshold,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SelectionGesturesEvent extends InheritedWidget {
|
||||
|
||||
Reference in New Issue
Block a user