fixes: nav bar scroll & highlight + minor
This commit is contained in:
parent
98e54f9dff
commit
d4a545ad2a
6 changed files with 63 additions and 84 deletions
|
@ -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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -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(
|
||||||
|
|
30
pubspec.lock
30
pubspec.lock
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue