diff --git a/lib/components/comic.dart b/lib/components/comic.dart index 6420308..d01f63a 100644 --- a/lib/components/comic.dart +++ b/lib/components/comic.dart @@ -829,6 +829,7 @@ class ComicList extends StatefulWidget { this.trailingSliver, this.errorLeading, this.menuBuilder, + this.controller, }); final Future>> Function(int page)? loadPage; @@ -843,6 +844,8 @@ class ComicList extends StatefulWidget { final List Function(Comic)? menuBuilder; + final ScrollController? controller; + @override State createState() => ComicListState(); } @@ -1064,6 +1067,7 @@ class ComicListState extends State { ); } return SmoothCustomScrollView( + controller: widget.controller, slivers: [ if (widget.leadingSliver != null) widget.leadingSliver!, if (_maxPage != 1) _buildSliverPageSelector(), diff --git a/lib/components/navigation_bar.dart b/lib/components/navigation_bar.dart index 73e89ec..7965921 100644 --- a/lib/components/navigation_bar.dart +++ b/lib/components/navigation_bar.dart @@ -47,10 +47,16 @@ class NaviPane extends StatefulWidget { final GlobalKey navigatorKey; @override - State createState() => _NaviPaneState(); + State createState() => NaviPaneState(); + + static NaviPaneState of(BuildContext context) { + return context.findAncestorStateOfType()!; + } } -class _NaviPaneState extends State +typedef NaviItemTapListener = void Function(int); + +class NaviPaneState extends State with SingleTickerProviderStateMixin { late int _currentPage = widget.initialPage; @@ -66,6 +72,16 @@ class _NaviPaneState extends State late AnimationController controller; + final _naviItemTapListeners = []; + + void addNaviItemTapListener(NaviItemTapListener listener) { + _naviItemTapListeners.add(listener); + } + + void removeNaviItemTapListener(NaviItemTapListener listener) { + _naviItemTapListeners.remove(listener); + } + static const _kBottomBarHeight = 58.0; static const _kFoldedSideBarWidth = 80.0; @@ -85,9 +101,15 @@ class _NaviPaneState extends State } void updatePage(int index) { + for (var listener in _naviItemTapListeners) { + listener(index); + } if (widget.observer.routes.length > 1) { widget.navigatorKey.currentState!.popUntil((route) => route.isFirst); } + if (currentPage == index) { + return; + } setState(() { currentPage = index; }); @@ -670,14 +692,14 @@ class _NaviPopScope extends StatelessWidget { class _NaviMainView extends StatefulWidget { const _NaviMainView({required this.state}); - final _NaviPaneState state; + final NaviPaneState state; @override State<_NaviMainView> createState() => _NaviMainViewState(); } class _NaviMainViewState extends State<_NaviMainView> { - _NaviPaneState get state => widget.state; + NaviPaneState get state => widget.state; @override void initState() { diff --git a/lib/foundation/state_controller.dart b/lib/foundation/state_controller.dart index a4e0c9d..4e5fff3 100644 --- a/lib/foundation/state_controller.dart +++ b/lib/foundation/state_controller.dart @@ -1,14 +1,18 @@ import 'package:flutter/material.dart'; class SimpleController extends StateController { - final void Function()? refresh_; + final void Function()? refreshFunction; - SimpleController({this.refresh_}); + final Map Function()? control; + + SimpleController({this.refreshFunction, this.control}); @override void refresh() { - (refresh_ ?? super.refresh)(); + (refreshFunction ?? super.refresh)(); } + + Map get controlMap => control?.call() ?? {}; } abstract class StateController { @@ -71,8 +75,8 @@ abstract class StateController { static SimpleController putSimpleController( void Function() onUpdate, Object? tag, - {void Function()? refresh}) { - var controller = SimpleController(refresh_: refresh); + {void Function()? refresh, Map Function()? control}) { + var controller = SimpleController(refreshFunction: refresh, control: control); controller.stateUpdaters.add(Pair(null, onUpdate)); _controllers.add(StateControllerWrapped(controller, false, tag)); return controller; @@ -202,6 +206,7 @@ abstract class StateWithController extends State { }, tag, refresh: refresh, + control: () => control, ); super.initState(); } @@ -218,6 +223,8 @@ abstract class StateWithController extends State { } Object? get tag; + + Map get control => {}; } class Pair{ diff --git a/lib/pages/explore_page.dart b/lib/pages/explore_page.dart index 125bd7d..abd7618 100644 --- a/lib/pages/explore_page.dart +++ b/lib/pages/explore_page.dart @@ -46,6 +46,18 @@ class _ExplorePageState extends State } } + void onNaviItemTapped(int index) { + if (index == 2) { + int page = controller.index; + String currentPageId = pages[page]; + StateController.find(tag: currentPageId) + .control!()['toTop'] + ?.call(); + } + } + + NaviPaneState? naviPane; + @override void initState() { pages = List.from(appdata.settings["explore_pages"]); @@ -59,13 +71,21 @@ class _ExplorePageState extends State vsync: this, ); appdata.settings.addListener(onSettingsChanged); + NaviPane.of(context).addNaviItemTapListener(onNaviItemTapped); super.initState(); } + @override + void didChangeDependencies() { + naviPane = NaviPane.of(context); + super.didChangeDependencies(); + } + @override void dispose() { controller.dispose(); appdata.settings.removeListener(onSettingsChanged); + naviPane?.removeNaviItemTapListener(onNaviItemTapped); super.dispose(); } @@ -95,7 +115,7 @@ class _ExplorePageState extends State Widget buildEmpty() { var msg = "No Explore Pages".tl; msg += '\n'; - if(ComicSource.isEmpty) { + if (ComicSource.isEmpty) { msg += "Add a comic source in home page".tl; } else { msg += "Please check your settings".tl; @@ -232,6 +252,8 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage> bool _wantKeepAlive = true; + var scrollController = ScrollController(); + void onSettingsChanged() { var explorePages = appdata.settings["explore_pages"]; if (!explorePages.contains(widget.title)) { @@ -274,6 +296,7 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage> data, comicSourceKey, key: ValueKey(key), + controller: scrollController, ); } else { return const Center( @@ -287,6 +310,7 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage> loadPage: data.loadPage, loadNext: data.loadNext, key: ValueKey(key), + controller: scrollController, ); } @@ -323,6 +347,7 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage> Widget buildPage() { return SmoothCustomScrollView( + controller: scrollController, slivers: _buildPage().toList(), ); } @@ -352,15 +377,30 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage> @override bool get wantKeepAlive => _wantKeepAlive; + + void toTop() { + if (scrollController.hasClients) { + scrollController.animateTo( + scrollController.position.minScrollExtent, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + ); + } + } + + @override + Map get control => {"toTop": toTop}; } class _MixedExplorePage extends StatefulWidget { - const _MixedExplorePage(this.data, this.sourceKey, {super.key}); + const _MixedExplorePage(this.data, this.sourceKey, {super.key, this.controller}); final ExplorePageData data; final String sourceKey; + final ScrollController? controller; + @override State<_MixedExplorePage> createState() => _MixedExplorePageState(); } @@ -394,6 +434,7 @@ class _MixedExplorePageState @override Widget buildContent(BuildContext context, List data) { return SmoothCustomScrollView( + controller: widget.controller, slivers: [ ...buildSlivers(context, data), if (haveNextPage) const ListLoadingIndicator().toSliver()