fixes: nav bar scroll & highlight + minor

This commit is contained in:
Thibault Deckers 2022-05-19 11:51:13 +09:00
parent 98e54f9dff
commit d4a545ad2a
6 changed files with 63 additions and 84 deletions

View file

@ -32,7 +32,6 @@ class BugReport extends StatefulWidget {
} }
class _BugReportState extends State<BugReport> with FeedbackMixin { class _BugReportState extends State<BugReport> with FeedbackMixin {
final ScrollController _infoScrollController = ScrollController();
late Future<String> _infoLoader; late Future<String> _infoLoader;
bool _showInstructions = false; bool _showInstructions = false;
@ -90,28 +89,12 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
constraints: const BoxConstraints(maxHeight: 100), constraints: const BoxConstraints(maxHeight: 100),
margin: const EdgeInsets.symmetric(vertical: 8), margin: const EdgeInsets.symmetric(vertical: 8),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: Theme( child: SingleChildScrollView(
data: Theme.of(context).copyWith( padding: const EdgeInsetsDirectional.only(start: 8, top: 4, end: 16, bottom: 4),
scrollbarTheme: ScrollbarThemeData( // to show a scroll bar, we would need to provide a scroll controller
thumbVisibility: MaterialStateProperty.all(true), // to both the `Scrollable` and the `Scrollbar`, but
radius: const Radius.circular(16), // as of Flutter v3.0.0, `SelectableText` does not allow passing the `scrollController`
crossAxisMargin: 6, child: SelectableText(info),
mainAxisMargin: 6,
interactive: true,
),
),
child: Scrollbar(
// when using `Scrollbar.isAlwaysShown`, a controller must be provided
// and used by both the `Scrollbar` and the `Scrollable`, but
// as of Flutter v2.8.1, `SelectableText` does not allow passing the `scrollController`
// so we wrap it in a `SingleChildScrollView`
controller: _infoScrollController,
child: SingleChildScrollView(
padding: const EdgeInsetsDirectional.only(start: 8, top: 4, end: 16, bottom: 4),
controller: _infoScrollController,
child: SelectableText(info),
),
),
), ),
); );
}, },

View file

@ -127,14 +127,22 @@ class _CollectionPageState extends State<CollectionPage> {
), ),
), ),
floatingActionButton: appMode == AppMode.pickMultipleMediaExternal && hasSelection floatingActionButton: appMode == AppMode.pickMultipleMediaExternal && hasSelection
? FloatingActionButton( ? TooltipTheme(
tooltip: context.l10n.collectionPickPageTitle, data: TooltipTheme.of(context).copyWith(
onPressed: () { preferBelow: false,
final items = context.read<Selection<AvesEntry>>().selectedItems; ),
final uris = items.map((entry) => entry.uri).toList(); child: FloatingActionButton(
ViewerService.pick(uris); tooltip: context.l10n.collectionPickPageTitle,
}, onPressed: () {
child: const Icon(AIcons.apply), final items = context.read<Selection<AvesEntry>>().selectedItems;
final uris = items.map((entry) => entry.uri).toList();
ViewerService.pick(uris);
},
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(16)),
),
child: const Icon(AIcons.apply),
),
) )
: null, : null,
drawer: AppDrawer(currentCollection: _collection), drawer: AppDrawer(currentCollection: _collection),

View file

@ -3,7 +3,6 @@ import 'dart:math';
import 'package:aves/widgets/common/basic/draggable_scrollbar.dart'; import 'package:aves/widgets/common/basic/draggable_scrollbar.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class FloatingNavBar extends StatefulWidget { class FloatingNavBar extends StatefulWidget {
final ScrollController? scrollController; final ScrollController? scrollController;
@ -28,11 +27,8 @@ class _FloatingNavBarState extends State<FloatingNavBar> with SingleTickerProvid
late AnimationController _controller; late AnimationController _controller;
late Animation<Offset> _offsetAnimation; late Animation<Offset> _offsetAnimation;
double? _lastOffset; double? _lastOffset;
double _delta = 0;
bool _isDragging = false; bool _isDragging = false;
static const double _deltaThreshold = 50;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -73,7 +69,6 @@ class _FloatingNavBarState extends State<FloatingNavBar> with SingleTickerProvid
void _registerWidget(FloatingNavBar widget) { void _registerWidget(FloatingNavBar widget) {
_lastOffset = null; _lastOffset = null;
_delta = 0;
widget.scrollController?.addListener(_onScrollChange); widget.scrollController?.addListener(_onScrollChange);
_subscriptions.add(widget.events.listen(_onDraggableScrollBarEvent)); _subscriptions.add(widget.events.listen(_onDraggableScrollBarEvent));
} }
@ -98,25 +93,19 @@ class _FloatingNavBarState extends State<FloatingNavBar> with SingleTickerProvid
if (scrollController == null) return; if (scrollController == null) return;
final offset = scrollController.offset; final offset = scrollController.offset;
_delta += offset - (_lastOffset ?? offset); final delta = offset - (_lastOffset ?? offset);
_lastOffset = offset; _lastOffset = offset;
if (_isDragging) return; double? newValue;
final after = scrollController.position.extentAfter;
final childHeight = widget.childHeight; final childHeight = widget.childHeight;
if (after < childHeight && scrollController.position.userScrollDirection == ScrollDirection.reverse) { final after = scrollController.position.extentAfter;
_controller.value = min(_controller.value, after / childHeight); if (after < childHeight && delta > 0) {
_delta = 0; newValue = min(_controller.value, after / childHeight);
return; } else if (!_isDragging || delta > 0) {
newValue = _controller.value + delta / childHeight;
} }
if (newValue != null) {
if (_delta.abs() > _deltaThreshold) { _controller.value = newValue.clamp(0.0, 1.0);
if (_delta > 0) {
_hide();
} else {
_show();
}
} }
} }
@ -124,22 +113,10 @@ class _FloatingNavBarState extends State<FloatingNavBar> with SingleTickerProvid
switch (event) { switch (event) {
case DraggableScrollBarEvent.dragStart: case DraggableScrollBarEvent.dragStart:
_isDragging = true; _isDragging = true;
_hide();
break; break;
case DraggableScrollBarEvent.dragEnd: case DraggableScrollBarEvent.dragEnd:
_isDragging = false; _isDragging = false;
_show();
break; break;
} }
} }
void _show() {
_controller.reverse();
_delta = 0;
}
void _hide() {
_controller.forward();
_delta = 0;
}
} }

View file

@ -15,19 +15,26 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class AppBottomNavBar extends StatelessWidget { class AppBottomNavBar extends StatefulWidget {
final Stream<DraggableScrollBarEvent> events; final Stream<DraggableScrollBarEvent> events;
// collection loaded in the `CollectionPage`, if any // collection loaded in the `CollectionPage`, if any
final CollectionLens? currentCollection; final CollectionLens? currentCollection;
static double get height => kBottomNavigationBarHeight + AvesFloatingBar.margin.vertical;
const AppBottomNavBar({ const AppBottomNavBar({
super.key, super.key,
required this.events, required this.events,
this.currentCollection, this.currentCollection,
}); });
static double get height => kBottomNavigationBarHeight + AvesFloatingBar.margin.vertical; @override
State<AppBottomNavBar> createState() => _AppBottomNavBarState();
}
class _AppBottomNavBarState extends State<AppBottomNavBar> {
String? _lastRoute;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -68,8 +75,8 @@ class AppBottomNavBar extends StatelessWidget {
}, },
child: FloatingNavBar( child: FloatingNavBar(
scrollController: PrimaryScrollController.of(context), scrollController: PrimaryScrollController.of(context),
events: events, events: widget.events,
childHeight: height + context.select<MediaQueryData, double>((mq) => mq.effectiveBottomPadding), childHeight: AppBottomNavBar.height + context.select<MediaQueryData, double>((mq) => mq.effectiveBottomPadding),
child: SafeArea( child: SafeArea(
child: child, child: child,
), ),
@ -78,13 +85,16 @@ class AppBottomNavBar extends StatelessWidget {
} }
int _getCurrentIndex(BuildContext context, List<AvesBottomNavItem> items) { int _getCurrentIndex(BuildContext context, List<AvesBottomNavItem> items) {
final currentRoute = context.currentRouteName; // current route may be null during navigation
final currentRoute = context.currentRouteName ?? _lastRoute;
_lastRoute = currentRoute;
final currentItem = items.firstWhereOrNull((item) { final currentItem = items.firstWhereOrNull((item) {
if (currentRoute != item.route) return false; if (currentRoute != item.route) return false;
if (item.route != CollectionPage.routeName) return true; if (item.route != CollectionPage.routeName) return true;
final currentFilters = currentCollection?.filters; final currentFilters = widget.currentCollection?.filters;
if (currentFilters == null || currentFilters.length > 1) return false; if (currentFilters == null || currentFilters.length > 1) return false;
return currentFilters.firstOrNull == item.filter; return currentFilters.firstOrNull == item.filter;
}); });

View file

@ -203,6 +203,7 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
myLocationEnabled: false, myLocationEnabled: false,
myLocationButtonEnabled: false, myLocationButtonEnabled: false,
markers: { markers: {
// TODO TLAD workaround for dot location marker not showing the last value until this is fixed: https://github.com/flutter/flutter/issues/103686
...markers, ...markers,
if (dotLocation != null && _dotMarkerBitmap != null) if (dotLocation != null && _dotMarkerBitmap != null)
Marker( Marker(

View file

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared name: _fe_analyzer_shared
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "39.0.0" version: "40.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.0" version: "4.1.0"
archive: archive:
dependency: transitive dependency: transitive
description: description:
@ -296,7 +296,7 @@ packages:
name: ffi name: ffi
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.2" version: "1.2.1"
fijkplayer: fijkplayer:
dependency: "direct main" dependency: "direct main"
description: description:
@ -319,35 +319,35 @@ packages:
name: firebase_core name: firebase_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.16.0" version: "1.17.0"
firebase_core_platform_interface: firebase_core_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_core_platform_interface name: firebase_core_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.3.0" version: "4.4.0"
firebase_core_web: firebase_core_web:
dependency: transitive dependency: transitive
description: description:
name: firebase_core_web name: firebase_core_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.3" version: "1.6.4"
firebase_crashlytics: firebase_crashlytics:
dependency: transitive dependency: transitive
description: description:
name: firebase_crashlytics name: firebase_crashlytics
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.7.2" version: "2.8.0"
firebase_crashlytics_platform_interface: firebase_crashlytics_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_crashlytics_platform_interface name: firebase_crashlytics_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.5" version: "3.2.6"
flex_color_picker: flex_color_picker:
dependency: "direct main" dependency: "direct main"
description: description:
@ -449,7 +449,7 @@ packages:
name: frontend_server_client name: frontend_server_client
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.2" version: "2.1.3"
fuchsia_remote_debug_protocol: fuchsia_remote_debug_protocol:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -517,7 +517,7 @@ packages:
name: http_parser name: http_parser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.0" version: "4.0.1"
image: image:
dependency: transitive dependency: transitive
description: description:
@ -657,7 +657,7 @@ packages:
name: overlay_support name: overlay_support
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.1" version: "2.0.0"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:
@ -769,7 +769,7 @@ packages:
name: percent_indicator name: percent_indicator
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.1" version: "4.2.2"
permission_handler: permission_handler:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1259,7 +1259,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.5.2" version: "2.6.1"
wkt_parser: wkt_parser:
dependency: transitive dependency: transitive
description: description:
@ -1287,7 +1287,7 @@ packages:
name: yaml name: yaml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.0" version: "3.1.1"
sdks: sdks:
dart: ">=2.17.0 <3.0.0" dart: ">=2.17.0 <3.0.0"
flutter: ">=2.10.0" flutter: ">=2.12.0"