diff --git a/lib/model/image_collection.dart b/lib/model/image_collection.dart index 7cbf6e3c8..8366c0501 100644 --- a/lib/model/image_collection.dart +++ b/lib/model/image_collection.dart @@ -2,7 +2,7 @@ import 'package:aves/model/image_entry.dart'; import 'package:aves/model/image_file_service.dart'; import 'package:aves/model/image_metadata.dart'; import 'package:aves/model/metadata_db.dart'; -import "package:collection/collection.dart"; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:path/path.dart'; diff --git a/lib/model/mime_types.dart b/lib/model/mime_types.dart index 2e592b492..dfd83ce93 100644 --- a/lib/model/mime_types.dart +++ b/lib/model/mime_types.dart @@ -1,6 +1,6 @@ class MimeTypes { - static const String MIME_VIDEO = "video"; - static const String MIME_JPEG = "image/jpeg"; - static const String MIME_PNG = "image/png"; - static const String MIME_GIF = "image/gif"; + static const String MIME_VIDEO = 'video'; + static const String MIME_JPEG = 'image/jpeg'; + static const String MIME_PNG = 'image/png'; + static const String MIME_GIF = 'image/gif'; } diff --git a/lib/widgets/album/thumbnail_collection.dart b/lib/widgets/album/thumbnail_collection.dart index 30e140d93..c1c82737c 100644 --- a/lib/widgets/album/thumbnail_collection.dart +++ b/lib/widgets/album/thumbnail_collection.dart @@ -2,9 +2,9 @@ import 'package:aves/model/image_collection.dart'; import 'package:aves/model/image_entry.dart'; import 'package:aves/widgets/album/sections.dart'; import 'package:aves/widgets/album/thumbnail.dart'; -import 'package:aves/widgets/common/draggable_scrollbar.dart'; import 'package:aves/widgets/common/icons.dart'; import 'package:aves/widgets/fullscreen/image_page.dart'; +import 'package:draggable_scrollbar/draggable_scrollbar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; @@ -78,10 +78,9 @@ class ThumbnailCollectionContent extends StatelessWidget { ], ), controller: _scrollController, - padding: EdgeInsets.only(top: topPadding, bottom: bottomInsets), - labelTextBuilder: (double offset) => Text( - "${offset ~/ 1}", - style: TextStyle(color: Colors.blueGrey), + padding: EdgeInsets.only( + top: topPadding, + bottom: bottomInsets, ), ), ); diff --git a/lib/widgets/common/draggable_scrollbar.dart b/lib/widgets/common/draggable_scrollbar.dart deleted file mode 100644 index 76ed1196c..000000000 --- a/lib/widgets/common/draggable_scrollbar.dart +++ /dev/null @@ -1,616 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -/// TLAD: copied from https://pub.dev/packages/draggable_scrollbar 0.0.4 -/// modified to allow any `ScrollView` as a child, not just `BoxScrollView` -/// modified to apply vertical padding when computing `barMaxScrollExtent` - -/// Build the Scroll Thumb and label using the current configuration -typedef Widget ScrollThumbBuilder( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text labelText, - BoxConstraints labelConstraints, -}); - -/// Build a Text widget using the current scroll offset -typedef Text LabelTextBuilder(double offsetY); - -/// A widget that will display a BoxScrollView with a ScrollThumb that can be dragged -/// for quick navigation of the BoxScrollView. -class DraggableScrollbar extends StatefulWidget { - /// The view that will be scrolled with the scroll thumb - final ScrollView child; - - /// A function that builds a thumb using the current configuration - final ScrollThumbBuilder scrollThumbBuilder; - - /// The height of the scroll thumb - final double heightScrollThumb; - - /// The background color of the label and thumb - final Color backgroundColor; - - /// The amount of padding that should surround the thumb - final EdgeInsetsGeometry padding; - - /// Determines how quickly the scrollbar will animate in and out - final Duration scrollbarAnimationDuration; - - /// How long should the thumb be visible before fading out - final Duration scrollbarTimeToFade; - - /// Build a Text widget from the current offset in the BoxScrollView - final LabelTextBuilder labelTextBuilder; - - /// Determines box constraints for Container displaying label - final BoxConstraints labelConstraints; - - /// The ScrollController for the BoxScrollView - final ScrollController controller; - - /// Determines scrollThumb displaying. If you draw own ScrollThumb and it is true you just don't need to use animation parameters in [scrollThumbBuilder] - final bool alwaysVisibleScrollThumb; - - DraggableScrollbar({ - Key key, - this.alwaysVisibleScrollThumb = false, - @required this.heightScrollThumb, - @required this.backgroundColor, - @required this.scrollThumbBuilder, - @required this.child, - @required this.controller, - this.padding, - this.scrollbarAnimationDuration = const Duration(milliseconds: 300), - this.scrollbarTimeToFade = const Duration(milliseconds: 600), - this.labelTextBuilder, - this.labelConstraints, - }) : assert(controller != null), - assert(scrollThumbBuilder != null), - assert(child.scrollDirection == Axis.vertical), - super(key: key); - - DraggableScrollbar.rrect({ - Key key, - Key scrollThumbKey, - this.alwaysVisibleScrollThumb = false, - @required this.child, - @required this.controller, - this.heightScrollThumb = 48.0, - this.backgroundColor = Colors.white, - this.padding, - this.scrollbarAnimationDuration = const Duration(milliseconds: 300), - this.scrollbarTimeToFade = const Duration(milliseconds: 600), - this.labelTextBuilder, - this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbRRectBuilder(scrollThumbKey, alwaysVisibleScrollThumb), - super(key: key); - - DraggableScrollbar.arrows({ - Key key, - Key scrollThumbKey, - this.alwaysVisibleScrollThumb = false, - @required this.child, - @required this.controller, - this.heightScrollThumb = 48.0, - this.backgroundColor = Colors.white, - this.padding, - this.scrollbarAnimationDuration = const Duration(milliseconds: 300), - this.scrollbarTimeToFade = const Duration(milliseconds: 600), - this.labelTextBuilder, - this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbArrowBuilder(scrollThumbKey, alwaysVisibleScrollThumb), - super(key: key); - - DraggableScrollbar.semicircle({ - Key key, - Key scrollThumbKey, - this.alwaysVisibleScrollThumb = false, - @required this.child, - @required this.controller, - this.heightScrollThumb = 48.0, - this.backgroundColor = Colors.white, - this.padding, - this.scrollbarAnimationDuration = const Duration(milliseconds: 300), - this.scrollbarTimeToFade = const Duration(milliseconds: 600), - this.labelTextBuilder, - this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb), - super(key: key); - - @override - _DraggableScrollbarState createState() => _DraggableScrollbarState(); - - static buildScrollThumbAndLabel({@required Widget scrollThumb, @required Color backgroundColor, @required Animation thumbAnimation, @required Animation labelAnimation, @required Text labelText, @required BoxConstraints labelConstraints, @required bool alwaysVisibleScrollThumb}) { - final scrollThumbAndLabel = labelText == null - ? scrollThumb - : Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - ScrollLabel( - animation: labelAnimation, - child: labelText, - backgroundColor: backgroundColor, - constraints: labelConstraints, - ), - scrollThumb, - ], - ); - - if (alwaysVisibleScrollThumb) { - return scrollThumbAndLabel; - } - return SlideFadeTransition( - animation: thumbAnimation, - child: scrollThumbAndLabel, - ); - } - - static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key scrollThumbKey, bool alwaysVisibleScrollThumb) { - return ( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text labelText, - BoxConstraints labelConstraints, - }) { - final scrollThumb = CustomPaint( - key: scrollThumbKey, - foregroundPainter: ArrowCustomPainter(Colors.grey), - child: Material( - elevation: 4.0, - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), - color: backgroundColor, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(height), - bottomLeft: Radius.circular(height), - topRight: Radius.circular(4.0), - bottomRight: Radius.circular(4.0), - ), - ), - ); - - return buildScrollThumbAndLabel( - scrollThumb: scrollThumb, - backgroundColor: backgroundColor, - thumbAnimation: thumbAnimation, - labelAnimation: labelAnimation, - labelText: labelText, - labelConstraints: labelConstraints, - alwaysVisibleScrollThumb: alwaysVisibleScrollThumb, - ); - }; - } - - static ScrollThumbBuilder _thumbArrowBuilder(Key scrollThumbKey, bool alwaysVisibleScrollThumb) { - return ( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text labelText, - BoxConstraints labelConstraints, - }) { - final scrollThumb = ClipPath( - child: Container( - height: height, - width: 20.0, - decoration: BoxDecoration( - color: backgroundColor, - borderRadius: BorderRadius.all( - Radius.circular(12.0), - ), - ), - ), - clipper: ArrowClipper(), - ); - - return buildScrollThumbAndLabel( - scrollThumb: scrollThumb, - backgroundColor: backgroundColor, - thumbAnimation: thumbAnimation, - labelAnimation: labelAnimation, - labelText: labelText, - labelConstraints: labelConstraints, - alwaysVisibleScrollThumb: alwaysVisibleScrollThumb, - ); - }; - } - - static ScrollThumbBuilder _thumbRRectBuilder(Key scrollThumbKey, bool alwaysVisibleScrollThumb) { - return ( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text labelText, - BoxConstraints labelConstraints, - }) { - final scrollThumb = Material( - elevation: 4.0, - child: Container( - constraints: BoxConstraints.tight( - Size(16.0, height), - ), - ), - color: backgroundColor, - borderRadius: BorderRadius.all(Radius.circular(7.0)), - ); - - return buildScrollThumbAndLabel( - scrollThumb: scrollThumb, - backgroundColor: backgroundColor, - thumbAnimation: thumbAnimation, - labelAnimation: labelAnimation, - labelText: labelText, - labelConstraints: labelConstraints, - alwaysVisibleScrollThumb: alwaysVisibleScrollThumb, - ); - }; - } -} - -class ScrollLabel extends StatelessWidget { - final Animation animation; - final Color backgroundColor; - final Text child; - - final BoxConstraints constraints; - static const BoxConstraints _defaultConstraints = BoxConstraints.tightFor(width: 72.0, height: 28.0); - - const ScrollLabel({ - Key key, - @required this.child, - @required this.animation, - @required this.backgroundColor, - this.constraints = _defaultConstraints, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return FadeTransition( - opacity: animation, - child: Container( - margin: EdgeInsets.only(right: 12.0), - child: Material( - elevation: 4.0, - color: backgroundColor, - borderRadius: BorderRadius.all(Radius.circular(16.0)), - child: Container( - constraints: constraints ?? _defaultConstraints, - alignment: Alignment.center, - child: child, - ), - ), - ), - ); - } -} - -class _DraggableScrollbarState extends State with TickerProviderStateMixin { - double _barOffset; - double _viewOffset; - bool _isDragInProcess; - - AnimationController _thumbAnimationController; - Animation _thumbAnimation; - AnimationController _labelAnimationController; - Animation _labelAnimation; - Timer _fadeoutTimer; - - @override - void initState() { - super.initState(); - _barOffset = 0.0; - _viewOffset = 0.0; - _isDragInProcess = false; - - _thumbAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); - - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastOutSlowIn, - ); - - _labelAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); - - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); - } - - @override - void dispose() { - _thumbAnimationController.dispose(); - _fadeoutTimer?.cancel(); - super.dispose(); - } - - double get barMaxScrollExtent => context.size.height - widget.heightScrollThumb - widget.padding.vertical; - - double get barMinScrollExtent => 0.0; - - double get viewMaxScrollExtent => widget.controller.position.maxScrollExtent; - - double get viewMinScrollExtent => widget.controller.position.minScrollExtent; - - @override - Widget build(BuildContext context) { - Widget labelText; - if (widget.labelTextBuilder != null && _isDragInProcess) { - labelText = widget.labelTextBuilder( - _viewOffset + _barOffset + widget.heightScrollThumb / 2, - ); - } - - return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { - //print("LayoutBuilder constraints=$constraints"); - - return NotificationListener( - onNotification: (ScrollNotification notification) { - changePosition(notification); - return false; - }, - child: Stack( - children: [ - RepaintBoundary( - child: widget.child, - ), - RepaintBoundary( - child: GestureDetector( - onVerticalDragStart: _onVerticalDragStart, - onVerticalDragUpdate: _onVerticalDragUpdate, - onVerticalDragEnd: _onVerticalDragEnd, - child: Container( - alignment: Alignment.topRight, - margin: EdgeInsets.only(top: _barOffset), - padding: widget.padding, - child: widget.scrollThumbBuilder( - widget.backgroundColor, - _thumbAnimation, - _labelAnimation, - widget.heightScrollThumb, - labelText: labelText, - labelConstraints: widget.labelConstraints, - ), - ), - ), - ), - ], - ), - ); - }); - } - - //scroll bar has received notification that it's view was scrolled - //so it should also changes his position - //but only if it isn't dragged - changePosition(ScrollNotification notification) { - if (_isDragInProcess) { - return; - } - - setState(() { - if (notification is ScrollUpdateNotification) { - _barOffset += getBarDelta( - notification.scrollDelta, - barMaxScrollExtent, - viewMaxScrollExtent, - ); - - if (_barOffset < barMinScrollExtent) { - _barOffset = barMinScrollExtent; - } - if (_barOffset > barMaxScrollExtent) { - _barOffset = barMaxScrollExtent; - } - - _viewOffset += notification.scrollDelta; - if (_viewOffset < widget.controller.position.minScrollExtent) { - _viewOffset = widget.controller.position.minScrollExtent; - } - if (_viewOffset > viewMaxScrollExtent) { - _viewOffset = viewMaxScrollExtent; - } - } - - if (notification is ScrollUpdateNotification || notification is OverscrollNotification) { - if (_thumbAnimationController.status != AnimationStatus.forward) { - _thumbAnimationController.forward(); - } - - _fadeoutTimer?.cancel(); - _fadeoutTimer = Timer(widget.scrollbarTimeToFade, () { - _thumbAnimationController.reverse(); - _labelAnimationController.reverse(); - _fadeoutTimer = null; - }); - } - }); - } - - double getBarDelta( - double scrollViewDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { - return scrollViewDelta * barMaxScrollExtent / viewMaxScrollExtent; - } - - double getScrollViewDelta( - double barDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { - return barDelta * viewMaxScrollExtent / barMaxScrollExtent; - } - - void _onVerticalDragStart(DragStartDetails details) { - setState(() { - _isDragInProcess = true; - _labelAnimationController.forward(); - _fadeoutTimer?.cancel(); - }); - } - - void _onVerticalDragUpdate(DragUpdateDetails details) { - setState(() { - if (_thumbAnimationController.status != AnimationStatus.forward) { - _thumbAnimationController.forward(); - } - if (_isDragInProcess) { - _barOffset += details.delta.dy; - - if (_barOffset < barMinScrollExtent) { - _barOffset = barMinScrollExtent; - } - if (_barOffset > barMaxScrollExtent) { - _barOffset = barMaxScrollExtent; - } - - double viewDelta = getScrollViewDelta(details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent); - - _viewOffset = widget.controller.position.pixels + viewDelta; - if (_viewOffset < widget.controller.position.minScrollExtent) { - _viewOffset = widget.controller.position.minScrollExtent; - } - if (_viewOffset > viewMaxScrollExtent) { - _viewOffset = viewMaxScrollExtent; - } - widget.controller.jumpTo(_viewOffset); - } - }); - } - - void _onVerticalDragEnd(DragEndDetails details) { - _fadeoutTimer = Timer(widget.scrollbarTimeToFade, () { - _thumbAnimationController.reverse(); - _labelAnimationController.reverse(); - _fadeoutTimer = null; - }); - setState(() { - _isDragInProcess = false; - }); - } -} - -/// Draws 2 triangles like arrow up and arrow down -class ArrowCustomPainter extends CustomPainter { - Color color; - - ArrowCustomPainter(this.color); - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint()..color = color; - const width = 12.0; - const height = 8.0; - final baseX = size.width / 2; - final baseY = size.height / 2; - - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); - } - - static Path _trianglePath(Offset o, double width, double height, bool isUp) { - return Path() - ..moveTo(o.dx, o.dy) - ..lineTo(o.dx + width, o.dy) - ..lineTo(o.dx + (width / 2), isUp ? o.dy - height : o.dy + height) - ..close(); - } -} - -///This cut 2 lines in arrow shape -class ArrowClipper extends CustomClipper { - @override - Path getClip(Size size) { - Path path = Path(); - path.lineTo(0.0, size.height); - path.lineTo(size.width, size.height); - path.lineTo(size.width, 0.0); - path.lineTo(0.0, 0.0); - path.close(); - - double arrowWidth = 8.0; - double startPointX = (size.width - arrowWidth) / 2; - double startPointY = size.height / 2 - arrowWidth / 2; - path.moveTo(startPointX, startPointY); - path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); - path.lineTo(startPointX + arrowWidth, startPointY); - path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); - path.lineTo(startPointX, startPointY + 1.0); - path.close(); - - startPointY = size.height / 2 + arrowWidth / 2; - path.moveTo(startPointX + arrowWidth, startPointY); - path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); - path.lineTo(startPointX, startPointY); - path.lineTo(startPointX, startPointY - 1.0); - path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); - path.lineTo(startPointX + arrowWidth, startPointY - 1.0); - path.close(); - - return path; - } - - @override - bool shouldReclip(CustomClipper oldClipper) => false; -} - -class SlideFadeTransition extends StatelessWidget { - final Animation animation; - final Widget child; - - const SlideFadeTransition({ - Key key, - @required this.animation, - @required this.child, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return AnimatedBuilder( - animation: animation, - builder: (context, child) => animation.value == 0.0 ? Container() : child, - child: SlideTransition( - position: Tween( - begin: Offset(0.3, 0.0), - end: Offset(0.0, 0.0), - ).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), - ), - ); - } -} diff --git a/pubspec.lock b/pubspec.lock index 4c2d287a6..c5c2d075c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -64,6 +64,15 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + draggable_scrollbar: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: d7bd215380458393bd3dd5aa2e671f73606086f8 + url: "git://github.com/deckerst/flutter-draggable-scrollbar.git" + source: git + version: "0.0.4" flushbar: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 68160dcbe..da2c24875 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,6 +18,9 @@ dependencies: sdk: flutter chewie: 0.9.7 collection: + draggable_scrollbar: + git: + url: git://github.com/deckerst/flutter-draggable-scrollbar.git flushbar: flutter_native_timezone: flutter_sticky_header: