2021-09-29 23:57:09 +00:00
|
|
|
import 'package:event/event.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_search_bar/flutter_search_bar.dart';
|
2021-11-11 03:00:38 +00:00
|
|
|
import 'package:pikapika/basic/Entities.dart';
|
2021-11-24 13:22:22 +00:00
|
|
|
import 'package:pikapika/basic/config/ShadowCategoriesEvent.dart';
|
|
|
|
import 'package:pikapika/basic/config/shadowCategoriesMode.dart';
|
2021-11-11 03:00:38 +00:00
|
|
|
import 'package:pikapika/basic/store/Categories.dart';
|
|
|
|
import 'package:pikapika/basic/config/ShadowCategories.dart';
|
|
|
|
import 'package:pikapika/screens/RankingsScreen.dart';
|
|
|
|
import 'package:pikapika/screens/SearchScreen.dart';
|
|
|
|
import 'package:pikapika/screens/components/ContentError.dart';
|
|
|
|
import 'package:pikapika/basic/Method.dart';
|
2021-09-29 23:57:09 +00:00
|
|
|
import 'ComicsScreen.dart';
|
|
|
|
import 'GamesScreen.dart';
|
|
|
|
import 'RandomComicsScreen.dart';
|
|
|
|
import 'components/ContentLoading.dart';
|
|
|
|
import 'components/Images.dart';
|
|
|
|
|
|
|
|
// 分类
|
|
|
|
class CategoriesScreen extends StatefulWidget {
|
2022-03-17 03:31:25 +00:00
|
|
|
const CategoriesScreen({Key? key}) : super(key: key);
|
2021-09-29 23:57:09 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
State<StatefulWidget> createState() => _CategoriesScreenState();
|
|
|
|
}
|
|
|
|
|
2021-11-29 03:26:30 +00:00
|
|
|
class _CategoriesScreenState extends State<CategoriesScreen> {
|
2022-03-19 04:12:27 +00:00
|
|
|
late final SearchBar _searchBar = SearchBar(
|
2021-09-29 23:57:09 +00:00
|
|
|
hintText: '搜索',
|
|
|
|
inBar: false,
|
|
|
|
setState: setState,
|
|
|
|
onSubmitted: (value) {
|
|
|
|
if (value.isNotEmpty) {
|
|
|
|
Navigator.push(
|
|
|
|
context,
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (context) => SearchScreen(keyword: value),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
buildDefaultAppBar: (BuildContext context) {
|
|
|
|
return AppBar(
|
2022-03-19 04:12:27 +00:00
|
|
|
title: const Text('分类'),
|
2021-09-29 23:57:09 +00:00
|
|
|
actions: [
|
|
|
|
shadowCategoriesActionButton(context),
|
|
|
|
_searchBar.getSearchAction(context),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
late Future<List<Category>> _categoriesFuture = _fetch();
|
|
|
|
|
|
|
|
Future<List<Category>> _fetch() async {
|
|
|
|
List<Category> categories = await method.categories();
|
|
|
|
storedCategories = [];
|
2022-03-17 03:31:25 +00:00
|
|
|
for (var element in categories) {
|
2021-09-29 23:57:09 +00:00
|
|
|
if (!element.isWeb) {
|
|
|
|
storedCategories.add(element.title);
|
|
|
|
}
|
2022-03-17 03:31:25 +00:00
|
|
|
}
|
2021-09-29 23:57:09 +00:00
|
|
|
return categories;
|
|
|
|
}
|
|
|
|
|
|
|
|
void _reloadCategories() {
|
|
|
|
setState(() {
|
|
|
|
this._categoriesFuture = _fetch();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
shadowCategoriesEvent.subscribe(_onShadowChange);
|
|
|
|
super.initState();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
shadowCategoriesEvent.unsubscribe(_onShadowChange);
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
void _onShadowChange(EventArgs? args) {
|
|
|
|
_reloadCategories();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
var theme = Theme.of(context);
|
|
|
|
var themeBackground = theme.scaffoldBackgroundColor;
|
|
|
|
var shadeBackground = Color.fromARGB(
|
|
|
|
0x11,
|
|
|
|
255 - themeBackground.red,
|
|
|
|
255 - themeBackground.green,
|
|
|
|
255 - themeBackground.blue,
|
|
|
|
);
|
|
|
|
return Scaffold(
|
|
|
|
appBar: _searchBar.build(context),
|
|
|
|
body: Container(
|
|
|
|
color: shadeBackground,
|
|
|
|
child: FutureBuilder(
|
|
|
|
future: _categoriesFuture,
|
|
|
|
builder:
|
|
|
|
((BuildContext context, AsyncSnapshot<List<Category>> snapshot) {
|
|
|
|
if (snapshot.hasError) {
|
|
|
|
return ContentError(
|
|
|
|
error: snapshot.error,
|
|
|
|
stackTrace: snapshot.stackTrace,
|
|
|
|
onRefresh: () async {
|
|
|
|
_reloadCategories();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (snapshot.connectionState != ConnectionState.done) {
|
2022-03-19 04:12:27 +00:00
|
|
|
return const ContentLoading(label: '加载中');
|
2021-09-29 23:57:09 +00:00
|
|
|
}
|
|
|
|
return ListView(
|
|
|
|
children: [
|
|
|
|
Container(height: 20),
|
|
|
|
Wrap(
|
|
|
|
runSpacing: 20,
|
|
|
|
alignment: WrapAlignment.spaceAround,
|
|
|
|
children: _buildChannels(),
|
|
|
|
),
|
2022-03-19 04:12:27 +00:00
|
|
|
const Divider(),
|
2021-09-29 23:57:09 +00:00
|
|
|
Wrap(
|
|
|
|
runSpacing: 20,
|
|
|
|
alignment: WrapAlignment.spaceAround,
|
|
|
|
children: _buildCategories(snapshot.data!),
|
|
|
|
),
|
|
|
|
Container(height: 20),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
List<Widget> _buildCategories(List<Category> cList) {
|
|
|
|
var size = MediaQuery.of(context).size;
|
|
|
|
var min = size.width < size.height ? size.width : size.height;
|
|
|
|
var blockSize = min / 3;
|
|
|
|
var imageSize = blockSize - 15;
|
|
|
|
var imageRs = imageSize / 10;
|
|
|
|
|
|
|
|
List<Widget> list = [];
|
|
|
|
|
2022-03-25 14:57:30 +00:00
|
|
|
append(Widget widget, String title, Function() onTap) {
|
2021-09-29 23:57:09 +00:00
|
|
|
list.add(
|
|
|
|
GestureDetector(
|
|
|
|
onTap: onTap,
|
2022-03-17 03:31:25 +00:00
|
|
|
child: SizedBox(
|
2021-09-29 23:57:09 +00:00
|
|
|
width: blockSize,
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
Card(
|
|
|
|
elevation: .5,
|
|
|
|
child: ClipRRect(
|
|
|
|
borderRadius: BorderRadius.all(Radius.circular(imageRs)),
|
|
|
|
child: widget,
|
|
|
|
),
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
borderRadius: BorderRadius.all(Radius.circular(imageRs)),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Container(height: 5),
|
|
|
|
Center(
|
|
|
|
child: Text(title),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
2022-03-25 14:57:30 +00:00
|
|
|
}
|
2021-09-29 23:57:09 +00:00
|
|
|
|
|
|
|
append(
|
|
|
|
buildSvg('lib/assets/books.svg', imageSize, imageSize, margin: 20),
|
|
|
|
"全分类",
|
|
|
|
() => _navigateToCategory(null),
|
|
|
|
);
|
|
|
|
|
|
|
|
for (var i = 0; i < cList.length; i++) {
|
|
|
|
var c = cList[i];
|
|
|
|
if (c.isWeb) continue;
|
2021-11-24 13:22:22 +00:00
|
|
|
switch (currentShadowCategoriesMode()) {
|
|
|
|
case ShadowCategoriesMode.BLACK_LIST:
|
|
|
|
if (shadowCategories.contains(c.title)) continue;
|
|
|
|
break;
|
|
|
|
case ShadowCategoriesMode.WHITE_LIST:
|
|
|
|
if (!shadowCategories.contains(c.title)) continue;
|
|
|
|
break;
|
|
|
|
}
|
2021-09-29 23:57:09 +00:00
|
|
|
append(
|
|
|
|
RemoteImage(
|
|
|
|
fileServer: c.thumb.fileServer,
|
|
|
|
path: c.thumb.path,
|
|
|
|
width: imageSize,
|
|
|
|
height: imageSize,
|
|
|
|
),
|
|
|
|
c.title,
|
|
|
|
() => _navigateToCategory(c.title),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<Widget> _buildChannels() {
|
|
|
|
var size = MediaQuery.of(context).size;
|
|
|
|
var min = size.width < size.height ? size.width : size.height;
|
|
|
|
var blockSize = min / 3;
|
|
|
|
var imageSize = blockSize - 15;
|
|
|
|
var imageRs = imageSize / 10;
|
|
|
|
|
|
|
|
List<Widget> list = [];
|
|
|
|
|
2022-03-25 14:57:30 +00:00
|
|
|
append(Widget widget, String title, Function() onTap) {
|
2021-09-29 23:57:09 +00:00
|
|
|
list.add(
|
|
|
|
GestureDetector(
|
|
|
|
onTap: onTap,
|
2022-03-17 03:31:25 +00:00
|
|
|
child: SizedBox(
|
2021-09-29 23:57:09 +00:00
|
|
|
width: blockSize,
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
Card(
|
|
|
|
elevation: .5,
|
|
|
|
child: ClipRRect(
|
|
|
|
borderRadius: BorderRadius.all(Radius.circular(imageRs)),
|
|
|
|
child: widget,
|
|
|
|
),
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
borderRadius: BorderRadius.all(Radius.circular(imageRs)),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Container(height: 5),
|
|
|
|
Center(
|
|
|
|
child: Text(title),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
2022-03-25 14:57:30 +00:00
|
|
|
}
|
2021-09-29 23:57:09 +00:00
|
|
|
|
|
|
|
append(
|
|
|
|
buildSvg('lib/assets/rankings.svg', imageSize, imageSize,
|
|
|
|
margin: 20, color: Colors.red.shade700),
|
|
|
|
"排行榜",
|
|
|
|
() {
|
|
|
|
Navigator.push(
|
|
|
|
context,
|
2022-03-19 04:12:27 +00:00
|
|
|
MaterialPageRoute(builder: (context) => const RankingsScreen()),
|
2021-09-29 23:57:09 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
append(
|
|
|
|
buildSvg('lib/assets/random.svg', imageSize, imageSize,
|
|
|
|
margin: 20, color: Colors.orangeAccent.shade700),
|
|
|
|
"随机本子",
|
|
|
|
() {
|
|
|
|
Navigator.push(
|
|
|
|
context,
|
2022-03-19 04:12:27 +00:00
|
|
|
MaterialPageRoute(builder: (context) => const RandomComicsScreen()),
|
2021-09-29 23:57:09 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
append(
|
|
|
|
buildSvg('lib/assets/gamepad.svg', imageSize, imageSize,
|
|
|
|
margin: 20, color: Colors.blue.shade500),
|
|
|
|
"游戏专区",
|
|
|
|
() {
|
|
|
|
Navigator.push(
|
|
|
|
context,
|
2022-03-19 04:12:27 +00:00
|
|
|
MaterialPageRoute(builder: (context) => const GamesScreen()),
|
2021-09-29 23:57:09 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
void _navigateToCategory(String? categoryTitle) {
|
|
|
|
Navigator.push(
|
|
|
|
context,
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (context) => ComicsScreen(category: categoryTitle),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|