v1.0.1
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
61
lib/modules/comic/home/category/comic_category_view.dart
Normal file
61
lib/modules/comic/home/category/comic_category_view.dart
Normal 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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
57
lib/modules/comic/home/comic_home_controller.dart
Normal file
57
lib/modules/comic/home/comic_home_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
62
lib/modules/comic/home/comic_home_page.dart
Normal file
62
lib/modules/comic/home/comic_home_page.dart
Normal 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(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
21
lib/modules/comic/home/latest/comic_latest_controller.dart
Normal file
21
lib/modules/comic/home/latest/comic_latest_controller.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
176
lib/modules/comic/home/latest/comic_latest_view.dart
Normal file
176
lib/modules/comic/home/latest/comic_latest_view.dart
Normal 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),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
54
lib/modules/comic/home/rank/comic_rank_controller.dart
Normal file
54
lib/modules/comic/home/rank/comic_rank_controller.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
200
lib/modules/comic/home/rank/comic_rank_view.dart
Normal file
200
lib/modules/comic/home/rank/comic_rank_view.dart
Normal 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,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
188
lib/modules/comic/home/recommend/comic_recommend_controller.dart
Normal file
188
lib/modules/comic/home/recommend/comic_recommend_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
372
lib/modules/comic/home/recommend/comic_recommend_view.dart
Normal file
372
lib/modules/comic/home/recommend/comic_recommend_view.dart
Normal 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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
23
lib/modules/comic/home/special/comic_special_controller.dart
Normal file
23
lib/modules/comic/home/special/comic_special_controller.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
lib/modules/comic/home/special/comic_special_view.dart
Normal file
65
lib/modules/comic/home/special/comic_special_view.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user