mod: 直播样式&组件

This commit is contained in:
guozhigq
2023-06-23 15:45:10 +08:00
parent fccd21e8b6
commit 4b1ddae3d7
8 changed files with 340 additions and 38 deletions

View File

@ -139,15 +139,36 @@ class VideoContent extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
videoItem.title,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
fontWeight: FontWeight.w500),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
if (videoItem.title is String) ...[
Text(
videoItem.title,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
fontWeight: FontWeight.w500),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
] else ...[
RichText(
text: TextSpan(
children: [
for (var i in videoItem.title) ...[
TextSpan(
text: i['text'],
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: i['type'] == 'em'
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
),
]
],
),
),
],
const Spacer(),
if (videoItem.rcmdReason != null &&
videoItem.rcmdReason.content != '')

View File

@ -1,3 +1,5 @@
import 'package:pilipala/utils/em.dart';
class SearchVideoModel {
SearchVideoModel({this.list});
List<SearchVideoItemModel>? list;
@ -50,7 +52,7 @@ class SearchVideoItemModel {
String? arcurl;
int? aid;
String? bvid;
String? title;
List? title;
String? description;
String? pic;
// String? play;
@ -76,7 +78,7 @@ class SearchVideoItemModel {
arcurl = json['arcurl'];
aid = json['aid'];
bvid = json['bvid'];
title = json['title'].replaceAll(RegExp(r'<.*?>'), '');
title = Em.regTitle(json['title']);
description = json['description'];
pic = 'https:${json['pic']}';
videoReview = json['video_review'];
@ -241,6 +243,7 @@ class SearchLiveItemModel {
this.rankScore,
this.roomid,
this.attentions,
this.cateName,
});
int? rankOffset;
@ -251,13 +254,14 @@ class SearchLiveItemModel {
String? uface;
String? userCover;
String? type;
String? title;
List? title;
String? cover;
int? online;
int? rankIndex;
int? rankScore;
int? roomid;
int? attentions;
String? cateName;
SearchLiveItemModel.fromJson(Map<String, dynamic> json) {
rankOffset = json['rank_offset'];
@ -268,12 +272,13 @@ class SearchLiveItemModel {
uface = json['uface'];
userCover = json['user_cover'];
type = json['type'];
title = json['title'];
title = Em.regTitle(json['title']);
cover = json['cover'];
online = json['online'];
rankIndex = json['rank_index'];
rankScore = json['rank_score'];
roomid = json['roomid'];
attentions = json['attentions'];
cateName = Em.regCate(json['cate_name']) ?? '';
}
}

View File

@ -7,6 +7,9 @@ import 'package:pilipala/common/widgets/video_card_h.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'controller.dart';
import 'widgets/live_panerl.dart';
import 'widgets/user_panel.dart';
import 'widgets/video_panel.dart';
import 'widgets/userPanel.dart';
class SearchPanel extends StatefulWidget {
@ -63,28 +66,21 @@ class _SearchPanelState extends State<SearchPanel>
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data;
var ctr = _searchPanelController;
List list = ctr!.resultList;
if (data['status']) {
return Obx(
() => ListView.builder(
controller: _searchPanelController!.scrollController,
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
itemCount: _searchPanelController!.resultList.length,
itemBuilder: (context, index) {
var i = _searchPanelController!.resultList[index];
switch (widget.searchType) {
case SearchType.video:
return VideoCardH(videoItem: i);
case SearchType.bili_user:
return UserPanel(userItem: i);
case SearchType.live_room:
return LiveCard(liveItem: i);
default:
return const SizedBox();
}
},
),
);
return Obx(() {
switch (widget.searchType) {
case SearchType.video:
return searchVideoPanel(context, ctr, list);
case SearchType.bili_user:
return searchUserPanel(context, ctr, list);
case SearchType.live_room:
return searchLivePanel(context, ctr, list);
default:
return const SizedBox();
}
});
} else {
return CustomScrollView(
physics: const NeverScrollableScrollPhysics(),

View File

@ -0,0 +1,169 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/utils/utils.dart';
Widget searchLivePanel(BuildContext context, ctr, list) {
return Padding(
padding: const EdgeInsets.only(
left: StyleString.cardSpace, right: StyleString.cardSpace),
child: GridView.builder(
primary: false,
controller: ctr!.scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: StyleString.cardSpace,
mainAxisSpacing: StyleString.cardSpace,
mainAxisExtent:
MediaQuery.of(context).size.width / 2 / StyleString.aspectRatio +
65,
),
itemCount: list.length,
itemBuilder: (context, index) {
var i = list![index];
return Card(
elevation: 0.8,
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: StyleString.mdRadius,
),
margin: EdgeInsets.zero,
child: InkWell(
onTap: () {},
child: Column(
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
double PR = MediaQuery.of(context).devicePixelRatio;
return Stack(
children: [
Hero(
tag: Utils.makeHeroTag(i.roomid),
child: NetworkImgLayer(
// 指定图片尺寸
// src: videoItem.pic + '@${(maxWidth * 2).toInt()}w',
src: i.cover + '@.webp',
type: 'emote',
width: maxWidth,
height: maxHeight,
),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: AnimatedOpacity(
opacity: 1,
duration: const Duration(milliseconds: 200),
child: LiveStat(
online: i.online,
cateName: i.cateName,
),
),
),
],
);
}),
),
LiveContent(liveItem: i)
],
),
),
);
},
),
);
}
class LiveContent extends StatelessWidget {
final liveItem;
const LiveContent({Key? key, required this.liveItem}) : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 6, 7),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RichText(
text: TextSpan(
children: [
for (var i in liveItem.title) ...[
TextSpan(
text: i['text'],
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: i['type'] == 'em'
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
),
]
],
),
),
SizedBox(
width: double.infinity,
child: Text(
liveItem.uname,
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
),
],
),
),
);
}
}
class LiveStat extends StatelessWidget {
final int? online;
final String? cateName;
const LiveStat({Key? key, required this.online, this.cateName})
: super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 45,
padding: const EdgeInsets.only(top: 22, left: 8, right: 8),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Colors.transparent,
Colors.black54,
],
tileMode: TileMode.mirror,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
cateName!,
style: const TextStyle(fontSize: 11, color: Colors.white),
),
Text(
'围观:${online.toString()}',
style: const TextStyle(fontSize: 11, color: Colors.white),
)
],
),
);
}
}

View File

@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
Widget searchUserPanel(BuildContext context, ctr, list) {
TextStyle style = TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
color: Theme.of(context).colorScheme.outline);
return ListView.builder(
controller: ctr!.scrollController,
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
itemCount: list!.length,
itemBuilder: (context, index) {
var i = list![index];
return InkWell(
onTap: () {},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Row(
children: [
NetworkImgLayer(
width: 42,
height: 42,
src: i.upic,
type: 'avatar',
),
const SizedBox(width: 10),
Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Text(
i!.uname,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.titleMedium!.fontSize,
),
),
const SizedBox(width: 6),
Image.asset(
'assets/images/lv/lv${i!.level}.png',
height: 11,
),
],
),
Row(
children: [
Text('粉丝:${i.fans} ', style: style),
Text(' 视频:${i.videos}', style: style)
],
),
if (i.officialVerify['desc'] != '')
Text(
i.officialVerify['desc'],
style: style,
),
],
)
],
),
),
);
;
},
);
}

View File

@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
Widget searchVideoPanel(BuildContext context, ctr, list) {
return ListView.builder(
controller: ctr!.scrollController,
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
itemCount: list!.length,
itemBuilder: (context, index) {
var i = list![index];
return VideoCardH(videoItem: i);
},
);
}

View File

@ -52,6 +52,7 @@ class _SearchResultPageState extends State<SearchResultPage>
),
body: Column(
children: [
const SizedBox(height: 4),
Theme(
data: ThemeData(
splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明
@ -68,9 +69,7 @@ class _SearchResultPageState extends State<SearchResultPage>
const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
indicator: BoxDecoration(
color: Theme.of(context).colorScheme.secondaryContainer,
borderRadius: const BorderRadius.all(
Radius.circular(16),
),
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
indicatorSize: TabBarIndicatorSize.tab,
labelColor: Theme.of(context).colorScheme.onSecondaryContainer,
@ -87,7 +86,6 @@ class _SearchResultPageState extends State<SearchResultPage>
},
),
),
const SizedBox(height: 4),
Expanded(
child: TabBarView(
controller: _tabController,

29
lib/utils/em.dart Normal file
View File

@ -0,0 +1,29 @@
class Em {
static regCate(String origin) {
String str = origin;
RegExp exp = RegExp('<[^>]*>([^<]*)</[^>]*>');
Iterable<Match> matches = exp.allMatches(origin);
for (Match match in matches) {
str = match.group(1)!;
}
return str;
}
static regTitle(String origin) {
RegExp exp = RegExp('<[^>]*>([^<]*)</[^>]*>');
List res = [];
origin.splitMapJoin(exp, onMatch: (Match match) {
String matchStr = match[0]!;
Map map = {'type': 'em', 'text': regCate(matchStr)};
res.add(map);
return regCate(matchStr);
}, onNonMatch: (String str) {
if (str != '') {
Map map = {'type': 'text', 'text': str};
res.add(map);
}
return str;
});
return res;
}
}