This commit is contained in:
2026-03-07 17:24:59 +08:00
parent 4418ebecac
commit b0ec8ab4bd
417 changed files with 42546 additions and 2 deletions

View File

@@ -0,0 +1,23 @@
import 'package:flutter_dmzj/app/controller/base_controller.dart';
import 'package:flutter_dmzj/models/comic/category_item_model.dart';
import 'package:flutter_dmzj/requests/comic_request.dart';
import 'package:flutter_dmzj/routes/app_navigator.dart';
class ComicCategoryController
extends BasePageController<ComicCategoryItemModel> {
final ComicRequest request = ComicRequest();
@override
Future<List<ComicCategoryItemModel>> getData(int page, int pageSize) async {
if (page > 1) {
return [];
}
var ls = await request.categores();
return ls;
}
void toDetail(ComicCategoryItemModel item) {
AppNavigator.toComicCategoryDetail(item.tagId);
}
}

View File

@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import 'package:flutter_dmzj/app/app_style.dart';
import 'package:flutter_dmzj/modules/comic/home/category/comic_category_controller.dart';
import 'package:flutter_dmzj/widgets/keep_alive_wrapper.dart';
import 'package:flutter_dmzj/widgets/net_image.dart';
import 'package:flutter_dmzj/widgets/page_grid_view.dart';
import 'package:flutter_dmzj/widgets/shadow_card.dart';
import 'package:get/get.dart';
class ComicCategoryView extends StatelessWidget {
final ComicCategoryController controller;
ComicCategoryView({Key? key})
: controller = Get.put(ComicCategoryController()),
super(key: key);
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
var count = constraints.maxWidth ~/ 160;
if (count < 3) count = 3;
return KeepAliveWrapper(
child: PageGridView(
pageController: controller,
firstRefresh: true,
loadMore: false,
crossAxisCount: count,
padding: AppStyle.edgeInsetsH12.copyWith(bottom: 12),
mainAxisSpacing: 12,
crossAxisSpacing: 12,
itemBuilder: (context, i) {
var item = controller.list[i];
return ShadowCard(
onTap: () {
controller.toDetail(item);
},
child: Column(
children: [
AspectRatio(
aspectRatio: 1.0,
child: NetImage(
item.cover,
borderRadius: 8,
),
),
Padding(
padding: AppStyle.edgeInsetsA8,
child: Text(
item.title,
textAlign: TextAlign.center,
style: const TextStyle(height: 1),
),
),
],
),
);
},
),
);
});
}
}

View File

@@ -0,0 +1,57 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_dmzj/app/controller/base_controller.dart';
import 'package:flutter_dmzj/app/event_bus.dart';
import 'package:flutter_dmzj/modules/comic/home/category/comic_category_controller.dart';
//import 'package:flutter_dmzj/modules/comic/home/category/comic_category_controller.dart';
import 'package:flutter_dmzj/modules/comic/home/latest/comic_latest_controller.dart';
import 'package:flutter_dmzj/modules/comic/home/rank/comic_rank_controller.dart';
import 'package:flutter_dmzj/modules/comic/home/recommend/comic_recommend_controller.dart';
//import 'package:flutter_dmzj/modules/comic/home/special/comic_special_controller.dart';
import 'package:flutter_dmzj/routes/app_navigator.dart';
import 'package:get/get.dart';
class ComicHomeController extends GetxController
with GetTickerProviderStateMixin {
late TabController tabController;
StreamSubscription<dynamic>? streamSubscription;
@override
void onInit() {
streamSubscription = EventBus.instance.listen(
EventBus.kBottomNavigationBarClicked,
(index) {
if (index == 0) {
refreshOrScrollTop();
}
},
);
tabController = TabController(length: 4, vsync: this);
super.onInit();
}
void refreshOrScrollTop() {
var tabIndex = tabController.index;
BasePageController? controller;
if (tabIndex == 0) {
controller = Get.find<ComicRecommendController>();
} else if (tabIndex == 1) {
controller = Get.find<ComicLatestController>();
} else if (tabIndex == 2) {
controller = Get.find<ComicCategoryController>();
} else if (tabIndex == 3) {
controller = Get.find<ComicRankController>();
}
// else if (tabIndex == 4) {
// controller = Get.find<ComicSpecialController>();
// }
controller?.scrollToTopOrRefresh();
}
void search() {
AppNavigator.toComicSearch();
}
}

View File

@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:flutter_dmzj/app/platform_utils.dart';
import 'package:flutter_dmzj/modules/comic/home/category/comic_category_view.dart';
import 'package:flutter_dmzj/modules/comic/home/comic_home_controller.dart';
import 'package:flutter_dmzj/modules/comic/home/latest/comic_latest_view.dart';
import 'package:flutter_dmzj/modules/comic/home/rank/comic_rank_view.dart';
import 'package:flutter_dmzj/modules/comic/home/recommend/comic_recommend_view.dart';
//import 'package:flutter_dmzj/modules/comic/home/special/comic_special_view.dart';
import 'package:flutter_dmzj/widgets/tab_appbar.dart';
import 'package:flutter_dmzj/widgets/windows_tab_page.dart';
import 'package:get/get.dart';
class ComicHomePage extends GetView<ComicHomeController> {
const ComicHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
if (PlatformUtils.isWindows) {
return WindowsTabPage(
tabs: [
WindowsTabItem(label: '推荐', body: ComicRecommendView()),
WindowsTabItem(label: '更新', body: ComicLatestView()),
WindowsTabItem(label: '分类', body: ComicCategoryView()),
WindowsTabItem(label: '排行', body: ComicRankView()),
],
headerAction: IconButton(
onPressed: controller.search,
icon: const Icon(Icons.search),
),
);
}
return Scaffold(
appBar: TabAppBar(
tabs: const [
Tab(text: "推荐"),
Tab(text: "更新"),
Tab(text: "分类"),
Tab(text: "排行"),
// Tab(text: "专题"),
],
controller: controller.tabController,
action: IconButton(
onPressed: controller.search,
icon: const Icon(
Icons.search,
),
),
),
body: TabBarView(
controller: controller.tabController,
children: [
ComicRecommendView(),
ComicLatestView(),
ComicCategoryView(),
ComicRankView(),
//ComicSpecialView(),
],
),
);
}
}

View File

@@ -0,0 +1,21 @@
import 'package:flutter_dmzj/app/controller/base_controller.dart';
import 'package:flutter_dmzj/models/comic/update_item_model.dart';
import 'package:flutter_dmzj/requests/comic_request.dart';
import 'package:get/get.dart';
class ComicLatestController extends BasePageController<ComicUpdateItemModel> {
final ComicRequest request = ComicRequest();
Map types = {
"全部漫画": 100,
"原创漫画": 1,
"译制漫画": 0,
};
var type = 100.obs;
@override
Future<List<ComicUpdateItemModel>> getData(int page, int pageSize) async {
var ls = await request.latest(type: type.value, page: page);
return ls;
}
}

View File

@@ -0,0 +1,176 @@
import 'package:flutter/material.dart';
import 'package:flutter_dmzj/app/app_constant.dart';
import 'package:flutter_dmzj/app/app_style.dart';
import 'package:flutter_dmzj/app/utils.dart';
import 'package:flutter_dmzj/models/comic/update_item_model.dart';
import 'package:flutter_dmzj/modules/comic/home/latest/comic_latest_controller.dart';
import 'package:flutter_dmzj/routes/app_navigator.dart';
import 'package:flutter_dmzj/services/user_service.dart';
import 'package:flutter_dmzj/widgets/keep_alive_wrapper.dart';
import 'package:flutter_dmzj/widgets/net_image.dart';
import 'package:flutter_dmzj/widgets/page_list_view.dart';
import 'package:get/get.dart';
class ComicLatestView extends StatelessWidget {
final ComicLatestController controller;
ComicLatestView({Key? key})
: controller = Get.put(ComicLatestController()),
super(key: key);
@override
Widget build(BuildContext context) {
return KeepAliveWrapper(
child: Column(
children: [
Row(
children: [
AppStyle.hGap12,
...controller.types.keys.map(
(e) => buildFilterButton(
title: e,
value: controller.types[e],
),
),
],
),
AppStyle.vGap12,
Expanded(
child: PageListView(
pageController: controller,
firstRefresh: true,
showPageLoadding: false,
separatorBuilder: (context, i) => Divider(
endIndent: 12,
indent: 12,
color: Colors.grey.withOpacity(.2),
height: 1,
),
itemBuilder: (context, i) {
var item = controller.list[i];
return buildItem(item);
},
),
),
],
),
);
}
Widget buildItem(ComicUpdateItemModel item) {
return InkWell(
onTap: () {
AppNavigator.toComicDetail(item.comicId.toInt());
},
child: Container(
padding: AppStyle.edgeInsetsA12,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
NetImage(
item.cover ?? '',
width: 80,
height: 110,
borderRadius: 4,
),
AppStyle.hGap12,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text.rich(
TextSpan(children: [
const WidgetSpan(
child: Icon(
Icons.account_circle,
color: Colors.grey,
size: 18,
)),
const TextSpan(
text: " ",
),
TextSpan(
text: item.authors,
style:
const TextStyle(color: Colors.grey, fontSize: 14))
]),
),
const SizedBox(height: 2),
Text(item.types ?? '',
style: const TextStyle(color: Colors.grey, fontSize: 14)),
const SizedBox(height: 2),
Text(item.lastUpdateChapterName ?? '',
style: const TextStyle(color: Colors.grey, fontSize: 14)),
const SizedBox(height: 2),
Text("更新于${Utils.formatTimestamp(item.lastUpdatetime ?? 0)}",
style: const TextStyle(color: Colors.grey, fontSize: 14)),
],
),
),
Center(
child: Obx(
() => UserService.instance.subscribedComicIds
.contains(item.comicId.toInt())
? IconButton(
icon: const Icon(Icons.favorite),
onPressed: () {
UserService.instance.cancelSubscribe(
[item.comicId.toInt()],
AppConstant.kTypeComic,
);
},
)
: IconButton(
icon: const Icon(Icons.favorite_border),
onPressed: () {
UserService.instance.addSubscribe(
[item.comicId.toInt()],
AppConstant.kTypeComic,
);
},
),
),
)
],
),
),
);
}
Widget buildFilterButton({required String title, required int value}) {
return Container(
height: 32,
margin: AppStyle.edgeInsetsR8,
child: Obx(
() => TextButton(
style: TextButton.styleFrom(
foregroundColor:
controller.type.value == value ? Get.theme.colorScheme.primary : Colors.grey,
shape: RoundedRectangleBorder(
borderRadius: AppStyle.radius24,
side: BorderSide(
color:
controller.type.value == value ? Get.theme.colorScheme.primary : Colors.grey,
),
),
textStyle: const TextStyle(fontSize: 14),
padding: AppStyle.edgeInsetsH16,
),
onPressed: () {
if (controller.type.value == value) {
return;
}
controller.type.value = value;
controller.refreshData();
},
child: Text(title),
),
),
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter_dmzj/app/controller/base_controller.dart';
import 'package:flutter_dmzj/models/comic/rank_item_model.dart';
import 'package:flutter_dmzj/requests/comic_request.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
class ComicRankController extends BasePageController<ComicRankListItemModel> {
final ComicRequest request = ComicRequest();
RxMap<int, String> tags = {
0: "全部分类",
}.obs;
var tag = 0.obs;
Map<int, String> byTimes = {
0: "日排行",
1: "周排行",
2: "月排行",
3: "总排行",
};
var byTime = 3.obs;
Map<int, String> rankTypes = {
0: "人气排行",
1: "吐槽排行",
2: "订阅排行",
};
var rankType = 0.obs;
@override
void onInit() {
loadFilter();
super.onInit();
}
void loadFilter() async {
try {
tags.value = await request.rankFilter();
} catch (e) {
SmartDialog.showToast(e.toString());
}
}
@override
Future<List<ComicRankListItemModel>> getData(int page, int pageSize) async {
var ls = await request.rank(
tagId: tag.value,
byTime: byTime.value,
rankType: rankType.value,
page: page,
);
return ls;
}
}

View File

@@ -0,0 +1,200 @@
import 'package:flutter/material.dart';
import 'package:flutter_dmzj/app/app_constant.dart';
import 'package:flutter_dmzj/app/app_style.dart';
import 'package:flutter_dmzj/app/utils.dart';
import 'package:flutter_dmzj/models/comic/rank_item_model.dart';
import 'package:flutter_dmzj/modules/comic/home/rank/comic_rank_controller.dart';
import 'package:flutter_dmzj/routes/app_navigator.dart';
import 'package:flutter_dmzj/services/user_service.dart';
import 'package:flutter_dmzj/widgets/keep_alive_wrapper.dart';
import 'package:flutter_dmzj/widgets/net_image.dart';
import 'package:flutter_dmzj/widgets/page_list_view.dart';
import 'package:get/get.dart';
class ComicRankView extends StatelessWidget {
final ComicRankController controller;
ComicRankView({Key? key})
: controller = Get.put(ComicRankController()),
super(key: key);
@override
Widget build(BuildContext context) {
return KeepAliveWrapper(
child: Column(
children: [
Obx(
() => Row(
children: [
buildFilter(
// ignore: invalid_use_of_protected_member
types: controller.tags.value,
value: controller.tag.value,
onSelected: (e) {
controller.tag.value = e;
controller.refreshData();
},
),
buildFilter(
types: controller.byTimes,
value: controller.byTime.value,
onSelected: (e) {
controller.byTime.value = e;
controller.refreshData();
},
),
buildFilter(
types: controller.rankTypes,
value: controller.rankType.value,
onSelected: (e) {
controller.rankType.value = e;
controller.refreshData();
},
),
],
),
),
AppStyle.vGap12,
Expanded(
child: PageListView(
pageController: controller,
firstRefresh: true,
showPageLoadding: false,
separatorBuilder: (context, i) => Divider(
endIndent: 12,
indent: 12,
color: Colors.grey.withOpacity(.2),
height: 1,
),
itemBuilder: (context, i) {
var item = controller.list[i];
return buildItem(item);
},
),
),
],
),
);
}
Widget buildFilter({
required Map<int, String> types,
required int value,
required Function(int) onSelected,
}) {
return Expanded(
child: PopupMenuButton<int>(
onSelected: onSelected,
itemBuilder: (c) => types.keys
.map(
(k) => CheckedPopupMenuItem<int>(
value: k,
checked: k == value,
child: Text(types[k] ?? ""),
),
)
.toList(),
child: SizedBox(
height: 36,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
types[value] ?? "",
),
const Icon(
Icons.arrow_drop_down,
color: Colors.grey,
)
],
),
),
),
);
}
Widget buildItem(ComicRankListItemModel item) {
return InkWell(
onTap: () {
AppNavigator.toComicDetail(item.comicId.toInt());
},
child: Container(
padding: AppStyle.edgeInsetsA12,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
NetImage(
item.cover ?? '',
width: 80,
height: 110,
borderRadius: 4,
),
AppStyle.hGap12,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text.rich(
TextSpan(children: [
const WidgetSpan(
child: Icon(
Icons.account_circle,
color: Colors.grey,
size: 18,
)),
const TextSpan(
text: " ",
),
TextSpan(
text: item.authors,
style:
const TextStyle(color: Colors.grey, fontSize: 14))
]),
),
const SizedBox(height: 2),
Text(item.types ?? '-',
style: const TextStyle(color: Colors.grey, fontSize: 14)),
const SizedBox(height: 2),
Text(item.lastUpdateChapterName ?? '-',
style: const TextStyle(color: Colors.grey, fontSize: 14)),
const SizedBox(height: 2),
Text("更新于${Utils.formatTimestamp(item.lastUpdatetime ?? 0)}",
style: const TextStyle(color: Colors.grey, fontSize: 14)),
],
),
),
Center(
child: Obx(
() => UserService.instance.subscribedComicIds
.contains(item.comicId.toInt())
? IconButton(
icon: const Icon(Icons.favorite),
onPressed: () {
UserService.instance.cancelSubscribe(
[item.comicId.toInt()],
AppConstant.kTypeComic,
);
},
)
: IconButton(
icon: const Icon(Icons.favorite_border),
onPressed: () {
UserService.instance.addSubscribe(
[item.comicId.toInt()],
AppConstant.kTypeComic,
);
},
),
),
)
],
),
),
);
}
}

View File

@@ -0,0 +1,188 @@
import 'dart:async';
import 'package:flutter_dmzj/app/controller/base_controller.dart';
import 'package:flutter_dmzj/app/log.dart';
import 'package:flutter_dmzj/models/comic/recommend_model.dart';
import 'package:flutter_dmzj/modules/comic/home/comic_home_controller.dart';
import 'package:flutter_dmzj/requests/comic_request.dart';
import 'package:flutter_dmzj/routes/app_navigator.dart';
import 'package:flutter_dmzj/services/user_service.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:url_launcher/url_launcher_string.dart';
class ComicRecommendController extends BasePageController<ComicRecommendModel> {
final ComicRequest request = ComicRequest();
StreamSubscription<dynamic>? subLogin;
StreamSubscription<dynamic>? subLogout;
@override
void onInit() {
subLogin = UserService.loginedStream.listen((event) {
loadSubscribe();
});
subLogout = UserService.logoutStream.listen((event) {
list.removeWhere((x) => x.categoryId == 49);
});
super.onInit();
}
@override
Future<List<ComicRecommendModel>> getData(int page, int pageSize) async {
var ls = await request.recommend();
// ls.insert(
// ls.length > 3 ? 2 : 1,
// ComicRecommendModel(
// categoryId: 50,
// title: "随便看看",
// sort: 6,
// data: [],
// ),
// );
//loadRandom();
if (UserService.instance.logined.value) {
loadSubscribe();
}
return ls;
}
// /// 加载随机漫画
// Future<void> loadRandom() async {
// try {
// var result = await request.refreshRecommend(50);
// var index = list.indexWhere((x) => x.categoryId == 50);
// if (index != -1) {
// list[index].data = result;
// } else {
// list.insert(
// list.length > 3 ? 2 : 1,
// result,
// );
// }
// } catch (e) {
// Log.logPrint(e);
// }
// }
/// 刷新国漫
Future<void> refreshGuoman() async {
try {
var index = list.indexWhere((x) => x.categoryId == 111);
var result =
await request.refreshRecommend(111, size: 6, page: list[index].page);
if (index != -1) {
list[index].data = result;
list[index].page++;
list.refresh();
}
} catch (e) {
Log.logPrint(e);
}
}
/// 刷新近期必看
Future<void> refreshRecommend() async {
try {
var index = list.indexWhere((x) => x.categoryId == 110);
var result = await request.refreshRecommend(110, page: list[index].page);
if (index != -1) {
list[index].data = result;
list[index].page++;
list.refresh();
}
} catch (e) {
Log.logPrint(e);
}
}
/// 加载订阅
void loadSubscribe() async {
try {
var result = await request.recommendSubscribe();
var index = list.indexWhere((x) => x.categoryId == 49);
if (index != -1) {
list[index] = result;
} else {
list.insert(1, result);
}
} catch (e) {
Log.logPrint(e);
}
}
/// 刷新热门连载
Future<void> refreshHot() async {
try {
var index = list.indexWhere((x) => x.categoryId == 112);
var result =
await request.refreshRecommend(112, page: list[index].page, size: 6);
if (index != -1) {
list[index].data = result;
list[index].page++;
list.refresh();
}
} catch (e) {
Log.logPrint(e);
}
}
void openDetail(ComicRecommendItemModel item) {
//漫画=1
if (item.type == null || item.type == 1) {
AppNavigator.toComicDetail(
item.objId ?? item.id ?? 0,
);
} else if (item.type == 5) {
//专题=5
AppNavigator.toSpecialDetail(
item.objId ?? 0,
);
} else if (item.type == 6) {
//网页=6
AppNavigator.toWebView(item.url ?? "");
} else if (item.type == 7) {
//新闻=7
AppNavigator.toNewsDetail(
url: item.url ?? "",
newsId: item.objId ?? 0,
title: item.title,
);
} else if (item.type == 8) {
//作者=8
AppNavigator.toComicAuthorDetail(item.objId ?? 0);
} else if (item.type == 13) {
//社区=13
//直接跳转至网页
launchUrlString(
"http://m.forum.idmzj.com/thread/detail?tid=${item.objId}",
mode: LaunchMode.externalApplication,
);
// AppNavigator.toWebView(
// "http://m.forum.dmzj.com/thread/detail?tid=${item.objId}",
// );
} else {
SmartDialog.showToast("未知类型,无法跳转");
}
}
void toSpecial() {
var homeController = Get.find<ComicHomeController>();
homeController.tabController.animateTo(4);
}
void toMySubscribe() {
AppNavigator.toUserSubscribe();
}
@override
void onClose() {
subLogin?.cancel();
subLogout?.cancel();
super.onClose();
}
}

View File

@@ -0,0 +1,372 @@
import 'package:flutter/material.dart';
import 'package:flutter_dmzj/app/app_style.dart';
import 'package:flutter_dmzj/models/comic/recommend_model.dart';
import 'package:flutter_dmzj/modules/comic/home/recommend/comic_recommend_controller.dart';
import 'package:flutter_dmzj/widgets/keep_alive_wrapper.dart';
import 'package:flutter_dmzj/widgets/net_image.dart';
import 'package:flutter_dmzj/widgets/page_list_view.dart';
import 'package:flutter_dmzj/widgets/refresh_until_widget.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:flutter_swiper_view/flutter_swiper_view.dart';
import 'package:get/get.dart';
class ComicRecommendView extends StatelessWidget {
final ComicRecommendController controller;
ComicRecommendView({Key? key})
: controller = Get.put(ComicRecommendController()),
super(key: key);
@override
Widget build(BuildContext context) {
return KeepAliveWrapper(
child: PageListView(
pageController: controller,
padding: AppStyle.edgeInsetsH12,
firstRefresh: true,
loadMore: false,
showPageLoadding: true,
itemBuilder: (context, i) {
var item = controller.list[i];
//大图推荐
if (item.categoryId == 109) {
return buildBanner(item);
}
//随便看看
// if (item.categoryId == 50) {
// return buildCard(
// context,
// child: buildTreeColumnGridView(item.data),
// title: item.title.toString(),
// action: buildRefresh(onRefresh: controller.loadRandom),
// );
// }
//我的订阅
if (item.categoryId == 49) {
return buildCard(
context,
child: buildTreeColumnGridView(item.data),
title: item.title.toString(),
action: buildShowMore(onTap: controller.toMySubscribe),
);
}
//近期必看\国漫\热门连载\最新上架
if (item.categoryId == 110 ||
item.categoryId == 111 ||
item.categoryId == 112 ||
item.categoryId == 56) {
Widget? action;
//刷新国漫
if (item.categoryId == 110) {
action = buildRefresh(onRefresh: controller.refreshRecommend);
}
if (item.categoryId == 111) {
action = buildRefresh(onRefresh: controller.refreshGuoman);
}
if (item.categoryId == 112) {
action = buildRefresh(onRefresh: controller.refreshHot);
}
return buildCard(
context,
child: buildTreeColumnGridView(item.data),
title: item.title.toString(),
action: action,
);
}
//火热专题\美漫大事件\条漫
if (item.categoryId == 48 ||
item.categoryId == 53 ||
item.categoryId == 55) {
return buildCard(
context,
child: buildTwoColumnGridView(item.data),
title: item.title.toString(),
action: item.categoryId == 48
? buildShowMore(onTap: controller.toSpecial)
: null,
);
}
//大师
if (item.categoryId == 51) {
return buildCard(
context,
child: buildAuthorGridView(item.data),
title: item.title.toString(),
);
}
return buildCard(
context,
child: buildTreeColumnGridView(item.data),
title: item.title.toString(),
);
},
),
);
}
Widget buildCard(
BuildContext context, {
required Widget child,
required String title,
Widget? action,
}) {
return Padding(
padding: AppStyle.edgeInsetsB8,
child: Container(
decoration: BoxDecoration(
borderRadius: AppStyle.radius8,
),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 16, height: 1.0, fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 48,
child: action,
),
],
),
child,
],
),
),
);
}
Widget buildShowMore({required Function() onTap}) {
return GestureDetector(
onTap: onTap,
child: const Row(
children: [
Text(
"查看更多",
style: TextStyle(fontSize: 14, color: Colors.grey),
),
Icon(Icons.chevron_right, size: 18, color: Colors.grey),
],
),
);
}
Widget buildRefresh({required Future Function() onRefresh}) {
return RefreshUntilWidget(onRefresh: onRefresh, text: "换一批");
}
Widget buildBanner(ComicRecommendModel item) {
return Padding(
padding: AppStyle.edgeInsetsB12,
child: ClipRRect(
borderRadius: AppStyle.radius4,
child: AspectRatio(
aspectRatio: 75 / 40,
child: Swiper(
itemWidth: 750,
itemHeight: 400,
autoplay: true,
itemCount: item.data.length,
itemBuilder: (_, i) => NetImage(
item.data[i].cover,
width: 750,
height: 400,
),
onTap: (i) {
controller.openDetail(item.data[i]);
},
pagination: SwiperCustomPagination(
builder: (BuildContext context, SwiperPluginConfig config) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.only(
left: 8,
right: 12,
top: 4,
bottom: 4,
),
//color: Colors.black12,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.black38,
Colors.transparent,
],
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Text(
item.data[config.activeIndex].title,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14, color: Colors.white),
),
),
AppStyle.hGap8,
PageIndicator(
controller: config.pageController!,
count: config.itemCount,
size: 10,
layout: PageIndicatorLayout.SCALE,
),
],
),
),
);
},
),
),
),
),
);
}
Widget buildTreeColumnGridView(List<ComicRecommendItemModel> items) {
return MasonryGridView.count(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 3,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
itemCount: items.length,
itemBuilder: (_, i) {
var item = items[i];
return InkWell(
onTap: () => controller.openDetail(item),
borderRadius: AppStyle.radius4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: AppStyle.radius4,
child: AspectRatio(
aspectRatio: 27 / 36,
child: NetImage(
item.cover,
width: 270,
height: 360,
),
),
),
AppStyle.vGap8,
Text(
item.title,
maxLines: 1,
style: const TextStyle(height: 1.2),
overflow: TextOverflow.ellipsis,
),
Text(
item.subTitle ?? item.status ?? '',
maxLines: 1,
style: const TextStyle(
height: 1.2,
fontSize: 12,
color: Colors.grey,
overflow: TextOverflow.ellipsis,
),
),
AppStyle.vGap8,
],
),
);
},
);
}
Widget buildAuthorGridView(List<ComicRecommendItemModel> items) {
return MasonryGridView.count(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 3,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
itemCount: items.length,
itemBuilder: (_, i) {
var item = items[i];
return InkWell(
onTap: () => controller.openDetail(item),
borderRadius: AppStyle.radius8,
child: Padding(
padding: AppStyle.edgeInsetsA12,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
NetImage(
item.cover,
width: 56,
height: 56,
borderRadius: 32,
),
Padding(
padding: AppStyle.edgeInsetsV8,
child: Text(
item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(height: 1.2, fontSize: 12),
),
),
],
),
),
);
},
);
}
Widget buildTwoColumnGridView(List<ComicRecommendItemModel> items) {
return MasonryGridView.count(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
itemCount: items.length,
itemBuilder: (_, i) {
var item = items[i];
return InkWell(
onTap: () => controller.openDetail(item),
borderRadius: AppStyle.radius4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: AppStyle.radius4,
child: AspectRatio(
aspectRatio: 32 / 17,
child: NetImage(
item.cover,
width: 320,
height: 170,
),
),
),
Padding(
padding: AppStyle.edgeInsetsV8,
child: Text(
item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(height: 1.2),
),
),
],
),
);
},
);
}
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter_dmzj/app/controller/base_controller.dart';
import 'package:flutter_dmzj/models/comic/special_model.dart';
import 'package:flutter_dmzj/requests/comic_request.dart';
import 'package:flutter_dmzj/routes/app_navigator.dart';
class ComicSpecialController extends BasePageController<ComicSpecialModel> {
final ComicRequest request = ComicRequest();
@override
Future<List<ComicSpecialModel>> getData(int page, int pageSize) async {
var ls = await request.special(page: page - 1);
return ls;
}
void toDetail(ComicSpecialModel item) {
if (item.pageType == 3) {
AppNavigator.toSpecialDetail(item.id);
} else {
AppNavigator.toWebView(item.pageUrl);
}
}
}

View File

@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:flutter_dmzj/app/app_style.dart';
import 'package:flutter_dmzj/app/utils.dart';
import 'package:flutter_dmzj/modules/comic/home/special/comic_special_controller.dart';
import 'package:flutter_dmzj/widgets/keep_alive_wrapper.dart';
import 'package:flutter_dmzj/widgets/net_image.dart';
import 'package:flutter_dmzj/widgets/page_list_view.dart';
import 'package:flutter_dmzj/widgets/shadow_card.dart';
import 'package:get/get.dart';
class ComicSpecialView extends StatelessWidget {
final ComicSpecialController controller;
ComicSpecialView({Key? key})
: controller = Get.put(ComicSpecialController()),
super(key: key);
@override
Widget build(BuildContext context) {
return KeepAliveWrapper(
child: PageListView(
pageController: controller,
firstRefresh: true,
showPageLoadding: false,
padding: AppStyle.edgeInsetsA12.copyWith(top: 0),
separatorBuilder: (context, i) => AppStyle.vGap12,
itemBuilder: (context, i) {
var item = controller.list[i];
return ShadowCard(
onTap: () {
controller.toDetail(item);
},
radius: 8,
child: Column(
children: [
AspectRatio(
aspectRatio: 710 / 284,
child: NetImage(
item.smallCover,
width: 710,
height: 354,
),
),
Padding(
padding: AppStyle.edgeInsetsA8,
child: Row(
children: [
Expanded(child: Text(item.title)),
Text(
Utils.formatTimestampToDate(item.createTime),
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
],
),
),
],
),
);
},
),
);
}
}