diff --git a/assets/fonts/ArchivoNarrow-BoldItalic.ttf b/assets/fonts/ArchivoNarrow-BoldItalic.ttf new file mode 100644 index 00000000..e69fb6a5 Binary files /dev/null and b/assets/fonts/ArchivoNarrow-BoldItalic.ttf differ diff --git a/lib/common/skeleton/video_card_h.dart b/lib/common/skeleton/video_card_h.dart index dc5e9c32..46b858d3 100644 --- a/lib/common/skeleton/video_card_h.dart +++ b/lib/common/skeleton/video_card_h.dart @@ -59,7 +59,7 @@ class _VideoCardHSkeletonState extends State { .colorScheme .onInverseSurface, width: 200, - height: 13, + height: 11, margin: const EdgeInsets.only(bottom: 5), ), Container( diff --git a/lib/common/skeleton/video_card_v.dart b/lib/common/skeleton/video_card_v.dart index 4f83501e..aeff595f 100644 --- a/lib/common/skeleton/video_card_v.dart +++ b/lib/common/skeleton/video_card_v.dart @@ -25,12 +25,6 @@ class VideoCardVSkeleton extends StatelessWidget { decoration: BoxDecoration( color: Theme.of(context).colorScheme.background, borderRadius: BorderRadius.circular(6), - border: Border.all( - color: Theme.of(context) - .colorScheme - .outline - .withOpacity(0.1), - ), ), ); }, diff --git a/lib/common/skeleton/video_reply.dart b/lib/common/skeleton/video_reply.dart index 09efa8ce..219540eb 100644 --- a/lib/common/skeleton/video_reply.dart +++ b/lib/common/skeleton/video_reply.dart @@ -23,8 +23,8 @@ class VideoReplySkeleton extends StatelessWidget { ), const SizedBox(width: 12), Container( - width: 120, - height: 16, + width: 80, + height: 13, color: bgColor, ) ], @@ -40,28 +40,28 @@ class VideoReplySkeleton extends StatelessWidget { children: [ Container( width: 300, - height: 16, + height: 14, margin: const EdgeInsets.only(bottom: 4), color: bgColor, ), Container( width: 180, - height: 16, - margin: const EdgeInsets.only(bottom: 6), + height: 14, + margin: const EdgeInsets.only(bottom: 10), color: bgColor, ), Row( children: [ Container( - width: 60, - height: 16, + width: 40, + height: 14, margin: const EdgeInsets.only(bottom: 4), color: bgColor, ), const Spacer(), Container( - width: 60, - height: 16, + width: 40, + height: 14, margin: const EdgeInsets.only(bottom: 4), color: bgColor, ), diff --git a/lib/main.dart b/lib/main.dart index b3eb8796..08059adf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,7 +27,11 @@ class MyApp extends StatelessWidget { ColorScheme.fromSeed( seedColor: Colors.green, brightness: Brightness.light), useMaterial3: true), - darkTheme: ThemeData(colorScheme: darkDynamic, useMaterial3: true), + darkTheme: ThemeData( + colorScheme: darkDynamic ?? + ColorScheme.fromSeed( + seedColor: Colors.green, brightness: Brightness.dark), + useMaterial3: true), getPages: Routes.getPages, home: const MainApp(), // home: const Scaffold(), diff --git a/lib/models/video/reply/content.dart b/lib/models/video/reply/content.dart index 42ccaded..97897f75 100644 --- a/lib/models/video/reply/content.dart +++ b/lib/models/video/reply/content.dart @@ -6,6 +6,7 @@ class ReplyContent { this.emote, // 表情包 如果有的话 null this.jumpUrl, // {} this.pictures, // {} + this.vote, }); String? message; @@ -14,6 +15,7 @@ class ReplyContent { Map? emote; Map? jumpUrl; List? pictures; + Map? vote; ReplyContent.fromJson(Map json) { message = json['message']; @@ -22,5 +24,6 @@ class ReplyContent { emote = json['emote'] ?? {}; jumpUrl = json['jump_url'] ?? {}; pictures = json['pictures'] ?? []; + vote = json['vote'] ?? {}; } } diff --git a/lib/pages/home/widgets/app_bar.dart b/lib/pages/home/widgets/app_bar.dart index 56a53861..eb62ece2 100644 --- a/lib/pages/home/widgets/app_bar.dart +++ b/lib/pages/home/widgets/app_bar.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; class HomeAppBar extends StatelessWidget { const HomeAppBar({super.key}); @@ -29,19 +30,26 @@ class HomeAppBar extends StatelessWidget { title: const Text( 'PiLiPaLa', style: TextStyle( - fontSize: 18, + fontSize: 19, fontWeight: FontWeight.bold, letterSpacing: 1, + fontFamily: 'ArchivoNarrow', ), ), actions: [ IconButton( onPressed: () {}, - icon: const Icon(Icons.notifications_none_rounded), + icon: const FaIcon( + FontAwesomeIcons.magnifyingGlass, + size: 18, + ), ), IconButton( onPressed: () {}, - icon: const Icon(Icons.search_rounded), + icon: const FaIcon( + FontAwesomeIcons.envelope, + size: 20, + ), ), const SizedBox(width: 10) ], diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 0b7556f4..89b952c0 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -19,7 +19,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { late AnimationController? _animationController; late Animation? _fadeAnimation; late Animation? _slideAnimation; - int selectedIndex = 0; + int selectedIndex = 2; int? _lastSelectTime; //上次点击时间 @override diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index fd455db0..c2ae6532 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:pilipala/common/constants.dart'; class MinePage extends StatefulWidget { const MinePage({super.key}); @@ -12,7 +14,187 @@ class _MinePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('我的'), + title: null, + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon(Icons.light_mode_rounded), + ), + IconButton( + onPressed: () {}, + icon: const FaIcon( + FontAwesomeIcons.sliders, + size: 18, + ), + ), + const SizedBox(width: 10), + ], + ), + body: Padding( + padding: const EdgeInsets.only(left: 12, right: 12), + child: Column( + children: [ + Row( + children: [ + const SizedBox(width: 20), + ClipOval( + child: Container( + width: 75, + height: 75, + color: Theme.of(context).colorScheme.onInverseSurface, + child: Center( + child: Image.asset('assets/images/loading.png'), + ), + ), + ), + const SizedBox(width: 14), + Text( + '点击登录', + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + const SizedBox(height: 20), + LayoutBuilder( + builder: (context, constraints) { + TextStyle style = TextStyle( + fontSize: Theme.of(context).textTheme.titleMedium!.fontSize, + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold); + return SizedBox( + height: constraints.maxWidth / 3 * 0.6, + child: GridView.count( + primary: false, + padding: const EdgeInsets.all(0), + crossAxisCount: 3, + childAspectRatio: 1.67, + children: [ + InkWell( + onTap: () {}, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('-', style: style), + const SizedBox(height: 8), + Text( + '动态', + style: Theme.of(context).textTheme.labelMedium, + ), + ], + ), + ), + InkWell( + onTap: () {}, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '50', + style: style, + ), + const SizedBox(height: 8), + Text( + '关注', + style: Theme.of(context).textTheme.labelMedium, + ), + ], + ), + ), + InkWell( + onTap: () {}, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '-', + style: style, + ), + const SizedBox(height: 8), + Text( + '粉丝', + style: Theme.of(context).textTheme.labelMedium, + ), + ], + ), + ), + ], + ), + ); + }, + ), + const SizedBox(height: 20), + LayoutBuilder( + builder: (context, constraints) { + return SizedBox( + height: constraints.maxWidth / 4 * 0.8, + child: GridView.count( + primary: false, + padding: const EdgeInsets.all(0), + crossAxisCount: 4, + childAspectRatio: 1.25, + children: [ + ActionItem( + icon: const Icon(FontAwesomeIcons.download), + onTap: () => {}, + text: '离线缓存', + ), + ActionItem( + icon: const Icon(FontAwesomeIcons.clockRotateLeft), + onTap: () => {}, + text: '历史记录', + ), + ActionItem( + icon: const Icon(FontAwesomeIcons.star), + onTap: () => {}, + text: '我的收藏', + ), + ActionItem( + icon: const Icon(FontAwesomeIcons.film), + onTap: () => {}, + text: '稍后再看', + ), + ], + ), + ); + }, + ), + ], + ), + ), + ); + } +} + +class ActionItem extends StatelessWidget { + Icon? icon; + Function? onTap; + String? text; + + ActionItem({ + Key? key, + this.icon, + this.onTap, + this.text, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () {}, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon!.icon!), + const SizedBox(height: 8), + Text( + text!, + style: Theme.of(context).textTheme.labelMedium, + ), + ], ), ); } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 52a6efb0..8aa0d570 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,4 @@ +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/constants.dart'; @@ -114,116 +115,29 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { return SliverPadding( - padding: const EdgeInsets.only(left: 12, right: 12, top: 25), + padding: const EdgeInsets.only(left: 12, right: 12, top: 20), sliver: SliverToBoxAdapter( child: !widget.loadingStatus || videoItem.isNotEmpty ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - NetworkImgLayer( - type: 'avatar', - src: !widget.loadingStatus - ? widget.videoDetail!.owner!.face - : videoItem['owner'].face, - width: 38, - height: 38, - fadeInDuration: Duration.zero, - fadeOutDuration: Duration.zero, - ), - const SizedBox(width: 14), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(!widget.loadingStatus - ? widget.videoDetail!.owner!.name - : videoItem['owner'].name), - const SizedBox(height: 2), - Text( - widget.loadingStatus - ? '- 粉丝' - : '${Utils.numFormat(widget.videoIntroController!.userStat['follower'])}粉丝', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelSmall! - .fontSize, - color: Theme.of(context).colorScheme.outline), - ) - ]), - const Spacer(), - AnimatedOpacity( - opacity: widget.loadingStatus ? 0 : 1, - duration: const Duration(milliseconds: 150), - child: SizedBox( - height: 35, - child: ElevatedButton( - onPressed: () {}, child: const Text('+ 关注')), - ), - ), - const SizedBox(width: 4), - ], + SelectableRegion( + magnifierConfiguration: const TextMagnifierConfiguration(), + focusNode: FocusNode(), + selectionControls: MaterialTextSelectionControls(), + child: Text( + !widget.loadingStatus + ? widget.videoDetail!.title + : videoItem['title'], + style: Theme.of(context).textTheme.titleMedium!.copyWith( + letterSpacing: 0.5, + ), + ), ), - const SizedBox(height: 13), - // 标题 超过两行收起 - // Container( - // color: Colors.blue[50], - // child: SizedOverflowBox( - // size: const Size(50.0, 50.0), - // alignment: AlignmentDirectional.bottomStart, - // child: Container(height: 150.0, width: 150.0, color: Colors.blue,), - // ), - // ), - // Row( - // children: [ - // Expanded( - // child: ExpandedSection( - // expand: false, - // begin: 1, - // end: 1, - // child: Text( - // !widget.loadingStatus - // ? widget.videoDetail!.title - // : videoItem['title'], - // overflow: TextOverflow.ellipsis, - // maxLines: 1, - // ), - // ), - // ), - // const SizedBox(width: 10), - // RotationTransition( - // turns: _manualAnimation!, - // child: IconButton( - // onPressed: () { - // /// 获取动画当前的值 - // var value = _manualController!.value; - - // /// 0.5代表 180弧度 - // if (value == 0) { - // _manualController!.animateTo(0.5); - // } else { - // _manualController!.animateTo(0); - // } - // setState(() { - // isExpand = !isExpand; - // }); - // }, - // icon: const Icon(Icons.expand_less)), - // ), - // ], - // ), - - Text( - !widget.loadingStatus - ? widget.videoDetail!.title - : videoItem['title'], - ), - // const SizedBox(height: 5), - // 播放量、评论、日期 - InkWell( splashColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, onTap: () { _manualController!.animateTo(isExpand ? 0 : 0.5); setState(() { @@ -276,8 +190,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { }); }, icon: Icon( - Icons.expand_less, - size: 22, + FontAwesomeIcons.angleUp, + size: 15, color: Theme.of(context).colorScheme.outline, ), ), @@ -287,8 +201,65 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ], ), ), - - // const SizedBox(height: 5), + const SizedBox(height: 12), + Row( + children: [ + NetworkImgLayer( + type: 'avatar', + src: !widget.loadingStatus + ? widget.videoDetail!.owner!.face + : videoItem['owner'].face, + width: 38, + height: 38, + fadeInDuration: Duration.zero, + fadeOutDuration: Duration.zero, + ), + const SizedBox(width: 14), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(!widget.loadingStatus + ? widget.videoDetail!.owner!.name + : videoItem['owner'].name), + // const SizedBox(width: 10), + Text( + widget.loadingStatus + ? '- 粉丝' + : '${Utils.numFormat(widget.videoIntroController!.userStat['follower'])}粉丝', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize, + color: Theme.of(context).colorScheme.outline), + ), + ], + ), + const Spacer(), + AnimatedOpacity( + opacity: widget.loadingStatus ? 0 : 1, + duration: const Duration(milliseconds: 150), + child: SizedBox( + height: 36, + child: ElevatedButton( + onPressed: () {}, + child: Row( + children: const [ + Icon( + FontAwesomeIcons.lemon, + size: 17, + ), + SizedBox(width: 4), + Text('关注'), + ], + ), + ), + ), + ), + const SizedBox(width: 4), + ], + ), + const SizedBox(height: 10), // 简介 默认收起 if (!widget.loadingStatus) ExpandedSection( @@ -320,8 +291,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ), + const SizedBox(height: 5), _actionGrid(context), - // const SizedBox(height: 5), ], ) : const Center(child: CircularProgressIndicator()), @@ -333,14 +304,15 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget _actionGrid(BuildContext context) { return LayoutBuilder(builder: (context, constraints) { return SizedBox( - height: constraints.maxWidth / 5, + height: constraints.maxWidth / 5 * 0.8, child: GridView.count( primary: false, padding: const EdgeInsets.all(0), crossAxisCount: 5, + childAspectRatio: 1.25, children: [ ActionItem( - icon: const Icon(Icons.thumb_up), + icon: const Icon(FontAwesomeIcons.thumbsUp), onTap: () => {}, selectStatus: false, loadingStatus: widget.loadingStatus, @@ -348,13 +320,13 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? widget.videoDetail!.stat!.like!.toString() : '-'), ActionItem( - icon: const Icon(Icons.thumb_down), + icon: const Icon(FontAwesomeIcons.thumbsDown), onTap: () => {}, selectStatus: false, loadingStatus: widget.loadingStatus, text: '不喜欢'), ActionItem( - icon: const Icon(Icons.generating_tokens), + icon: const Icon(FontAwesomeIcons.b), onTap: () => {}, selectStatus: false, loadingStatus: widget.loadingStatus, @@ -362,7 +334,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? widget.videoDetail!.stat!.coin!.toString() : '-'), ActionItem( - icon: const Icon(Icons.star), + icon: const Icon( + FontAwesomeIcons.heart, + size: 17, + ), onTap: () => {}, selectStatus: false, loadingStatus: widget.loadingStatus, @@ -370,7 +345,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? widget.videoDetail!.stat!.favorite!.toString() : '-'), ActionItem( - icon: const Icon(Icons.share), + icon: const Icon(FontAwesomeIcons.shareFromSquare), onTap: () => {}, selectStatus: false, loadingStatus: widget.loadingStatus, @@ -403,33 +378,35 @@ class ActionItem extends StatelessWidget { @override Widget build(BuildContext context) { return Material( - child: Ink( - child: InkWell( - onTap: () {}, - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(icon!.icon!, - color: selectStatus - ? Theme.of(context).primaryColor - : Theme.of(context).colorScheme.outline), - const SizedBox(height: 2), - AnimatedOpacity( - opacity: loadingStatus! ? 0 : 1, - duration: const Duration(milliseconds: 200), - child: Text( - text!, - style: TextStyle( - color: selectStatus - ? Theme.of(context).primaryColor - : Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context).textTheme.labelSmall?.fontSize), + child: Ink( + child: InkWell( + onTap: () {}, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon!.icon!, + color: selectStatus + ? Theme.of(context).primaryColor + : Theme.of(context).colorScheme.outline), + const SizedBox(height: 2), + AnimatedOpacity( + opacity: loadingStatus! ? 0 : 1, + duration: const Duration(milliseconds: 200), + child: Text( + text!, + style: TextStyle( + color: selectStatus + ? Theme.of(context).primaryColor + : Theme.of(context).colorScheme.outline, + fontSize: + Theme.of(context).textTheme.labelSmall?.fontSize), + ), ), - ), - ], + ], + ), ), ), - )); + ); } } diff --git a/lib/pages/video/detail/related/view.dart b/lib/pages/video/detail/related/view.dart index 4034cdfb..3c79ee50 100644 --- a/lib/pages/video/detail/related/view.dart +++ b/lib/pages/video/detail/related/view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; +import 'package:pilipala/common/widgets/video_card_v.dart'; import './controller.dart'; class RelatedVideoPanel extends StatefulWidget { diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 73bafe2f..3885d8f1 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,5 +1,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/video/reply/item.dart'; @@ -289,45 +290,46 @@ class ReplyItemRow extends StatelessWidget { ); } else { return InkWell( - onTap: () {}, - child: Padding( - padding: EdgeInsets.fromLTRB( - 8, - index == 0 && (extraRow == 1 || replies!.length > 1) - ? 8 - : 5, - 8, - 5), - child: Text.rich( - overflow: extraRow == 1 - ? TextOverflow.ellipsis - : TextOverflow.visible, - maxLines: extraRow == 1 ? 2 : null, - TextSpan( - children: [ - if (replies![index].isUp) - WidgetSpan( - child: UpTag(), - ), - TextSpan( - text: replies![index].member.uname + ' ', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .titleSmall! - .fontSize, - color: Theme.of(context).colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () => { - print('跳转至用户主页'), - }, + onTap: () {}, + child: Padding( + padding: EdgeInsets.fromLTRB( + 8, + index == 0 && (extraRow == 1 || replies!.length > 1) + ? 8 + : 5, + 8, + 5), + child: Text.rich( + overflow: extraRow == 1 + ? TextOverflow.ellipsis + : TextOverflow.visible, + maxLines: extraRow == 1 ? 2 : null, + TextSpan( + children: [ + if (replies![index].isUp) + WidgetSpan( + child: UpTag(), ), - buildContent(context, replies![index].content), - ], - ), + TextSpan( + text: replies![index].member.uname + ' ', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .titleSmall! + .fontSize, + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () => { + print('跳转至用户主页'), + }, + ), + buildContent(context, replies![index].content), + ], ), - )); + ), + ), + ); } }, ), @@ -382,6 +384,7 @@ InlineSpan buildContent(BuildContext context, content) { if (content.emote.isEmpty && content.atNameToMid.isEmpty && content.jumpUrl.isEmpty && + content.vote.isEmpty && content.pictures.isEmpty) { return TextSpan(text: content.message); } @@ -416,7 +419,7 @@ InlineSpan buildContent(BuildContext context, content) { String matchMember = str; if (content.atNameToMid.isNotEmpty) { matchMember = str.splitMapJoin( - RegExp(r"@.*:"), + RegExp(r"@.*( |:)"), onMatch: (Match match) { if (match[0] != null) { content.atNameToMid.forEach((key, value) { @@ -455,7 +458,6 @@ InlineSpan buildContent(BuildContext context, content) { RegExp("(?:${urlKeys.join("|")})"), onMatch: (Match match) { String matchStr = match[0]!; - // spanChilds.add(TextSpan(text: matchStr)); spanChilds.add( TextSpan( text: content.jumpUrl[matchStr]['title'], @@ -468,6 +470,16 @@ InlineSpan buildContent(BuildContext context, content) { }, ), ); + spanChilds.add( + WidgetSpan( + child: Icon( + FontAwesomeIcons.magnifyingGlass, + size: 9, + color: Theme.of(context).colorScheme.primary, + ), + alignment: PlaceholderAlignment.top, + ), + ); return ''; }, onNonMatch: (String str) { @@ -477,6 +489,29 @@ InlineSpan buildContent(BuildContext context, content) { ); } + str = matchUrl.splitMapJoin( + RegExp(r"\d{1,2}:\d{1,2}"), + onMatch: (Match match) { + String matchStr = match[0]!; + spanChilds.add( + TextSpan( + text: ' $matchStr ', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () => { + print('time 点击'), + }, + ), + ); + return ''; + }, + onNonMatch: (str) { + return str; + }, + ); + if (content.atNameToMid.isEmpty && content.jumpUrl.isEmpty) { spanChilds.add(TextSpan(text: str)); } @@ -486,66 +521,98 @@ InlineSpan buildContent(BuildContext context, content) { // 图片渲染 if (content.pictures.isNotEmpty) { - List list = []; List picList = []; int len = content.pictures.length; - for (var i = 0; i < len; i++) { - picList.add(content.pictures[i]['img_src']); - list.add( - LayoutBuilder( - builder: (context, BoxConstraints box) { - return GestureDetector( - onTap: () { - Get.toNamed('/preview', - arguments: {'initialPage': i, 'imgList': picList}); - }, - child: NetworkImgLayer( - src: content.pictures[i]['img_src'], - width: box.maxWidth, - height: box.maxWidth, - ), - ); - }, + if (len == 1) { + Map pictureItem = content.pictures.first; + picList.add(pictureItem['img_src']); + spanChilds.add(const TextSpan(text: '\n')); + spanChilds.add( + WidgetSpan( + child: LayoutBuilder( + builder: (context, BoxConstraints box) { + return GestureDetector( + onTap: () { + Get.toNamed('/preview', + arguments: {'initialPage': 0, 'imgList': picList}); + }, + child: Padding( + padding: EdgeInsets.only(top: 4), + child: NetworkImgLayer( + src: pictureItem['img_src'], + width: box.maxWidth / 2, + height: box.maxWidth * + 0.5 * + pictureItem['img_height'] / + pictureItem['img_width'], + ), + ), + ); + }, + ), ), ); } - spanChilds.add( - WidgetSpan( - child: LayoutBuilder( - builder: (context, BoxConstraints box) { - double maxWidth = box.maxWidth; - double crossCount = len < 3 ? 2 : 3; - double height = maxWidth / - crossCount * - (len % crossCount == 0 - ? len ~/ crossCount - : len ~/ crossCount + 1) + - 6; - return Container( - padding: const EdgeInsets.only(top: 6), - height: height, - child: GridView( - padding: EdgeInsets.zero, - physics: const NeverScrollableScrollPhysics(), - // 子Item排列规则 - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - //横轴元素个数 - crossAxisCount: crossCount.toInt(), - //纵轴间距 - mainAxisSpacing: 4.0, - //横轴间距 - crossAxisSpacing: 4.0, - //子组件宽高长度比例 - // childAspectRatio: 1, + if (len > 1) { + List list = []; + for (var i = 0; i < len; i++) { + picList.add(content.pictures[i]['img_src']); + list.add( + LayoutBuilder( + builder: (context, BoxConstraints box) { + return GestureDetector( + onTap: () { + Get.toNamed('/preview', + arguments: {'initialPage': i, 'imgList': picList}); + }, + child: NetworkImgLayer( + src: content.pictures[i]['img_src'], + width: box.maxWidth, + height: box.maxWidth, ), - //GridView中使用的子Widegt - children: list, - ), - ); - }, + ); + }, + ), + ); + } + spanChilds.add( + WidgetSpan( + child: LayoutBuilder( + builder: (context, BoxConstraints box) { + double maxWidth = box.maxWidth; + double crossCount = len < 3 ? 2 : 3; + double height = maxWidth / + crossCount * + (len % crossCount == 0 + ? len ~/ crossCount + : len ~/ crossCount + 1) + + 6; + return Container( + padding: const EdgeInsets.only(top: 6), + height: height, + child: GridView( + padding: EdgeInsets.zero, + physics: const NeverScrollableScrollPhysics(), + // 子Item排列规则 + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + //横轴元素个数 + crossAxisCount: crossCount.toInt(), + //纵轴间距 + mainAxisSpacing: 4.0, + //横轴间距 + crossAxisSpacing: 4.0, + //子组件宽高长度比例 + // childAspectRatio: 1, + ), + //GridView中使用的子Widegt + children: list, + ), + ); + }, + ), ), - ), - ); + ); + } } // spanChilds.add(TextSpan(text: matchMember)); return TextSpan(children: spanChilds); @@ -554,24 +621,25 @@ InlineSpan buildContent(BuildContext context, content) { class UpTag extends StatelessWidget { String? tagText; UpTag({super.key, this.tagText = 'UP'}); - @override Widget build(BuildContext context) { + Color primary = Theme.of(context).colorScheme.primary; return Container( - width: tagText == 'UP' ? 28 : 38, - height: tagText == 'UP' ? 17 : 19, + width: tagText == 'UP' ? 25 : 32, + height: tagText == 'UP' ? 16 : 18, decoration: BoxDecoration( borderRadius: BorderRadius.circular(3), - // color: Theme.of(context).colorScheme.primary, - border: Border.all(color: Theme.of(context).colorScheme.primary)), + color: tagText == 'UP' ? primary : null, + border: Border.all(color: primary)), margin: const EdgeInsets.only(right: 4), - // padding: const EdgeInsets.symmetric(vertical: 0.5, horizontal: 4), child: Center( child: Text( tagText!, style: TextStyle( - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, - color: Theme.of(context).colorScheme.primary, + fontSize: 10, + color: tagText == 'UP' + ? Theme.of(context).colorScheme.onPrimary + : primary, ), ), ), diff --git a/pubspec.lock b/pubspec.lock index 9f021f27..36af3acf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -272,6 +272,14 @@ packages: description: flutter source: sdk version: "0.0.0" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + sha256: "959ef4add147753f990b4a7c6cccb746d5792dbdc81b1cde99e62e7edb31b206" + url: "https://pub.dev" + source: hosted + version: "10.4.0" get: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 7b78fe6a..8251e578 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -65,6 +65,7 @@ dependencies: flutter_inappwebview: 5.4.4 extended_nested_scroll_view: ^6.0.0 + font_awesome_flutter: ^10.4.0 dev_dependencies: flutter_test: @@ -106,6 +107,9 @@ flutter: - family: fansCard fonts: - asset: assets/fonts/fansCard.ttf + - family: ArchivoNarrow + fonts: + - asset: assets/fonts/ArchivoNarrow-BoldItalic.ttf # For details regarding fonts from package dependencies,