mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
Use PageStorage to store state
This commit is contained in:
@@ -281,7 +281,9 @@ class _FilledTabBarState extends State<FilledTabBar> {
|
|||||||
|
|
||||||
_IndicatorPainter? painter;
|
_IndicatorPainter? painter;
|
||||||
|
|
||||||
var scrollController = ScrollController();
|
var scrollController = ScrollController(
|
||||||
|
keepScrollOffset: false
|
||||||
|
);
|
||||||
|
|
||||||
var tabBarKey = GlobalKey();
|
var tabBarKey = GlobalKey();
|
||||||
|
|
||||||
@@ -298,12 +300,20 @@ class _FilledTabBarState extends State<FilledTabBar> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PageStorageBucket get bucket => PageStorage.of(context);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
_controller = widget.controller ?? DefaultTabController.of(context);
|
_controller = widget.controller ?? DefaultTabController.of(context);
|
||||||
_controller.animation!.addListener(onTabChanged);
|
_controller.animation!.addListener(onTabChanged);
|
||||||
initPainter();
|
initPainter();
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
|
var prevIndex = bucket.readState(context) as int?;
|
||||||
|
if (prevIndex != null && prevIndex != _controller.index) {
|
||||||
|
Future.microtask(() {
|
||||||
|
_controller.index = prevIndex;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -387,6 +397,7 @@ class _FilledTabBarState extends State<FilledTabBar> {
|
|||||||
}
|
}
|
||||||
updateScrollOffset(i);
|
updateScrollOffset(i);
|
||||||
previousIndex = i;
|
previousIndex = i;
|
||||||
|
bucket.writeState(context, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateScrollOffset(int i) {
|
void updateScrollOffset(int i) {
|
||||||
|
@@ -865,6 +865,39 @@ class ComicListState extends State<ComicList> {
|
|||||||
|
|
||||||
String? _nextUrl;
|
String? _nextUrl;
|
||||||
|
|
||||||
|
Map<String, dynamic> get state => {
|
||||||
|
'maxPage': _maxPage,
|
||||||
|
'data': _data,
|
||||||
|
'page': _page,
|
||||||
|
'error': _error,
|
||||||
|
'loading': _loading,
|
||||||
|
'nextUrl': _nextUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
void restoreState(Map<String, dynamic>? state) {
|
||||||
|
if (state == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_maxPage = state['maxPage'];
|
||||||
|
_data.clear();
|
||||||
|
_data.addAll(state['data']);
|
||||||
|
_page = state['page'];
|
||||||
|
_error = state['error'];
|
||||||
|
_loading.clear();
|
||||||
|
_loading.addAll(state['loading']);
|
||||||
|
_nextUrl = state['nextUrl'];
|
||||||
|
}
|
||||||
|
|
||||||
|
void storeState() {
|
||||||
|
PageStorage.of(context).writeState(context, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
restoreState(PageStorage.of(context).readState(context));
|
||||||
|
}
|
||||||
|
|
||||||
void remove(Comic c) {
|
void remove(Comic c) {
|
||||||
if (_data[_page] == null || !_data[_page]!.remove(c)) {
|
if (_data[_page] == null || !_data[_page]!.remove(c)) {
|
||||||
for (var page in _data.values) {
|
for (var page in _data.values) {
|
||||||
@@ -1025,6 +1058,7 @@ class ComicListState extends State<ComicList> {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
_loading[page] = false;
|
_loading[page] = false;
|
||||||
|
storeState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1073,6 +1107,7 @@ class ComicListState extends State<ComicList> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return SmoothCustomScrollView(
|
return SmoothCustomScrollView(
|
||||||
|
key: const PageStorageKey('scroll'),
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
slivers: [
|
slivers: [
|
||||||
if (widget.leadingSliver != null) widget.leadingSliver!,
|
if (widget.leadingSliver != null) widget.leadingSliver!,
|
||||||
|
@@ -53,6 +53,7 @@ class CategoriesPage extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
FilledTabBar(
|
FilledTabBar(
|
||||||
|
key: PageStorageKey(categories.toString()),
|
||||||
tabs: categories.map((e) {
|
tabs: categories.map((e) {
|
||||||
String title = e;
|
String title = e;
|
||||||
try {
|
try {
|
||||||
|
@@ -110,7 +110,7 @@ class _ExplorePageState extends State<ExplorePage>
|
|||||||
return Tab(text: i.ts(comicSource.key), key: Key(i));
|
return Tab(text: i.ts(comicSource.key), key: Key(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildBody(String i) => _SingleExplorePage(i, key: Key(i));
|
Widget buildBody(String i) => _SingleExplorePage(i, key: PageStorageKey(i));
|
||||||
|
|
||||||
Widget buildEmpty() {
|
Widget buildEmpty() {
|
||||||
var msg = "No Explore Pages".tl;
|
var msg = "No Explore Pages".tl;
|
||||||
@@ -147,7 +147,7 @@ class _ExplorePageState extends State<ExplorePage>
|
|||||||
|
|
||||||
Widget tabBar = Material(
|
Widget tabBar = Material(
|
||||||
child: FilledTabBar(
|
child: FilledTabBar(
|
||||||
key: Key(pages.toString()),
|
key: PageStorageKey(pages.toString()),
|
||||||
tabs: pages.map((e) => buildTab(e)).toList(),
|
tabs: pages.map((e) => buildTab(e)).toList(),
|
||||||
controller: controller,
|
controller: controller,
|
||||||
),
|
),
|
||||||
@@ -240,12 +240,6 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage>
|
|||||||
with AutomaticKeepAliveClientMixin<_SingleExplorePage> {
|
with AutomaticKeepAliveClientMixin<_SingleExplorePage> {
|
||||||
late final ExplorePageData data;
|
late final ExplorePageData data;
|
||||||
|
|
||||||
bool loading = true;
|
|
||||||
|
|
||||||
String? message;
|
|
||||||
|
|
||||||
List<ExplorePagePart>? parts;
|
|
||||||
|
|
||||||
late final String comicSourceKey;
|
late final String comicSourceKey;
|
||||||
|
|
||||||
int key = 0;
|
int key = 0;
|
||||||
@@ -288,9 +282,19 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
if (data.loadMultiPart != null) {
|
if (data.loadMultiPart != null) {
|
||||||
return buildMultiPart();
|
return _MultiPartExplorePage(
|
||||||
|
key: PageStorageKey(key),
|
||||||
|
data: data,
|
||||||
|
controller: scrollController,
|
||||||
|
comicSourceKey: comicSourceKey,
|
||||||
|
);
|
||||||
} else if (data.loadPage != null || data.loadNext != null) {
|
} else if (data.loadPage != null || data.loadNext != null) {
|
||||||
return buildComicList();
|
return ComicList(
|
||||||
|
loadPage: data.loadPage,
|
||||||
|
loadNext: data.loadNext,
|
||||||
|
key: PageStorageKey(key),
|
||||||
|
controller: scrollController,
|
||||||
|
);
|
||||||
} else if (data.loadMixed != null) {
|
} else if (data.loadMixed != null) {
|
||||||
return _MixedExplorePage(
|
return _MixedExplorePage(
|
||||||
data,
|
data,
|
||||||
@@ -305,74 +309,14 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildComicList() {
|
|
||||||
return ComicList(
|
|
||||||
loadPage: data.loadPage,
|
|
||||||
loadNext: data.loadNext,
|
|
||||||
key: ValueKey(key),
|
|
||||||
controller: scrollController,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void load() async {
|
|
||||||
var res = await data.loadMultiPart!();
|
|
||||||
loading = false;
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
if (res.error) {
|
|
||||||
message = res.errorMessage;
|
|
||||||
} else {
|
|
||||||
parts = res.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildMultiPart() {
|
|
||||||
if (loading) {
|
|
||||||
load();
|
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
} else if (message != null) {
|
|
||||||
return NetworkError(
|
|
||||||
message: message!,
|
|
||||||
retry: refresh,
|
|
||||||
withAppbar: false,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return buildPage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildPage() {
|
|
||||||
return SmoothCustomScrollView(
|
|
||||||
controller: scrollController,
|
|
||||||
slivers: _buildPage().toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<Widget> _buildPage() sync* {
|
|
||||||
for (var part in parts!) {
|
|
||||||
yield* _buildExplorePagePart(part, comicSourceKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Object? get tag => widget.title;
|
Object? get tag => widget.title;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void refresh() {
|
void refresh() {
|
||||||
message = null;
|
setState(() {
|
||||||
if (data.loadMultiPart != null) {
|
key++;
|
||||||
setState(() {
|
});
|
||||||
loading = true;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
key++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -393,7 +337,8 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MixedExplorePage extends StatefulWidget {
|
class _MixedExplorePage extends StatefulWidget {
|
||||||
const _MixedExplorePage(this.data, this.sourceKey, {super.key, this.controller});
|
const _MixedExplorePage(this.data, this.sourceKey,
|
||||||
|
{super.key, this.controller});
|
||||||
|
|
||||||
final ExplorePageData data;
|
final ExplorePageData data;
|
||||||
|
|
||||||
@@ -518,3 +463,112 @@ Iterable<Widget> _buildExplorePagePart(
|
|||||||
yield buildTitle(part);
|
yield buildTitle(part);
|
||||||
yield buildComics(part);
|
yield buildComics(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _MultiPartExplorePage extends StatefulWidget {
|
||||||
|
const _MultiPartExplorePage({
|
||||||
|
super.key,
|
||||||
|
required this.data,
|
||||||
|
required this.controller,
|
||||||
|
required this.comicSourceKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ExplorePageData data;
|
||||||
|
|
||||||
|
final ScrollController controller;
|
||||||
|
|
||||||
|
final String comicSourceKey;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_MultiPartExplorePage> createState() => _MultiPartExplorePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MultiPartExplorePageState extends State<_MultiPartExplorePage> {
|
||||||
|
late final ExplorePageData data;
|
||||||
|
|
||||||
|
List<ExplorePagePart>? parts;
|
||||||
|
|
||||||
|
bool loading = true;
|
||||||
|
|
||||||
|
String? message;
|
||||||
|
|
||||||
|
Map<String, dynamic> get state => {
|
||||||
|
"loading": loading,
|
||||||
|
"message": message,
|
||||||
|
"parts": parts,
|
||||||
|
};
|
||||||
|
|
||||||
|
void restoreState(dynamic state) {
|
||||||
|
if (state == null) return;
|
||||||
|
loading = state["loading"];
|
||||||
|
message = state["message"];
|
||||||
|
parts = state["parts"];
|
||||||
|
}
|
||||||
|
|
||||||
|
void storeState() {
|
||||||
|
PageStorage.of(context).writeState(context, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
data = widget.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
restoreState(PageStorage.of(context).readState(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
void load() async {
|
||||||
|
var res = await data.loadMultiPart!();
|
||||||
|
loading = false;
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
if (res.error) {
|
||||||
|
message = res.errorMessage;
|
||||||
|
} else {
|
||||||
|
parts = res.data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
storeState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (loading) {
|
||||||
|
load();
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
} else if (message != null) {
|
||||||
|
return NetworkError(
|
||||||
|
message: message!,
|
||||||
|
retry: () {
|
||||||
|
setState(() {
|
||||||
|
loading = true;
|
||||||
|
message = null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
withAppbar: false,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return buildPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildPage() {
|
||||||
|
return SmoothCustomScrollView(
|
||||||
|
key: const PageStorageKey('scroll'),
|
||||||
|
controller: widget.controller,
|
||||||
|
slivers: _buildPage().toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterable<Widget> _buildPage() sync* {
|
||||||
|
for (var part in parts!) {
|
||||||
|
yield* _buildExplorePagePart(part, widget.comicSourceKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -62,10 +62,18 @@ class _MainPageState extends State<MainPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final _pages = [
|
final _pages = [
|
||||||
const HomePage(),
|
const HomePage(
|
||||||
const FavoritesPage(),
|
key: PageStorageKey('home'),
|
||||||
const ExplorePage(),
|
),
|
||||||
const CategoriesPage(),
|
const FavoritesPage(
|
||||||
|
key: PageStorageKey('favorites'),
|
||||||
|
),
|
||||||
|
const ExplorePage(
|
||||||
|
key: PageStorageKey('explore'),
|
||||||
|
),
|
||||||
|
const CategoriesPage(
|
||||||
|
key: PageStorageKey('categories'),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
Reference in New Issue
Block a user