mod: 布局

This commit is contained in:
guozhigq
2024-09-27 00:21:07 +08:00
parent 982a52372f
commit 620b32aa71
3 changed files with 311 additions and 199 deletions

View File

@ -149,16 +149,19 @@ class ModuleAuthor {
this.face, this.face,
this.mid, this.mid,
this.name, this.name,
this.pubTime,
}); });
String? face; String? face;
int? mid; int? mid;
String? name; String? name;
String? pubTime;
ModuleAuthor.fromJson(Map<String, dynamic> json) { ModuleAuthor.fromJson(Map<String, dynamic> json) {
face = json['face']; face = json['face'];
mid = json['mid']; mid = json['mid'];
name = json['name']; name = json['name'];
pubTime = json['pub_time'];
} }
} }

View File

@ -1,23 +1,31 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/http/read.dart'; import 'package:pilipala/http/read.dart';
import 'package:pilipala/models/read/opus.dart'; import 'package:pilipala/models/read/opus.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart';
import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart';
class OpusController extends GetxController { class OpusController extends GetxController {
late String url; late String url;
late String title; RxString title = ''.obs;
late String id; late String id;
late String articleType; late String articleType;
Rx<OpusDataModel> opusData = OpusDataModel().obs; Rx<OpusDataModel> opusData = OpusDataModel().obs;
final ScrollController scrollController = ScrollController();
final StreamController<bool> appbarStream = StreamController<bool>();
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
title = Get.parameters['title']!; title.value = Get.parameters['title'] ?? '';
id = Get.parameters['id']!; id = Get.parameters['id']!;
articleType = Get.parameters['articleType']!; articleType = Get.parameters['articleType']!;
if (articleType == 'opus') { if (articleType == 'opus') {
url = 'https://www.bilibili.com/opus/$id'; url = 'https://www.bilibili.com/opus/$id';
} }
scrollController.addListener(_scrollListener);
} }
Future fetchOpusData() async { Future fetchOpusData() async {
@ -27,4 +35,57 @@ class OpusController extends GetxController {
} }
return res; return res;
} }
void _scrollListener() {
final double offset = scrollController.position.pixels;
if (offset > 100) {
appbarStream.add(true);
} else {
appbarStream.add(false);
}
}
void onPreviewImg(picList, initIndex, context) {
Navigator.of(context).push(
HeroDialogRoute<void>(
builder: (BuildContext context) => InteractiveviewerGallery(
sources: picList,
initIndex: initIndex,
itemBuilder: (
BuildContext context,
int index,
bool isFocus,
bool enablePageView,
) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (enablePageView) {
Navigator.of(context).pop();
}
},
child: Center(
child: Hero(
tag: picList[index],
child: CachedNetworkImage(
fadeInDuration: const Duration(milliseconds: 0),
imageUrl: picList[index],
fit: BoxFit.contain,
),
),
),
);
},
onPageChanged: (int pageIndex) {},
),
),
);
}
@override
void onClose() {
scrollController.removeListener(_scrollListener);
appbarStream.close();
super.onClose();
}
} }

View File

@ -1,10 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/read/opus.dart'; import 'package:pilipala/models/read/opus.dart';
import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart';
import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart';
import 'controller.dart'; import 'controller.dart';
import 'text_helper.dart'; import 'text_helper.dart';
@ -25,47 +22,43 @@ class _OpusPageState extends State<OpusPage> {
_futureBuilderFuture = controller.fetchOpusData(); _futureBuilderFuture = controller.fetchOpusData();
} }
void onPreviewImg(picList, initIndex, context) { @override
Navigator.of(context).push( Widget build(BuildContext context) {
HeroDialogRoute<void>( return Scaffold(
builder: (BuildContext context) => InteractiveviewerGallery( appBar: _buildAppBar(),
sources: picList, body: SingleChildScrollView(
initIndex: initIndex, controller: controller.scrollController,
itemBuilder: ( child: Column(
BuildContext context, mainAxisAlignment: MainAxisAlignment.start,
int index, crossAxisAlignment: CrossAxisAlignment.start,
bool isFocus, children: [
bool enablePageView, _buildTitle(),
) { _buildFutureContent(),
return GestureDetector( ],
behavior: HitTestBehavior.opaque,
onTap: () {
if (enablePageView) {
Navigator.of(context).pop();
}
},
child: Center(
child: Hero(
tag: picList[index],
child: CachedNetworkImage(
fadeInDuration: const Duration(milliseconds: 0),
imageUrl: picList[index],
fit: BoxFit.contain,
),
),
),
);
},
onPageChanged: (int pageIndex) {},
), ),
), ),
); );
} }
@override AppBar _buildAppBar() {
Widget build(BuildContext context) { return AppBar(
return Scaffold( title: StreamBuilder(
appBar: AppBar( stream: controller.appbarStream.stream.distinct(),
initialData: false,
builder: (BuildContext context, AsyncSnapshot snapshot) {
return AnimatedOpacity(
opacity: snapshot.data ? 1 : 0,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 500),
child: Obx(
() => Text(
controller.title.value,
style: const TextStyle(fontSize: 16),
),
),
);
},
),
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.more_vert_rounded), icon: const Icon(Icons.more_vert_rounded),
@ -73,16 +66,15 @@ class _OpusPageState extends State<OpusPage> {
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
], ],
), );
body: SingleChildScrollView( }
child: Column(
mainAxisAlignment: MainAxisAlignment.start, Widget _buildTitle() {
crossAxisAlignment: CrossAxisAlignment.start, return Padding(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 4), padding: const EdgeInsets.fromLTRB(16, 16, 16, 4),
child: Text( child: Obx(
controller.title, () => Text(
controller.title.value,
style: const TextStyle( style: const TextStyle(
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -91,7 +83,11 @@ class _OpusPageState extends State<OpusPage> {
), ),
), ),
), ),
FutureBuilder( );
}
Widget _buildFutureContent() {
return FutureBuilder(
future: _futureBuilderFuture, future: _futureBuilderFuture,
builder: (BuildContext context, AsyncSnapshot snapshot) { builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
@ -99,18 +95,26 @@ class _OpusPageState extends State<OpusPage> {
return const SizedBox(); return const SizedBox();
} }
if (snapshot.data['status']) { if (snapshot.data['status']) {
final modules = controller.opusData.value.detail!.modules!; return _buildContent(controller.opusData.value);
final ModuleStat moduleStat = modules.last.moduleStat!;
late ModuleContent moduleContent;
final int moduleIndex = modules
.indexWhere((module) => module.moduleContent != null);
if (moduleIndex != -1) {
moduleContent = modules[moduleIndex].moduleContent!;
} else { } else {
print('No moduleContent found'); return _buildError(snapshot.data['message']);
} }
} else {
return _buildLoading();
}
},
);
}
Widget _buildContent(OpusDataModel opusData) {
final modules = opusData.detail!.modules!;
late ModuleContent moduleContent;
// 获取所有的图片链接 // 获取所有的图片链接
final List<String> picList = []; final List<String> picList = [];
final int moduleIndex =
modules.indexWhere((module) => module.moduleContent != null);
if (moduleIndex != -1) {
moduleContent = modules[moduleIndex].moduleContent!;
for (var paragraph in moduleContent.paragraphs!) { for (var paragraph in moduleContent.paragraphs!) {
if (paragraph.paraType == 2) { if (paragraph.paraType == 2) {
for (var pic in paragraph.pic!.pics!) { for (var pic in paragraph.pic!.pics!) {
@ -118,35 +122,24 @@ class _OpusPageState extends State<OpusPage> {
} }
} }
} }
} else {
print('No moduleContent found');
}
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, padding: EdgeInsets.fromLTRB(
MediaQuery.of(context).padding.bottom + 40), 16, 0, 16, MediaQuery.of(context).padding.bottom + 40),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 20), padding: const EdgeInsets.only(bottom: 30),
child: SelectableText.rich( child: _buildStatsWidget(opusData),
TextSpan(
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
fontSize: 12,
letterSpacing: 1,
),
children: [
TextSpan(
text: '${moduleStat.comment!.count}评论'),
const TextSpan(text: ' '),
const TextSpan(text: ' '),
TextSpan(text: '${moduleStat.like!.count}'),
const TextSpan(text: ' '),
const TextSpan(text: ' '),
TextSpan(
text: '${moduleStat.favorite!.count}转发'),
],
),
), ),
Padding(
padding: const EdgeInsets.only(bottom: 20),
child: _buildAuthorWidget(opusData),
), ),
...moduleContent.paragraphs!.map( ...moduleContent.paragraphs!.map(
(ModuleParagraph paragraph) { (ModuleParagraph paragraph) {
@ -154,17 +147,13 @@ class _OpusPageState extends State<OpusPage> {
children: [ children: [
if (paragraph.paraType == 1) ...[ if (paragraph.paraType == 1) ...[
Container( Container(
alignment: TextHelper.getAlignment( alignment: TextHelper.getAlignment(paragraph.align),
paragraph.align),
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10),
child: Text.rich( child: Text.rich(
TextSpan( TextSpan(
children: paragraph.text?.nodes children: paragraph.text?.nodes?.map((node) {
?.map((node) {
return TextHelper.buildTextSpan( return TextHelper.buildTextSpan(
node, node, paragraph.align, context);
paragraph.align,
context);
}).toList() ?? }).toList() ??
[], [],
), ),
@ -174,11 +163,11 @@ class _OpusPageState extends State<OpusPage> {
...paragraph.pic?.pics?.map( ...paragraph.pic?.pics?.map(
(Pic pic) => Center( (Pic pic) => Center(
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding:
top: 10, bottom: 10), const EdgeInsets.only(top: 10, bottom: 10),
child: InkWell( child: InkWell(
onTap: () { onTap: () {
onPreviewImg( controller.onPreviewImg(
picList, picList,
picList.indexOf(pic.url!), picList.indexOf(pic.url!),
context, context,
@ -186,10 +175,8 @@ class _OpusPageState extends State<OpusPage> {
}, },
child: NetworkImgLayer( child: NetworkImgLayer(
src: pic.url, src: pic.url,
width: (Get.size.width - 32) * width: (Get.size.width - 32) * pic.scale!,
pic.scale!, height: (Get.size.width - 32) *
height:
(Get.size.width - 32) *
pic.scale! / pic.scale! /
pic.aspectRatio!, pic.aspectRatio!,
type: 'emote', type: 'emote',
@ -208,16 +195,67 @@ class _OpusPageState extends State<OpusPage> {
], ],
), ),
); );
}
Widget _buildAuthorWidget(OpusDataModel opusData) {
final modules = opusData.detail!.modules!;
late ModuleAuthor moduleAuthor;
final int moduleIndex =
modules.indexWhere((module) => module.moduleAuthor != null);
if (moduleIndex != -1) {
moduleAuthor = modules[moduleIndex].moduleAuthor!;
} else { } else {
// 请求错误 return const SizedBox();
}
return Row(
children: [
NetworkImgLayer(
width: 48,
height: 48,
type: 'avatar',
src: moduleAuthor.face,
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
moduleAuthor.name!,
style: const TextStyle(
fontWeight: FontWeight.w500,
),
),
StyledText(moduleAuthor.pubTime!),
],
),
],
);
}
Widget _buildStatsWidget(OpusDataModel opusData) {
final modules = opusData.detail!.modules!;
final ModuleStat moduleStat = modules.last.moduleStat!;
return Row(
children: [
StyledText('${moduleStat.comment!.count}评论'),
const SizedBox(width: 10),
StyledText('${moduleStat.like!.count}'),
const SizedBox(width: 10),
StyledText('${moduleStat.favorite!.count}转发'),
],
);
}
Widget _buildError(String message) {
return SizedBox( return SizedBox(
height: 100, height: 100,
child: Center( child: Center(
child: Text(snapshot.data['message']), child: Text(message),
), ),
); );
} }
} else {
Widget _buildLoading() {
return const SizedBox( return const SizedBox(
height: 100, height: 100,
child: Center( child: Center(
@ -225,10 +263,20 @@ class _OpusPageState extends State<OpusPage> {
), ),
); );
} }
}, }
)
], class StyledText extends StatelessWidget {
), final String text;
const StyledText(this.text, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
text,
style: TextStyle(
fontSize: 13,
color: Theme.of(context).colorScheme.outline,
), ),
); );
} }