#437 tv: rail nav fix
This commit is contained in:
parent
3be4f661cc
commit
207e8cd545
7 changed files with 185 additions and 123 deletions
|
@ -8,6 +8,7 @@ import 'package:aves/widgets/common/basic/insets.dart';
|
|||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/navigation/tv_rail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AboutPage extends StatelessWidget {
|
||||
static const routeName = '/about';
|
||||
|
@ -47,7 +48,9 @@ class AboutPage extends StatelessWidget {
|
|||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
const TvRail(),
|
||||
TvRail(
|
||||
controller: context.read<TvRailController>(),
|
||||
),
|
||||
Expanded(child: body),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -35,6 +35,7 @@ import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
|
|||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/home_page.dart';
|
||||
import 'package:aves/widgets/navigation/tv_page_transitions.dart';
|
||||
import 'package:aves/widgets/navigation/tv_rail.dart';
|
||||
import 'package:aves/widgets/welcome_page.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
@ -126,6 +127,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
late final Future<void> _appSetup;
|
||||
late final Future<bool> _shouldUseBoldFontLoader;
|
||||
late final Future<CorePalette?> _dynamicColorPaletteLoader;
|
||||
final TvRailController _tvRailController = TvRailController();
|
||||
final CollectionSource _mediaStoreSource = MediaStoreSource();
|
||||
final Debouncer _mediaStoreChangeDebouncer = Debouncer(delay: Durations.mediaContentChangeDebounceDelay);
|
||||
final Set<String> _changedUris = {};
|
||||
|
@ -173,6 +175,8 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
value: appModeNotifier,
|
||||
child: Provider<CollectionSource>.value(
|
||||
value: _mediaStoreSource,
|
||||
child: Provider<TvRailController>.value(
|
||||
value: _tvRailController,
|
||||
child: DurationsProvider(
|
||||
child: HighlightInfoProvider(
|
||||
child: OverlaySupport(
|
||||
|
@ -283,6 +287,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,10 @@ class _CollectionPageState extends State<CollectionPage> {
|
|||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
TvRail(currentCollection: _collection),
|
||||
TvRail(
|
||||
controller: context.read<TvRailController>(),
|
||||
currentCollection: _collection,
|
||||
),
|
||||
Expanded(child: body),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -128,7 +128,9 @@ class FilterGridPage<T extends CollectionFilter> extends StatelessWidget {
|
|||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
const TvRail(),
|
||||
TvRail(
|
||||
controller: context.read<TvRailController>(),
|
||||
),
|
||||
Expanded(child: body),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -20,12 +20,19 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class TvRailController {
|
||||
int? focusedIndex;
|
||||
double offset = 0;
|
||||
}
|
||||
|
||||
class TvRail extends StatefulWidget {
|
||||
// collection loaded in the `CollectionPage`, if any
|
||||
final CollectionLens? currentCollection;
|
||||
final TvRailController controller;
|
||||
|
||||
const TvRail({
|
||||
super.key,
|
||||
required this.controller,
|
||||
this.currentCollection,
|
||||
});
|
||||
|
||||
|
@ -34,10 +41,41 @@ class TvRail extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _TvRailState extends State<TvRail> {
|
||||
final _scrollController = ScrollController();
|
||||
late final ScrollController _scrollController;
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
|
||||
TvRailController get controller => widget.controller;
|
||||
|
||||
CollectionLens? get currentCollection => widget.currentCollection;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_scrollController = ScrollController(initialScrollOffset: controller.offset);
|
||||
_scrollController.addListener(_onScrollChanged);
|
||||
|
||||
final focusedIndex = controller.focusedIndex;
|
||||
if (focusedIndex != null) {
|
||||
controller.focusedIndex = null;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final nodes = _focusNode.children.toList();
|
||||
debugPrint('TLAD focusedIndex=$focusedIndex < nodes.length=${nodes.length}');
|
||||
if (focusedIndex < nodes.length) {
|
||||
nodes[focusedIndex].requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.removeListener(_onScrollChanged);
|
||||
_scrollController.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final header = Row(
|
||||
|
@ -64,11 +102,14 @@ class _TvRailState extends State<TvRail> {
|
|||
...[
|
||||
SettingsPage.routeName,
|
||||
AboutPage.routeName,
|
||||
if (!kReleaseMode) AppDebugPage.routeName,
|
||||
].map(_routeNavEntry),
|
||||
if (!kReleaseMode) _routeNavEntry(AppDebugPage.routeName),
|
||||
];
|
||||
|
||||
final rail = NavigationRail(
|
||||
final rail = Focus(
|
||||
focusNode: _focusNode,
|
||||
skipTraversal: true,
|
||||
child: NavigationRail(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
extended: true,
|
||||
destinations: navEntries
|
||||
|
@ -78,7 +119,11 @@ class _TvRailState extends State<TvRail> {
|
|||
))
|
||||
.toList(),
|
||||
selectedIndex: max(0, navEntries.indexWhere(((v) => v.isSelected))),
|
||||
onDestinationSelected: (index) => navEntries[index].onSelection(),
|
||||
onDestinationSelected: (index) {
|
||||
controller.focusedIndex = index;
|
||||
navEntries[index].onSelection();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return Column(
|
||||
|
@ -177,6 +222,8 @@ class _TvRailState extends State<TvRail> {
|
|||
(route) => false,
|
||||
);
|
||||
}
|
||||
|
||||
void _onScrollChanged() => controller.offset = _scrollController.offset;
|
||||
}
|
||||
|
||||
@immutable
|
||||
|
|
|
@ -18,7 +18,7 @@ class CrumbLine extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _CrumbLineState extends State<CrumbLine> {
|
||||
final ScrollController _controller = ScrollController();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
VolumeRelativeDirectory get directory => widget.directory;
|
||||
|
||||
|
@ -28,8 +28,8 @@ class _CrumbLineState extends State<CrumbLine> {
|
|||
if (oldWidget.directory.relativeDir.length < widget.directory.relativeDir.length) {
|
||||
// scroll to show last crumb
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final extent = _controller.position.maxScrollExtent;
|
||||
_controller.animateTo(
|
||||
final extent = _scrollController.position.maxScrollExtent;
|
||||
_scrollController.animateTo(
|
||||
extent,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeOutQuad,
|
||||
|
@ -53,7 +53,7 @@ class _CrumbLineState extends State<CrumbLine> {
|
|||
),
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
controller: _controller,
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
itemBuilder: (context, index) {
|
||||
Widget _buildText(String text) => Padding(
|
||||
|
|
|
@ -75,7 +75,9 @@ class _SettingsPageState extends State<SettingsPage> with FeedbackMixin {
|
|||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
const TvRail(),
|
||||
TvRail(
|
||||
controller: context.read<TvRailController>(),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
|
|
Loading…
Reference in a new issue