v1.0.1
This commit is contained in:
398
lib/modules/news/detail/news_detail_controller.dart
Normal file
398
lib/modules/news/detail/news_detail_controller.dart
Normal file
@@ -0,0 +1,398 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dmzj/app/app_color.dart';
|
||||
import 'package:flutter_dmzj/app/app_constant.dart';
|
||||
import 'package:flutter_dmzj/app/app_style.dart';
|
||||
import 'package:flutter_dmzj/app/controller/base_controller.dart';
|
||||
import 'package:flutter_dmzj/app/dialog_utils.dart';
|
||||
import 'package:flutter_dmzj/app/log.dart';
|
||||
import 'package:flutter_dmzj/app/utils.dart';
|
||||
import 'package:flutter_dmzj/requests/news_request.dart';
|
||||
import 'package:flutter_dmzj/routes/app_navigator.dart';
|
||||
import 'package:flutter_dmzj/services/app_settings_service.dart';
|
||||
import 'package:flutter_dmzj/services/db_service.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:universal_html/html.dart' as html;
|
||||
import 'package:universal_html/parsing.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class NewsDetailController extends BaseController {
|
||||
final String newsUrl;
|
||||
final String title;
|
||||
final int id;
|
||||
final NewsRequest request = NewsRequest();
|
||||
AppSettingsService get settings => AppSettingsService.instance;
|
||||
NewsDetailController(
|
||||
{required this.newsUrl, this.title = "资讯详情", required this.id}) {
|
||||
newsTitle.value = title;
|
||||
if (id == 0) {
|
||||
newsId = int.tryParse(
|
||||
RegExp(r"/(\d+).html").firstMatch(newsUrl)?.group(1) ?? "0") ??
|
||||
0;
|
||||
} else {
|
||||
newsId = id;
|
||||
}
|
||||
}
|
||||
WebViewController? webViewController =
|
||||
(Platform.isAndroid || Platform.isIOS) ? WebViewController() : null;
|
||||
|
||||
/// 评论数
|
||||
var commentAmount = 0.obs;
|
||||
|
||||
/// 点赞数
|
||||
var moodAmount = 0.obs;
|
||||
|
||||
/// 是否点过赞
|
||||
var liked = false.obs;
|
||||
|
||||
/// 是否已经收藏
|
||||
var collected = false.obs;
|
||||
|
||||
var newsId = 0;
|
||||
|
||||
var newsTitle = "资讯详情".obs;
|
||||
|
||||
var htmlContent = "".obs;
|
||||
var author = "".obs;
|
||||
var photo = "".obs;
|
||||
var src = "".obs;
|
||||
var time = "".obs;
|
||||
|
||||
var images = <String>[];
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
liked.value = DBService.instance.newsLikeBox.containsKey(newsId);
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
initWebView();
|
||||
} else {
|
||||
loadHtml();
|
||||
}
|
||||
// loadStat();
|
||||
// checkCollected();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
var currentUrl = "";
|
||||
void initWebView() {
|
||||
webViewController!.setJavaScriptMode(JavaScriptMode.unrestricted);
|
||||
webViewController!.setBackgroundColor(
|
||||
Get.isDarkMode ? Colors.black : AppColor.backgroundColor);
|
||||
webViewController!.setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageStarted: (String url) {
|
||||
pageLoadding.value = true;
|
||||
},
|
||||
onPageFinished: (String url) async {
|
||||
try {
|
||||
await setFontSize();
|
||||
//防止亮瞎24K钛合金狗眼
|
||||
if (Get.isDarkMode) {
|
||||
await webViewController!.runJavaScript("""
|
||||
document.body.style.background="#000000";
|
||||
document.getElementsByClassName("min_box")[0].style.background="#000000";
|
||||
document.getElementsByClassName("news_box")[0].style.color="#f1f2f6";
|
||||
document.getElementsByClassName("min_box_tit")[0].style.color="#fff";
|
||||
""");
|
||||
}
|
||||
//加载前5张图片
|
||||
//当Web没有滚动条时,图片不会加载,这里手动给他加载出来
|
||||
await webViewController!.runJavaScript("""
|
||||
\$('.news_box img:lt(5)').each(function () {
|
||||
\$(this).lazyload({
|
||||
effect: "fadeIn"
|
||||
});
|
||||
});""");
|
||||
//读取全部的图片
|
||||
|
||||
var imagesResult =
|
||||
await webViewController?.runJavaScriptReturningResult('''
|
||||
function getImgLinks(){
|
||||
var imgLinks = [];
|
||||
\$('img').each(function() {
|
||||
var src = \$(this).attr('data-original');
|
||||
if (src && src.startsWith('https://images')) {
|
||||
imgLinks.push(src);
|
||||
}
|
||||
});
|
||||
console.log(imgLinks);
|
||||
return ${Platform.isIOS ? "JSON.stringify(imgLinks)" : "imgLinks"};
|
||||
}
|
||||
getImgLinks();
|
||||
''');
|
||||
if (imagesResult != null && imagesResult != "null") {
|
||||
List list = json.decode(imagesResult.toString());
|
||||
images = list.map((e) => e.toString()).toList();
|
||||
}
|
||||
} finally {
|
||||
pageLoadding.value = false;
|
||||
}
|
||||
},
|
||||
onWebResourceError: (WebResourceError error) {},
|
||||
onNavigationRequest: (NavigationRequest request) async {
|
||||
var result = await onTapUrl(request.url);
|
||||
return result
|
||||
? NavigationDecision.prevent
|
||||
: NavigationDecision.navigate;
|
||||
},
|
||||
),
|
||||
);
|
||||
Log.d(newsUrl);
|
||||
currentUrl = "https://v3api.zaimanhua.com/v3/article/show/$newsId.html";
|
||||
|
||||
webViewController!.loadRequest(Uri.parse(currentUrl));
|
||||
}
|
||||
|
||||
void loadHtml() async {
|
||||
try {
|
||||
pageError.value = false;
|
||||
pageLoadding.value = true;
|
||||
var result = await Dio().get(
|
||||
newsUrl,
|
||||
options: Options(
|
||||
responseType: ResponseType.plain,
|
||||
),
|
||||
);
|
||||
final htmlDocument = parseHtmlDocument(result.data);
|
||||
var news = htmlDocument.documentElement!.querySelector('.news_box');
|
||||
|
||||
htmlContent.value = news!.innerHtml ?? "";
|
||||
|
||||
author.value =
|
||||
htmlDocument.documentElement?.querySelector('.txt1')?.innerText ?? "";
|
||||
src.value =
|
||||
htmlDocument.documentElement?.querySelector('.txt2')?.innerText ?? "";
|
||||
time.value =
|
||||
htmlDocument.documentElement?.querySelector('.txt3')?.innerText ?? "";
|
||||
|
||||
var imgList = htmlDocument.documentElement?.querySelectorAll('img');
|
||||
var imagesList = <String>[];
|
||||
for (html.Element img in imgList ?? []) {
|
||||
var imgSrc = img.getAttribute("data-original");
|
||||
if (imgSrc != null) {
|
||||
imagesList.add(imgSrc);
|
||||
}
|
||||
}
|
||||
images = imagesList;
|
||||
} catch (e) {
|
||||
pageError.value = true;
|
||||
errorMsg.value = e.toString();
|
||||
} finally {
|
||||
pageLoadding.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void loadStat() async {
|
||||
try {
|
||||
var result = await request.stat(newsId);
|
||||
commentAmount.value = result.commentAmount;
|
||||
moodAmount.value = result.moodAmount;
|
||||
newsTitle.value = result.title;
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
SmartDialog.showToast("读取新闻数据失败:$e");
|
||||
}
|
||||
}
|
||||
|
||||
void checkCollected() async {
|
||||
if (!UserService.instance.logined.value) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
collected.value = await request.checkCollect(newsId);
|
||||
} catch (e) {
|
||||
Log.logPrint(e);
|
||||
SmartDialog.showToast("检查用户收藏状态失败:$e");
|
||||
}
|
||||
}
|
||||
|
||||
void refershContent() {
|
||||
webViewController!.reload();
|
||||
}
|
||||
|
||||
void collect() async {
|
||||
if (!await UserService.instance.login()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
SmartDialog.showLoading();
|
||||
await (collected.value
|
||||
? request.delCollect(newsId)
|
||||
: request.collect(newsId));
|
||||
collected.value = !collected.value;
|
||||
} catch (e) {
|
||||
Log.logPrint(e);
|
||||
SmartDialog.showToast(e.toString());
|
||||
} finally {
|
||||
SmartDialog.dismiss(status: SmartStatus.loading);
|
||||
}
|
||||
}
|
||||
|
||||
void like() async {
|
||||
if (liked.value) {
|
||||
SmartDialog.showToast("已经点过赞了");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
SmartDialog.showLoading();
|
||||
await request.like(newsId);
|
||||
liked.value = true;
|
||||
moodAmount.value += 1;
|
||||
DBService.instance.newsLikeBox.put(newsId, true);
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
} finally {
|
||||
SmartDialog.dismiss(status: SmartStatus.loading);
|
||||
}
|
||||
}
|
||||
|
||||
void share() {
|
||||
Utils.share(newsUrl, content: title);
|
||||
}
|
||||
|
||||
void comment() async {
|
||||
AppNavigator.toComment(objId: newsId, type: AppConstant.kTypeNews);
|
||||
}
|
||||
|
||||
void photoView() {
|
||||
DialogUtils.showImageViewer(0, images);
|
||||
}
|
||||
|
||||
void showImageView(String imgSrc) {
|
||||
if (imgSrc.isEmpty) {
|
||||
return;
|
||||
}
|
||||
if (images.contains(imgSrc)) {
|
||||
DialogUtils.showImageViewer(
|
||||
images.indexOf(imgSrc),
|
||||
images,
|
||||
);
|
||||
} else {
|
||||
DialogUtils.showImageViewer(0, [imgSrc]);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> onTapUrl(url) async {
|
||||
//iOS处理
|
||||
if (url == currentUrl) {
|
||||
return false;
|
||||
}
|
||||
var uri = Uri.parse(url);
|
||||
Log.d(url);
|
||||
if (uri.scheme == "dmzjimage") {
|
||||
//打开图片
|
||||
showImageView(uri.queryParameters['src'].toString());
|
||||
return true;
|
||||
} else if (uri.scheme == "dmzjandroid") {
|
||||
var id = int.tryParse(uri.queryParameters["id"].toString()) ?? 0;
|
||||
if (uri.path == "/cartoon_description") {
|
||||
AppNavigator.toComicDetail(id);
|
||||
} else {
|
||||
AppNavigator.toNovelDetail(id);
|
||||
}
|
||||
return true;
|
||||
} else if (uri.scheme == "https" || uri.scheme == "http") {
|
||||
if (uri.path.contains("article/")) {
|
||||
AppNavigator.toNewsDetail(url: url);
|
||||
} else {
|
||||
AppNavigator.toWebView(url);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
SmartDialog.showToast("无法打开链接:$url");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void showSettings() {
|
||||
AppNavigator.showBottomSheet(
|
||||
SizedBox(
|
||||
height: 400,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ListTile(
|
||||
title: Text("设置"),
|
||||
trailing: IconButton(
|
||||
onPressed: AppNavigator.closePage,
|
||||
icon: Icon(Icons.close),
|
||||
),
|
||||
contentPadding: AppStyle.edgeInsetsL12,
|
||||
),
|
||||
Divider(
|
||||
height: 1.0,
|
||||
color: Colors.grey.withOpacity(.2),
|
||||
),
|
||||
Obx(
|
||||
() => ListTile(
|
||||
title: const Text("字体大小"),
|
||||
leading: const Icon(Icons.text_fields_rounded),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
settings.setNewsFontSize(
|
||||
settings.newsFontSize.value + 1,
|
||||
);
|
||||
setFontSize();
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
AppStyle.hGap12,
|
||||
Text("${settings.newsFontSize.value}"),
|
||||
AppStyle.hGap12,
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
settings.setNewsFontSize(
|
||||
settings.newsFontSize.value - 1,
|
||||
);
|
||||
setFontSize();
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.remove,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.photo),
|
||||
title: const Text("进入看图模式"),
|
||||
onTap: () {
|
||||
AppNavigator.closePage();
|
||||
photoView();
|
||||
},
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future setFontSize() async {
|
||||
try {
|
||||
if (webViewController == null) {
|
||||
return;
|
||||
}
|
||||
await webViewController!.runJavaScript(
|
||||
'''document.getElementsByClassName("news_box")[0].style.fontSize="${settings.newsFontSize}px";
|
||||
document.getElementsByClassName("news_box")[0].style.lineHeight="1.6em";
|
||||
''');
|
||||
} catch (e) {
|
||||
Log.logPrint(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
189
lib/modules/news/detail/news_detail_page.dart
Normal file
189
lib/modules/news/detail/news_detail_page.dart
Normal file
@@ -0,0 +1,189 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dmzj/app/app_style.dart';
|
||||
import 'package:flutter_dmzj/modules/news/detail/news_detail_controller.dart';
|
||||
import 'package:flutter_dmzj/widgets/net_image.dart';
|
||||
import 'package:flutter_dmzj/widgets/status/app_error_widget.dart';
|
||||
import 'package:flutter_dmzj/widgets/status/app_loadding_widget.dart';
|
||||
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class NewsDetailPage extends StatelessWidget {
|
||||
final String newsUrl;
|
||||
final int newsId;
|
||||
final String title;
|
||||
final NewsDetailController controller;
|
||||
NewsDetailPage({
|
||||
required this.newsUrl,
|
||||
this.title = "资讯详情",
|
||||
required this.newsId,
|
||||
Key? key,
|
||||
}) : controller = Get.put(
|
||||
NewsDetailController(id: newsId, newsUrl: newsUrl, title: title),
|
||||
tag: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Obx(() => Text(controller.newsTitle.value)),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: controller.share,
|
||||
icon: const Icon(Icons.share),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
(Platform.isAndroid || Platform.isIOS)
|
||||
? Obx(
|
||||
() => Offstage(
|
||||
offstage: controller.pageLoadding.value,
|
||||
child: WebViewWidget(
|
||||
controller: controller.webViewController!,
|
||||
),
|
||||
),
|
||||
)
|
||||
: buildHtml(),
|
||||
Obx(
|
||||
() => Offstage(
|
||||
offstage: !controller.pageLoadding.value,
|
||||
child: const AppLoaddingWidget(),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => Offstage(
|
||||
offstage: !controller.pageError.value,
|
||||
child: AppErrorWidget(
|
||||
errorMsg: controller.errorMsg.value,
|
||||
onRefresh: controller.refershContent,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: BottomAppBar(
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Obx(
|
||||
() => TextButton.icon(
|
||||
onPressed: controller.like,
|
||||
icon: Icon(
|
||||
controller.liked.value
|
||||
? Remix.thumb_up_fill
|
||||
: Remix.thumb_up_line,
|
||||
size: 20,
|
||||
),
|
||||
label: Text(controller.moodAmount > 0
|
||||
? "${controller.moodAmount}"
|
||||
: "点赞"),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Obx(
|
||||
() => TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: const TextStyle(fontSize: 14),
|
||||
),
|
||||
onPressed: controller.comment,
|
||||
icon: const Icon(
|
||||
Remix.chat_2_line,
|
||||
size: 20,
|
||||
),
|
||||
label: Text(controller.commentAmount > 0
|
||||
? "${controller.commentAmount}"
|
||||
: "评论"),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Expanded(
|
||||
// child: Obx(
|
||||
// () => TextButton.icon(
|
||||
// style: TextButton.styleFrom(
|
||||
// textStyle: const TextStyle(fontSize: 14),
|
||||
// ),
|
||||
// onPressed: controller.collect,
|
||||
// icon: Icon(
|
||||
// controller.collected.value
|
||||
// ? Remix.star_fill
|
||||
// : Remix.star_line,
|
||||
// size: 20,
|
||||
// ),
|
||||
// label: Text(controller.collected.value ? "已收藏" : "收藏"),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
Expanded(
|
||||
child: TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: const TextStyle(fontSize: 14),
|
||||
),
|
||||
onPressed: controller.showSettings,
|
||||
icon: const Icon(
|
||||
Remix.settings_line,
|
||||
size: 20,
|
||||
),
|
||||
label: const Text("设置"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildHtml() {
|
||||
return Obx(
|
||||
() => ListView(
|
||||
padding: AppStyle.edgeInsetsA12,
|
||||
children: [
|
||||
Text(
|
||||
controller.title,
|
||||
style: Get.textTheme.titleLarge,
|
||||
),
|
||||
AppStyle.vGap4,
|
||||
Text(
|
||||
"${controller.author.value} ${controller.src.value} ${controller.time.value}",
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
AppStyle.vGap12,
|
||||
HtmlWidget(
|
||||
controller.htmlContent.value,
|
||||
textStyle: TextStyle(
|
||||
fontSize: controller.settings.newsFontSize.value.toDouble(),
|
||||
),
|
||||
customWidgetBuilder: (e) {
|
||||
if (e.localName == "img") {
|
||||
var imgSrc = e.attributes["src"];
|
||||
imgSrc ??= e.attributes["data-original"];
|
||||
return GestureDetector(
|
||||
child: NetImage(
|
||||
imgSrc!,
|
||||
borderRadius: 4,
|
||||
),
|
||||
onTap: () {
|
||||
controller.showImageView(imgSrc ?? "");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
onTapUrl: controller.onTapUrl,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
67
lib/modules/news/home/news_home_controller.dart
Normal file
67
lib/modules/news/home/news_home_controller.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
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/models/news/news_tag_model.dart';
|
||||
import 'package:flutter_dmzj/modules/news/home/news_list_controller.dart';
|
||||
import 'package:flutter_dmzj/requests/news_request.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class NewsHomeController extends GetxController
|
||||
with GetTickerProviderStateMixin {
|
||||
NewsRequest request = NewsRequest();
|
||||
late TabController tabController;
|
||||
var loadding = true;
|
||||
List<NewsTagModel> categores = [];
|
||||
var error = false;
|
||||
var errorMsg = "";
|
||||
|
||||
StreamSubscription<dynamic>? streamSubscription;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
streamSubscription = EventBus.instance.listen(
|
||||
EventBus.kBottomNavigationBarClicked,
|
||||
(index) {
|
||||
if (index == 1) {
|
||||
refreshOrScrollTop();
|
||||
}
|
||||
},
|
||||
);
|
||||
loadCategores();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
streamSubscription?.cancel();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void loadCategores() async {
|
||||
try {
|
||||
loadding = true;
|
||||
error = false;
|
||||
update();
|
||||
var category = await request.category();
|
||||
category.insert(0, NewsTagModel(id: 0, name: "最新"));
|
||||
tabController = TabController(length: category.length, vsync: this);
|
||||
|
||||
categores = category;
|
||||
} catch (e) {
|
||||
errorMsg = e.toString();
|
||||
error = true;
|
||||
} finally {
|
||||
loadding = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void refreshOrScrollTop() {
|
||||
var tabIndex = tabController.index;
|
||||
BasePageController controller;
|
||||
controller = Get.find<NewsListController>(tag: "${categores[tabIndex].id}");
|
||||
controller.scrollToTopOrRefresh();
|
||||
}
|
||||
}
|
||||
57
lib/modules/news/home/news_home_page.dart
Normal file
57
lib/modules/news/home/news_home_page.dart
Normal file
@@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dmzj/app/platform_utils.dart';
|
||||
import 'package:flutter_dmzj/modules/news/home/news_home_controller.dart';
|
||||
import 'package:flutter_dmzj/modules/news/home/news_list_view.dart';
|
||||
import 'package:flutter_dmzj/widgets/status/app_error_widget.dart';
|
||||
import 'package:flutter_dmzj/widgets/status/app_loadding_widget.dart';
|
||||
import 'package:flutter_dmzj/widgets/tab_appbar.dart';
|
||||
import 'package:flutter_dmzj/widgets/windows_tab_page.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class NewsHomePage extends GetView<NewsHomeController> {
|
||||
const NewsHomePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<NewsHomeController>(
|
||||
init: controller,
|
||||
builder: (controller) {
|
||||
if (controller.loadding) {
|
||||
return const Scaffold(
|
||||
body: AppLoaddingWidget(),
|
||||
);
|
||||
}
|
||||
if (!controller.loadding && controller.error) {
|
||||
return Scaffold(
|
||||
body: AppErrorWidget(
|
||||
errorMsg: controller.errorMsg,
|
||||
onRefresh: controller.loadCategores,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (PlatformUtils.isWindows) {
|
||||
return WindowsTabPage(
|
||||
tabs: controller.categores
|
||||
.map((e) => WindowsTabItem(
|
||||
label: e.name,
|
||||
body: NewsListView(tag: e),
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
return Scaffold(
|
||||
appBar: TabAppBar(
|
||||
tabs: controller.categores.map((e) => Tab(text: e.name)).toList(),
|
||||
controller: controller.tabController,
|
||||
),
|
||||
body: TabBarView(
|
||||
controller: controller.tabController,
|
||||
children:
|
||||
controller.categores.map((e) => NewsListView(tag: e)).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
40
lib/modules/news/home/news_list_controller.dart
Normal file
40
lib/modules/news/home/news_list_controller.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter_dmzj/app/controller/base_controller.dart';
|
||||
import 'package:flutter_dmzj/models/news/news_banner_model.dart';
|
||||
import 'package:flutter_dmzj/models/news/news_list_item_model.dart';
|
||||
import 'package:flutter_dmzj/models/news/news_tag_model.dart';
|
||||
import 'package:flutter_dmzj/requests/news_request.dart';
|
||||
import 'package:flutter_dmzj/routes/app_navigator.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class NewsListController extends BasePageController<NewsListItemModel> {
|
||||
final NewsRequest request = NewsRequest();
|
||||
final NewsTagModel tag;
|
||||
NewsListController(this.tag);
|
||||
|
||||
RxList<NewsBannerModel> banners = RxList<NewsBannerModel>();
|
||||
|
||||
@override
|
||||
Future<List<NewsListItemModel>> getData(int page, int pageSize) async {
|
||||
if (tag.id == 0 && page == 1) {
|
||||
loadBanner();
|
||||
}
|
||||
return await request.getNewsList(tag.id, page);
|
||||
}
|
||||
|
||||
void loadBanner() async {
|
||||
try {
|
||||
banners.value = await request.banner();
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void openBanner(NewsBannerModel item) {
|
||||
AppNavigator.toNewsDetail(
|
||||
url: item.objectUrl ?? "",
|
||||
newsId: item.objectId ?? 0,
|
||||
title: item.title,
|
||||
);
|
||||
}
|
||||
}
|
||||
200
lib/modules/news/home/news_list_view.dart
Normal file
200
lib/modules/news/home/news_list_view.dart
Normal file
@@ -0,0 +1,200 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dmzj/app/app_style.dart';
|
||||
import 'package:flutter_dmzj/app/utils.dart';
|
||||
import 'package:flutter_dmzj/models/news/news_tag_model.dart';
|
||||
import 'package:flutter_dmzj/modules/news/home/news_list_controller.dart';
|
||||
import 'package:flutter_dmzj/routes/app_navigator.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_swiper_view/flutter_swiper_view.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class NewsListView extends StatelessWidget {
|
||||
final NewsTagModel tag;
|
||||
final NewsListController controller;
|
||||
NewsListView({Key? key, required this.tag})
|
||||
: controller = Get.put(NewsListController(tag), tag: tag.id.toString()),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return KeepAliveWrapper(
|
||||
child: PageListView(
|
||||
pageController: controller,
|
||||
firstRefresh: true,
|
||||
separatorBuilder: (context, i) => Divider(
|
||||
endIndent: 12,
|
||||
indent: 12,
|
||||
color: Colors.grey.withOpacity(.2),
|
||||
height: 1,
|
||||
),
|
||||
header: tag.id == 0 ? buildBanner() : null,
|
||||
itemBuilder: (context, i) {
|
||||
var item = controller.list[i];
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
AppNavigator.toNewsDetail(
|
||||
newsId: item.articleId.toInt(),
|
||||
title: item.title,
|
||||
url: item.pageUrl ?? "",
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: AppStyle.edgeInsetsA12,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
NetImage(
|
||||
item.rowPicUrl ?? "",
|
||||
width: 100,
|
||||
height: 62,
|
||||
borderRadius: 4,
|
||||
),
|
||||
AppStyle.hGap12,
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 62,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
Utils.formatTimestamp(item.createTime ?? 0),
|
||||
style: const TextStyle(
|
||||
color: Colors.grey, fontSize: 12),
|
||||
),
|
||||
// Row(
|
||||
// children: <Widget>[
|
||||
// const Icon(
|
||||
// Icons.thumb_up,
|
||||
// size: 12.0,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// AppStyle.hGap4,
|
||||
// Text(
|
||||
// item.moodAmount.toString(),
|
||||
// style: const TextStyle(
|
||||
// color: Colors.grey,
|
||||
// fontSize: 12,
|
||||
// ),
|
||||
// ),
|
||||
// AppStyle.hGap8,
|
||||
// const Icon(
|
||||
// Icons.chat,
|
||||
// size: 12.0,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// AppStyle.hGap4,
|
||||
// Text(
|
||||
// item.commentAmount.toString(),
|
||||
// style: const TextStyle(
|
||||
// color: Colors.grey,
|
||||
// fontSize: 12,
|
||||
// ),
|
||||
// )
|
||||
// ],
|
||||
// )
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBanner() {
|
||||
return Padding(
|
||||
padding: AppStyle.edgeInsetsH12.copyWith(bottom: 4),
|
||||
child: Obx(
|
||||
() => ClipRRect(
|
||||
borderRadius: AppStyle.radius4,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 75 / 40,
|
||||
child: controller.banners.isEmpty
|
||||
? const SizedBox()
|
||||
: Swiper(
|
||||
itemWidth: 750,
|
||||
itemHeight: 400,
|
||||
autoplay: true,
|
||||
itemCount: controller.banners.length,
|
||||
onTap: (i) {
|
||||
controller.openBanner(controller.banners[i]);
|
||||
},
|
||||
itemBuilder: (_, i) => NetImage(
|
||||
controller.banners[i].picUrl,
|
||||
width: 750,
|
||||
height: 400,
|
||||
),
|
||||
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(
|
||||
controller
|
||||
.banners[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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user