feat: 动态页自渲染
This commit is contained in:
136
lib/common/widgets/html_render.dart
Normal file
136
lib/common/widgets/html_render.dart
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
|
class HtmlRender extends StatelessWidget {
|
||||||
|
String? htmlContent;
|
||||||
|
final int? imgCount;
|
||||||
|
final List? imgList;
|
||||||
|
|
||||||
|
HtmlRender({
|
||||||
|
this.htmlContent,
|
||||||
|
this.imgCount,
|
||||||
|
this.imgList,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Html(
|
||||||
|
data: htmlContent,
|
||||||
|
// tagsList: Html.tags..addAll(["form", "label", "input"]),
|
||||||
|
onLinkTap: (url, buildContext, attributes) => {},
|
||||||
|
extensions: [
|
||||||
|
TagExtension(
|
||||||
|
tagsToExtend: {"img"},
|
||||||
|
builder: (extensionContext) {
|
||||||
|
String? imgUrl = extensionContext.attributes['src'];
|
||||||
|
if (imgUrl!.startsWith('//')) {
|
||||||
|
imgUrl = 'https:$imgUrl';
|
||||||
|
}
|
||||||
|
if (imgUrl.startsWith('http://')) {
|
||||||
|
imgUrl = imgUrl.replaceAll('http://', 'https://');
|
||||||
|
}
|
||||||
|
|
||||||
|
print(imgUrl);
|
||||||
|
bool isEmote = imgUrl.contains('/emote/');
|
||||||
|
bool isMall = imgUrl.contains('/mall/');
|
||||||
|
if (isMall) {
|
||||||
|
return SizedBox();
|
||||||
|
}
|
||||||
|
// bool inTable =
|
||||||
|
// extensionContext.element!.previousElementSibling == null ||
|
||||||
|
// extensionContext.element!.nextElementSibling == null;
|
||||||
|
// imgUrl = Utils().imageUrl(imgUrl!);
|
||||||
|
return Image.network(
|
||||||
|
imgUrl,
|
||||||
|
width: isEmote ? 22 : null,
|
||||||
|
height: isEmote ? 22 : null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
style: {
|
||||||
|
"html": Style(
|
||||||
|
fontSize: FontSize.medium,
|
||||||
|
lineHeight: LineHeight.percent(140),
|
||||||
|
),
|
||||||
|
"body": Style(margin: Margins.zero, padding: HtmlPaddings.zero),
|
||||||
|
"a": Style(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
textDecoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
"p": Style(
|
||||||
|
margin: Margins.only(bottom: 0),
|
||||||
|
),
|
||||||
|
"span": Style(
|
||||||
|
fontSize: FontSize.medium,
|
||||||
|
),
|
||||||
|
"li > p": Style(
|
||||||
|
display: Display.inline,
|
||||||
|
),
|
||||||
|
"li": Style(
|
||||||
|
padding: HtmlPaddings.only(bottom: 4),
|
||||||
|
textAlign: TextAlign.justify,
|
||||||
|
),
|
||||||
|
"image": Style(margin: Margins.only(top: 4, bottom: 4)),
|
||||||
|
"p > img": Style(margin: Margins.only(top: 4, bottom: 4)),
|
||||||
|
"code": Style(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.onInverseSurface),
|
||||||
|
"code > span": Style(textAlign: TextAlign.start),
|
||||||
|
"hr": Style(
|
||||||
|
margin: Margins.zero,
|
||||||
|
padding: HtmlPaddings.zero,
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
width: 1.0,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'table': Style(
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(
|
||||||
|
width: 0.5,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
bottom: BorderSide(
|
||||||
|
width: 0.5,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'tr': Style(
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
width: 1.0,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
left: BorderSide(
|
||||||
|
width: 1.0,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'thead': Style(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
|
),
|
||||||
|
'th': Style(
|
||||||
|
padding: HtmlPaddings.only(left: 3, right: 3),
|
||||||
|
),
|
||||||
|
'td': Style(
|
||||||
|
padding: HtmlPaddings.all(4.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
lib/http/html.dart
Normal file
40
lib/http/html.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import 'package:html/dom.dart';
|
||||||
|
import 'package:html/parser.dart';
|
||||||
|
import 'package:pilipala/http/index.dart';
|
||||||
|
|
||||||
|
class HtmlHttp {
|
||||||
|
static Future reqHtml(id) async {
|
||||||
|
var response = await Request().get("https://www.bilibili.com/opus/$id");
|
||||||
|
Document rootTree = parse(response.data);
|
||||||
|
Element body = rootTree.body!;
|
||||||
|
Element appDom = body.querySelector('#app')!;
|
||||||
|
Element authorHeader = appDom.querySelector('.fixed-author-header')!;
|
||||||
|
// 头像
|
||||||
|
String avatar = authorHeader.querySelector('img')!.attributes['src']!;
|
||||||
|
avatar = 'https:${avatar.split('@')[0]}';
|
||||||
|
String uname =
|
||||||
|
authorHeader.querySelector('.fixed-author-header__author__name')!.text;
|
||||||
|
// 动态详情
|
||||||
|
Element opusDetail = appDom.querySelector('.opus-detail')!;
|
||||||
|
// 发布时间
|
||||||
|
String updateTime =
|
||||||
|
opusDetail.querySelector('.opus-module-author__pub__text')!.text;
|
||||||
|
//
|
||||||
|
String opusContent =
|
||||||
|
opusDetail.querySelector('.opus-module-content')!.innerHtml;
|
||||||
|
String commentId = opusDetail
|
||||||
|
.querySelector('.bili-comment-container')!
|
||||||
|
.className
|
||||||
|
.split(' ')[1]
|
||||||
|
.split('-')[2];
|
||||||
|
// List imgList = opusDetail.querySelectorAll('bili-album__preview__picture__img');
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'avatar': avatar,
|
||||||
|
'uname': uname,
|
||||||
|
'updateTime': updateTime,
|
||||||
|
'content': opusContent,
|
||||||
|
'commentId': commentId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
19
lib/pages/html/controller.dart
Normal file
19
lib/pages/html/controller.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/http/html.dart';
|
||||||
|
|
||||||
|
class HtmlRenderController extends GetxController {
|
||||||
|
late String id;
|
||||||
|
late Map response;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
id = Get.parameters['id']!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future reqHtml() async {
|
||||||
|
var res = await HtmlHttp.reqHtml(id);
|
||||||
|
response = res;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
4
lib/pages/html/index.dart
Normal file
4
lib/pages/html/index.dart
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
library html_render;
|
||||||
|
|
||||||
|
export './controller.dart';
|
||||||
|
export './view.dart';
|
||||||
117
lib/pages/html/view.dart
Normal file
117
lib/pages/html/view.dart
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/common/widgets/html_render.dart';
|
||||||
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
|
||||||
|
import 'controller.dart';
|
||||||
|
|
||||||
|
class HtmlRenderPage extends StatefulWidget {
|
||||||
|
const HtmlRenderPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HtmlRenderPage> createState() => _HtmlRenderPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HtmlRenderPageState extends State<HtmlRenderPage> {
|
||||||
|
HtmlRenderController htmlRenderCtr = Get.put(HtmlRenderController());
|
||||||
|
late String title;
|
||||||
|
late String id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
title = Get.parameters['title']!;
|
||||||
|
id = Get.parameters['id']!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: false,
|
||||||
|
title: Text(title),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
FutureBuilder(
|
||||||
|
future: htmlRenderCtr.reqHtml(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
var data = snapshot.data;
|
||||||
|
if (data['status']) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
type: 'avatar',
|
||||||
|
src: htmlRenderCtr.response['avatar']!,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(htmlRenderCtr.response['uname'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleSmall!
|
||||||
|
.fontSize,
|
||||||
|
)),
|
||||||
|
Text(
|
||||||
|
htmlRenderCtr.response['updateTime'],
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.outline,
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelSmall!
|
||||||
|
.fontSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
||||||
|
child: HtmlRender(
|
||||||
|
htmlContent: htmlRenderCtr.response['content'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
width: 8,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.dividerColor
|
||||||
|
.withOpacity(0.05),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Text('error');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,6 +14,7 @@ import 'package:pilipala/pages/follow/index.dart';
|
|||||||
import 'package:pilipala/pages/history/index.dart';
|
import 'package:pilipala/pages/history/index.dart';
|
||||||
import 'package:pilipala/pages/home/index.dart';
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
import 'package:pilipala/pages/hot/index.dart';
|
import 'package:pilipala/pages/hot/index.dart';
|
||||||
|
import 'package:pilipala/pages/html/index.dart';
|
||||||
import 'package:pilipala/pages/later/index.dart';
|
import 'package:pilipala/pages/later/index.dart';
|
||||||
import 'package:pilipala/pages/liveRoom/view.dart';
|
import 'package:pilipala/pages/liveRoom/view.dart';
|
||||||
import 'package:pilipala/pages/member/index.dart';
|
import 'package:pilipala/pages/member/index.dart';
|
||||||
@ -107,6 +108,8 @@ class Routes {
|
|||||||
name: '/displayModeSetting', page: () => const SetDiaplayMode()),
|
name: '/displayModeSetting', page: () => const SetDiaplayMode()),
|
||||||
// 关于
|
// 关于
|
||||||
CustomGetPage(name: '/about', page: () => const AboutPage()),
|
CustomGetPage(name: '/about', page: () => const AboutPage()),
|
||||||
|
//
|
||||||
|
CustomGetPage(name: '/htmlRender', page: () => const HtmlRenderPage()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
32
pubspec.lock
32
pubspec.lock
@ -257,6 +257,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.3"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -454,6 +462,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.6.0"
|
||||||
|
flutter_html:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_html
|
||||||
|
sha256: "02ad69e813ecfc0728a455e4bf892b9379983e050722b1dce00192ee2e41d1ee"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0-beta.2"
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -581,6 +597,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
|
html:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.4"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -669,6 +693,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
list_counter:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: list_counter
|
||||||
|
sha256: c447ae3dfcd1c55f0152867090e67e219d42fe6d4f2807db4bbe8b8d69912237
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
loading_more_list:
|
loading_more_list:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -119,6 +119,11 @@ dependencies:
|
|||||||
system_proxy: ^0.1.0
|
system_proxy: ^0.1.0
|
||||||
# pip
|
# pip
|
||||||
floating: ^2.0.1
|
floating: ^2.0.1
|
||||||
|
# html解析
|
||||||
|
html: ^0.15.4
|
||||||
|
# html渲染
|
||||||
|
flutter_html: ^3.0.0-beta.2
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user