Skip to content
This repository has been archived by the owner on Apr 14, 2024. It is now read-only.

Commit

Permalink
feat: 动态添加表情、功能按钮、评论区、视频播放、回到顶部
Browse files Browse the repository at this point in the history
  • Loading branch information
lucinhu committed Apr 1, 2023
1 parent dd7d538 commit 9e0e35f
Show file tree
Hide file tree
Showing 15 changed files with 430 additions and 81 deletions.
55 changes: 41 additions & 14 deletions lib/common/api/dynamic_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import 'package:bili_you/common/models/local/dynamic/dynamic_content.dart';
import 'package:bili_you/common/models/local/dynamic/dynamic_item.dart';
import 'package:bili_you/common/models/local/dynamic/dynamic_stat.dart';
import 'package:bili_you/common/models/local/reply/official_verify.dart';
import 'package:bili_you/common/models/local/reply/reply_content.dart';
import 'package:bili_you/common/models/local/reply/reply_item.dart';
import 'package:bili_you/common/models/network/dynamic/dynamic.dart' as raw;
import 'package:bili_you/common/utils/index.dart';
import 'package:dio/dio.dart';
Expand Down Expand Up @@ -79,46 +81,71 @@ class DynamicApi {
switch (DynamicItemTypeCode.fromCode(i.type ?? "")) {
case DynamicItemType.word:
//消息
dynamicContent = DynamicContent(
description: moduleDynamic?.desc?.text ?? "", imageUrls: []);
dynamicContent = _buildWordDynamicContent(moduleDynamic);
break;
case DynamicItemType.article:
//文章
dynamicContent = DynamicContent(
description: moduleDynamic?.desc?.text ?? "", imageUrls: []);
dynamicContent = _buildWordDynamicContent(moduleDynamic);
break;
case DynamicItemType.av:
//视频投稿
dynamicContent = DynamicContent(
description: moduleDynamic?.desc?.text ?? "", imageUrls: []);
dynamicContent = AVDynamicContent(
description: moduleDynamic?.desc?.text ?? '',
emotes: _buildEmoteList(
moduleDynamic?.desc?.richTextNodes ?? <raw.RichTextNode>[]),
picUrl: moduleDynamic?.major?.archive?.cover ?? '',
bvid: moduleDynamic?.major?.archive?.bvid ?? '',
title: moduleDynamic?.major?.archive?.title ?? '',
subTitle: moduleDynamic?.major?.archive?.desc ?? '',
duration: moduleDynamic?.major?.archive?.durationText ?? '',
damakuCount: moduleDynamic?.major?.archive?.stat?.danmaku ?? '',
playCount: moduleDynamic?.major?.archive?.stat?.play ?? '');

break;
case DynamicItemType.draw:
//抽奖&互动
dynamicContent = DynamicContent(
description: moduleDynamic?.desc?.text ?? "", imageUrls: []);
dynamicContent = _buildWordDynamicContent(moduleDynamic);
break;
case DynamicItemType.liveRecommend:
//直播推荐
dynamicContent = DynamicContent(
description: moduleDynamic?.desc?.text ?? "", imageUrls: []);
dynamicContent = _buildWordDynamicContent(moduleDynamic);
break;
case DynamicItemType.forward:
//转发
dynamicContent = DynamicContent(
description: moduleDynamic?.desc?.text ?? "", imageUrls: []);
dynamicContent = _buildWordDynamicContent(moduleDynamic);
break;
case DynamicItemType.unkown:
//未知
dynamicContent = DynamicContent(
description: moduleDynamic?.desc?.text ?? "", imageUrls: []);
dynamicContent = _buildWordDynamicContent(moduleDynamic);
break;
}
list.add(DynamicItem(
replyId: i.basic?.commentIdStr ?? '',
replyType: ReplyTypeCode.fromCode(i.basic?.commentType ?? 0),
author: dynamicAuthor,
type: DynamicItemTypeCode.fromCode(i.type ?? ""),
content: dynamicContent,
stat: dynamicStat));
}
return list;
}

static List<Emote> _buildEmoteList(List<raw.RichTextNode> richTextNodes) {
return [
for (var i in richTextNodes)
if (i.emoji != null)
Emote(
text: i.emoji?.text ?? '',
url: i.emoji?.iconUrl ?? '',
size: i.emoji?.size == 2 ? EmoteSize.big : EmoteSize.small)
];
}

static DynamicContent _buildWordDynamicContent(
raw.ModuleDynamic? moduleDynamic) {
return WordDynamicContent(
description: moduleDynamic?.desc?.text ?? "",
emotes: _buildEmoteList(
moduleDynamic?.desc?.richTextNodes ?? <raw.RichTextNode>[]));
}
}
46 changes: 41 additions & 5 deletions lib/common/models/local/dynamic/dynamic_content.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
class DynamicContent {
DynamicContent({required this.description, required this.imageUrls});
static DynamicContent get zero =>
DynamicContent(description: "", imageUrls: []);
import 'package:bili_you/common/models/local/reply/reply_content.dart';

abstract class DynamicContent {
DynamicContent({required this.description, required this.emotes});
String description;
List<String> imageUrls = [];
List<Emote> emotes;
}

class WordDynamicContent extends DynamicContent {
WordDynamicContent({required super.description, required super.emotes});
static WordDynamicContent get zero =>
WordDynamicContent(description: "", emotes: []);
}

class AVDynamicContent extends DynamicContent {
AVDynamicContent(
{required super.description,
required super.emotes,
required this.picUrl,
required this.bvid,
required this.title,
required this.subTitle,
required this.duration,
required this.damakuCount,
required this.playCount});
static AVDynamicContent get zero => AVDynamicContent(
description: "",
emotes: [],
picUrl: '',
title: '',
subTitle: '',
duration: '',
damakuCount: '',
playCount: '',
bvid: '');
final String picUrl;
final String bvid;
final String title;
final String subTitle;
final String duration;
final String playCount;
final String damakuCount;
}
14 changes: 12 additions & 2 deletions lib/common/models/local/dynamic/dynamic_item.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import 'package:bili_you/common/models/local/reply/reply_item.dart';

import 'dynamic_author.dart';
import 'dynamic_content.dart';
import 'dynamic_stat.dart';

class DynamicItem {
DynamicItem(
{required this.author,
{required this.replyId,
required this.replyType,
required this.author,
required this.type,
required this.content,
required this.stat});
static DynamicItem get zero => DynamicItem(
replyId: '',
replyType: ReplyType.unkown,
author: DynamicAuthor.zero,
type: DynamicItemType.unkown,
content: DynamicContent.zero,
content: WordDynamicContent.zero,
stat: DynamicStat.zero);

///评论区id
String replyId;
ReplyType replyType;
DynamicAuthor author;
DynamicItemType type;
DynamicContent content;
Expand Down
1 change: 1 addition & 0 deletions lib/common/values/cache_keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class CacheKeys {
static const String recommendItemCoverKey = 'recommendItemCover';
static const String searchResultItemCoverKey = 'searchResultItemCover';
static const String relatedVideosItemCoverKey = 'relatedVideosItemCover';
static const String dynamicVideoItemCoverKey = 'dynamicVideoItemCover';
static const String emoteKey = 'emote';
static const String replyImageKey = 'replyImage';
}
5 changes: 3 additions & 2 deletions lib/common/widget/foldable_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ class _FoldableTextState extends State<FoldableText> {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) =>
_richText(constraints.maxWidth, constraints.minWidth),
builder: (context, constraints) => (widget.text?.isNotEmpty ?? false)
? _richText(constraints.maxWidth, constraints.minWidth)
: const SizedBox(),
);
}
}
39 changes: 39 additions & 0 deletions lib/common/widget/icon_text_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';

class IconTextButton extends StatelessWidget {
const IconTextButton({
super.key,
required this.onPressed,
required this.icon,
required this.text,
this.selected = false,
});
final Function()? onPressed;
final Icon icon;
final Text text;

///是否被选上
final bool selected;
@override
Widget build(BuildContext context) {
return ElevatedButton(
style: ButtonStyle(
foregroundColor: selected
? MaterialStatePropertyAll(Theme.of(context).colorScheme.onPrimary)
: null,
backgroundColor: selected
? MaterialStatePropertyAll(Theme.of(context).colorScheme.primary)
: null,
elevation: const MaterialStatePropertyAll(0),
padding: const MaterialStatePropertyAll(EdgeInsets.all(5)),
),
onPressed: onPressed ?? () {},
child: FittedBox(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [icon, text],
),
),
);
}
}
4 changes: 3 additions & 1 deletion lib/pages/bili_video/view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:bili_you/common/models/local/reply/reply_item.dart';
import 'package:bili_you/pages/bili_video/widgets/introduction/index.dart';
import 'package:bili_you/pages/bili_video/widgets/reply/view.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -102,7 +103,8 @@ class _BiliVideoPageState extends State<BiliVideoPage> {
Builder(builder: (context) {
//Builder可以让ReplyPage在TabBarView显示到它的时候才取controller.bvid
return ReplyPage(
bvid: controller.bvid,
replyId: controller.bvid,
replyType: ReplyType.video,
pauseVideoCallback:
controller.biliVideoPlayerController.pause,
);
Expand Down
39 changes: 1 addition & 38 deletions lib/pages/bili_video/widgets/introduction/view.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:bili_you/common/utils/string_format_utils.dart';
import 'package:bili_you/common/widget/avatar.dart';
import 'package:bili_you/common/widget/foldable_text.dart';
import 'package:bili_you/common/widget/icon_text_button.dart';
import 'package:bili_you/pages/user_space/view.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
Expand Down Expand Up @@ -375,41 +376,3 @@ class _IntroductionPageState extends State<IntroductionPage>
);
}
}

class IconTextButton extends StatelessWidget {
const IconTextButton({
super.key,
required this.onPressed,
required this.icon,
required this.text,
this.selected = false,
});
final Function()? onPressed;
final Icon icon;
final Text text;

///是否被选上
final bool selected;
@override
Widget build(BuildContext context) {
return ElevatedButton(
style: ButtonStyle(
foregroundColor: selected
? MaterialStatePropertyAll(Theme.of(context).colorScheme.onPrimary)
: null,
backgroundColor: selected
? MaterialStatePropertyAll(Theme.of(context).colorScheme.primary)
: null,
elevation: const MaterialStatePropertyAll(0),
padding: const MaterialStatePropertyAll(EdgeInsets.all(5)),
),
onPressed: onPressed ?? () {},
child: FittedBox(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [icon, text],
),
),
);
}
}
10 changes: 7 additions & 3 deletions lib/pages/bili_video/widgets/reply/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ import 'package:get/get.dart';
import 'package:bili_you/pages/bili_video/widgets/reply/widgets/reply_item.dart';

class ReplyController extends GetxController {
ReplyController({required this.bvid, required this.pauseVideoCallback});
ReplyController(
{required this.bvid,
required this.replyType,
required this.pauseVideoCallback});
String bvid;
EasyRefreshController refreshController = EasyRefreshController(
controlFinishLoad: true, controlFinishRefresh: true);
ScrollController scrollController = ScrollController();
List<Widget> replyList = <Widget>[];
int pageNum = 1;
final ReplyType replyType;
RxString sortTypeText = "按热度".obs;
RxString sortInfoText = "热门评论".obs;
ReplySort _replySort = ReplySort.like;
final Function() pauseVideoCallback;
final Function()? pauseVideoCallback;

//切换排列方式
void toggleSort() {
Expand Down Expand Up @@ -65,7 +69,7 @@ class ReplyController extends GetxController {
late ReplyInfo replyInfo;
try {
replyInfo = await ReplyApi.getReply(
oid: bvid, pageNum: pageNum, type: ReplyType.video, sort: _replySort);
oid: bvid, pageNum: pageNum, type: replyType, sort: _replySort);
} catch (e) {
log("评论区加载失败,_addReplyItems:$e");
return false;
Expand Down
17 changes: 11 additions & 6 deletions lib/pages/bili_video/widgets/reply/view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:bili_you/common/models/local/reply/reply_item.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
Expand All @@ -7,13 +8,15 @@ import 'index.dart';
class ReplyPage extends StatefulWidget {
const ReplyPage({
Key? key,
required this.bvid,
required this.pauseVideoCallback,
}) : tag = "ReplyPage:$bvid",
required this.replyId,
required this.replyType,
this.pauseVideoCallback,
}) : tag = "ReplyPage:$replyId",
super(key: key);
final String bvid;
final String replyId;
final ReplyType replyType;
final String tag;
final Function() pauseVideoCallback;
final Function()? pauseVideoCallback;

@override
State<ReplyPage> createState() => _ReplyPageState();
Expand Down Expand Up @@ -62,7 +65,9 @@ class _ReplyPageState extends State<ReplyPage>
super.build(context);
return GetBuilder(
init: ReplyController(
bvid: widget.bvid, pauseVideoCallback: widget.pauseVideoCallback),
bvid: widget.replyId,
replyType: widget.replyType,
pauseVideoCallback: widget.pauseVideoCallback),
tag: widget.tag,
builder: (controller) => _buildView(controller),
id: 'reply',
Expand Down
6 changes: 6 additions & 0 deletions lib/pages/dynamic/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:bili_you/common/api/dynamic_api.dart';
import 'package:bili_you/common/models/local/dynamic/dynamic_item.dart';
import 'package:bili_you/pages/dynamic/widget/dynamic_item_card.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class DynamicController extends GetxController {
Expand All @@ -15,6 +16,11 @@ class DynamicController extends GetxController {
// }
int currentPage = 1;
List<DynamicItemCard> dynamicItemCards = [];
ScrollController scrollController = ScrollController();
void animateToTop() {
scrollController.animateTo(0,
duration: const Duration(milliseconds: 200), curve: Curves.linear);
}

Future<bool> _loadDynamicItemCards() async {
late List<DynamicItem> items;
Expand Down
2 changes: 2 additions & 0 deletions lib/pages/dynamic/view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class DynamicPage extends GetView<DynamicController> {
),
controller: controller.refreshController,
childBuilder: (context, physics) => ListView.builder(
cacheExtent: MediaQuery.of(context).size.height,
controller: controller.scrollController,
physics: physics,
itemCount: controller.dynamicItemCards.length,
itemBuilder: (context, index) =>
Expand Down
Loading

0 comments on commit 9e0e35f

Please sign in to comment.