feat: 首页,播放页,搜索页,收藏UI
This commit is contained in:
parent
197c3e9b88
commit
e59ac2177b
BIN
assets/pics/liked.png
Normal file
BIN
assets/pics/liked.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/pics/player_lt.png
Normal file
BIN
assets/pics/player_lt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 528 B |
BIN
assets/pics/unlike.png
Normal file
BIN
assets/pics/unlike.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@ -3,6 +3,8 @@ import 'package:skywood/global/request/api_const.dart';
|
|||||||
import 'package:skywood/global/request/app_request.dart';
|
import 'package:skywood/global/request/app_request.dart';
|
||||||
import 'package:skywood/global/tool/sky_log.dart';
|
import 'package:skywood/global/tool/sky_log.dart';
|
||||||
import 'package:skywood/utils/models/drama/skywood_category.dart';
|
import 'package:skywood/utils/models/drama/skywood_category.dart';
|
||||||
|
import 'package:skywood/utils/models/drama/skywood_detail.dart';
|
||||||
|
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||||
import 'package:skywood/utils/models/drama/skywood_home.dart';
|
import 'package:skywood/utils/models/drama/skywood_home.dart';
|
||||||
|
|
||||||
class PlayClient {
|
class PlayClient {
|
||||||
@ -23,7 +25,7 @@ class PlayClient {
|
|||||||
|
|
||||||
static Future<List<SkywoodCategory>> getCategoriesList() {
|
static Future<List<SkywoodCategory>> getCategoriesList() {
|
||||||
return AppRequest.get(ApiStringPlay.getCategoriesList).then((value) {
|
return AppRequest.get(ApiStringPlay.getCategoriesList).then((value) {
|
||||||
SkyLog.print('vvvvv: ${value.data}');
|
// SkyLog.print('vvvvv: ${value.data}');
|
||||||
if (!value.isSuccess) return [];
|
if (!value.isSuccess) return [];
|
||||||
|
|
||||||
Map<String, dynamic> data = value.data['data'];
|
Map<String, dynamic> data = value.data['data'];
|
||||||
@ -34,8 +36,78 @@ class PlayClient {
|
|||||||
// id 28 不显示,删除
|
// id 28 不显示,删除
|
||||||
categoryList = list.map((e) => SkywoodCategory.fromJson(e)).toList();
|
categoryList = list.map((e) => SkywoodCategory.fromJson(e)).toList();
|
||||||
categoryList.removeWhere((element) => element.id == 28);
|
categoryList.removeWhere((element) => element.id == 28);
|
||||||
|
categoryList.removeWhere((element) => element.name.length >= 10);
|
||||||
}
|
}
|
||||||
return categoryList;
|
return categoryList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getSearchHots
|
||||||
|
static Future<List<SkywoodDrama>> getSearchHots() {
|
||||||
|
return AppRequest.get(ApiStringPlay.getSearchHots).then((value) {
|
||||||
|
if (value.isSuccess) {
|
||||||
|
SkyLog.print('getSearchHots: ${value.data}');
|
||||||
|
List list = parseDataForList(value.data);
|
||||||
|
return list.map((e) => SkywoodDrama.fromJson(e)).toList();
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//getCollections
|
||||||
|
static Future<List> getCollections({
|
||||||
|
int page = 1, int pageSize = 10
|
||||||
|
}) {
|
||||||
|
Map<String, dynamic> params = _setPage(page: page, pageSize: pageSize);
|
||||||
|
return AppRequest.get(ApiStringPlay.getCollections, queryParameters: params).then((value) {
|
||||||
|
if (value.isSuccess) {
|
||||||
|
SkyLog.print('getCollections: ${value.data}');
|
||||||
|
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHistory
|
||||||
|
static Future<List> getHistory({
|
||||||
|
int page = 1, int pageSize = 10
|
||||||
|
}) {
|
||||||
|
Map<String, dynamic> params = _setPage(page: page, pageSize: pageSize);
|
||||||
|
return AppRequest.get(ApiStringPlay.getHistories, queryParameters: params).then((value) {
|
||||||
|
if (value.isSuccess) {
|
||||||
|
SkyLog.print('getHistory: ${value.data}');
|
||||||
|
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVideoDetail
|
||||||
|
static Future<SkywoodDetail?> getVideoDetail({
|
||||||
|
required int shortPlayId,
|
||||||
|
required int shortPlayVideoId,
|
||||||
|
}) {
|
||||||
|
Map<String, dynamic> params = {};
|
||||||
|
params['short_play_id'] = shortPlayId;
|
||||||
|
params['short_play_video_id'] = shortPlayVideoId;
|
||||||
|
return AppRequest.get(ApiStringPlay.getVideoDetail, queryParameters: params).then((value) {
|
||||||
|
if (value.isSuccess) {
|
||||||
|
SkyLog.print('getVideoDetail: ${value.data}');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, dynamic> _setPage({int page = 1, int pageSize = 10}) {
|
||||||
|
return {
|
||||||
|
'current_page': page,
|
||||||
|
'page_size': pageSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static List parseDataForList(Map<String, dynamic> data) {
|
||||||
|
Map<String, dynamic> map = data['data'];
|
||||||
|
List? listData = map['list'];
|
||||||
|
return listData ?? [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -63,9 +63,9 @@ class ApiStringPlay {
|
|||||||
static const String getVideoDetail = "/getVideoDetails";
|
static const String getVideoDetail = "/getVideoDetails";
|
||||||
static const String getDetailsRecommand = "/getDetailsRecommand";
|
static const String getDetailsRecommand = "/getDetailsRecommand";
|
||||||
|
|
||||||
// getHomeModules GET /home/all-modules 首页全部模块数据
|
/// getHomeModules GET /home/all-modules 首页全部模块数据
|
||||||
// getRankingList POST /homeRanking 首页榜单,body 带 type,支持 most_trending/top_searched/new_releases
|
// getRankingList POST /homeRanking 首页榜单,body 带 type,支持 most_trending/top_searched/new_releases
|
||||||
// getCategoriesList GET /getCategories 分类列表
|
/// getCategoriesList GET /getCategories 分类列表
|
||||||
// getCategoriesDetail GET /videoList 分类详情/剧列表,分页 + extraParams
|
// getCategoriesDetail GET /videoList 分类详情/剧列表,分页 + extraParams
|
||||||
// getWaterflowList POST /newShortPlay 首页瀑布流,分页 + extraParams
|
// getWaterflowList POST /newShortPlay 首页瀑布流,分页 + extraParams
|
||||||
static const String getHomeModules = "/home/all-modules";
|
static const String getHomeModules = "/home/all-modules";
|
||||||
@ -74,7 +74,7 @@ class ApiStringPlay {
|
|||||||
static const String getCategoriesDetail = "/videoList";
|
static const String getCategoriesDetail = "/videoList";
|
||||||
static const String getWaterflowList = "/newShortPlay";
|
static const String getWaterflowList = "/newShortPlay";
|
||||||
|
|
||||||
// getSearchHots GET /search/hots 搜索热门列表
|
/// getSearchHots GET /search/hots 搜索热门列表
|
||||||
// getSearch GET /search 关键词搜索,query: search
|
// getSearch GET /search 关键词搜索,query: search
|
||||||
// collect POST /collect 收藏,body: short_play_id, video_id
|
// collect POST /collect 收藏,body: short_play_id, video_id
|
||||||
// cancelCollect POST /cancelCollect 取消收藏,body: short_play_id
|
// cancelCollect POST /cancelCollect 取消收藏,body: short_play_id
|
||||||
@ -88,8 +88,8 @@ class ApiStringPlay {
|
|||||||
static const String getSearch = "/search";
|
static const String getSearch = "/search";
|
||||||
static const String collect = "/collect";
|
static const String collect = "/collect";
|
||||||
static const String cancelCollect = "/cancelCollect";
|
static const String cancelCollect = "/cancelCollect";
|
||||||
static const String getMyCollections = "/myCollections";
|
static const String getCollections = "/myCollections";
|
||||||
static const String getMyHistorys = "/myHistorys";
|
static const String getHistories = "/myHistorys";
|
||||||
static const String getNoticeNum = "/noticeNum";
|
static const String getNoticeNum = "/noticeNum";
|
||||||
static const String versionControl = "/customer/versionControl";
|
static const String versionControl = "/customer/versionControl";
|
||||||
static const String getLanguges = "/languges";
|
static const String getLanguges = "/languges";
|
||||||
|
|||||||
@ -23,6 +23,7 @@ class _MainRootState extends State<MainRoot> {
|
|||||||
MinePage(),
|
MinePage(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
final _pageController = PageController();
|
||||||
final List<_TabBarItem> _items = const [
|
final List<_TabBarItem> _items = const [
|
||||||
_TabBarItem(
|
_TabBarItem(
|
||||||
selectedIcon: ConstImg.tabHomeSelected,
|
selectedIcon: ConstImg.tabHomeSelected,
|
||||||
@ -52,7 +53,12 @@ class _MainRootState extends State<MainRoot> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
body: IndexedStack(index: _currentIndex, children: _pages),
|
// body: IndexedStack(index: _currentIndex, children: _pages),
|
||||||
|
body: PageView(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
controller: _pageController,
|
||||||
|
children: _pages,
|
||||||
|
),
|
||||||
bottomNavigationBar: SafeArea(
|
bottomNavigationBar: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
minimum: EdgeInsets.only(bottom: 10.h),
|
minimum: EdgeInsets.only(bottom: 10.h),
|
||||||
@ -113,7 +119,7 @@ class _MainRootState extends State<MainRoot> {
|
|||||||
if (_currentIndex == index) {
|
if (_currentIndex == index) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_pageController.jumpToPage(index);
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentIndex = index;
|
_currentIndex = index;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -29,8 +29,8 @@ class _MainRootState extends State<HomePage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
// _getHomeData();
|
_getHomeData();
|
||||||
// _getDramaCate();
|
_getDramaCate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _getHomeData() {
|
void _getHomeData() {
|
||||||
@ -97,12 +97,14 @@ class _MainRootState extends State<HomePage> {
|
|||||||
),
|
),
|
||||||
_buildTitleImg(ConstImg.homeTitleHotCate),
|
_buildTitleImg(ConstImg.homeTitleHotCate),
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 15.w),
|
padding: EdgeInsets.only(left: 15.w, right: 15.w, top: 20.h),
|
||||||
sliver: HomeCateWidget(
|
sliver: SliverToBoxAdapter(
|
||||||
categories: _categories,
|
child: HomeCateWidget(
|
||||||
|
categories: _categories,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildSpace(height: 100),
|
_buildSpace(height: 20),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
60
lib/pages/home/players/page/drama_detail_page.dart
Normal file
60
lib/pages/home/players/page/drama_detail_page.dart
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:skywood/global/client/play_client.dart';
|
||||||
|
import 'package:skywood/pages/home/players/widget/player_bottom_widget.dart';
|
||||||
|
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||||
|
import 'package:skywood/utils/models/drama/skywood_home.dart';
|
||||||
|
import 'package:skywood/utils/tools/widgets/sky_app_bar.dart';
|
||||||
|
|
||||||
|
class DramaDetailPage extends StatefulWidget {
|
||||||
|
const DramaDetailPage({super.key,
|
||||||
|
required this.dramaBean,
|
||||||
|
});
|
||||||
|
|
||||||
|
final SkywoodDrama dramaBean;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return _DramaDetailPageState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DramaDetailPageState extends State<DramaDetailPage> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
// _getDetailDrama();
|
||||||
|
}
|
||||||
|
|
||||||
|
// void _getDetailDrama() {
|
||||||
|
// PlayClient.getVideoDetail(
|
||||||
|
// shortPlayId: widget.shortPlayId,
|
||||||
|
// shortPlayVideoId: widget.shortPlayVideoId
|
||||||
|
// ).then((value) {});
|
||||||
|
// }
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
appBar: SkyAppBar(
|
||||||
|
title: widget.dramaBean.name,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
body: _bodyWidget(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _bodyWidget() {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
SizedBox(width: double.infinity, height: double.infinity),
|
||||||
|
Positioned(
|
||||||
|
bottom: 0, left: 0, right: 0,
|
||||||
|
child: PlayerBottomWidget(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
0
lib/pages/home/players/page/drama_list_page.dart
Normal file
0
lib/pages/home/players/page/drama_list_page.dart
Normal file
95
lib/pages/home/players/widget/player_bottom_widget.dart
Normal file
95
lib/pages/home/players/widget/player_bottom_widget.dart
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:skywood/main/lib_file.dart';
|
||||||
|
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||||
|
|
||||||
|
class PlayerBottomWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
const PlayerBottomWidget({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return _PlayerBottomWidgetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlayerBottomWidgetState extends State<PlayerBottomWidget> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
HelpImg.asset(ConstImg.unlike, width: 40, height: 40),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Container(
|
||||||
|
height: 36,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
color: Colors.white.withAlpha(30),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
HelpImg.asset(ConstImg.playLt, width: 17, height: 17),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
SkyText(text: 'EP.1', fontSize: 13, color: Colors.white,),
|
||||||
|
SkyText(text: ' / EP.50', fontSize: 13, color: Colors.white.withAlpha(126)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Icon(Icons.arrow_forward_ios, color: Colors.white, size: 13,),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildProgressBar(),
|
||||||
|
const SizedBox(height: 7),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
SkyText(text: '00:09', fontSize: 13, color: Colors.white),
|
||||||
|
SkyText(text: ' / 10:20', fontSize: 13, color: Colors.white.withAlpha(126)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SkyText(text: 'x1.0', fontSize: 13, color: Colors.white),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildProgressBar() {
|
||||||
|
return SizedBox(
|
||||||
|
width: double.infinity, height: 3,
|
||||||
|
child: SliderTheme(
|
||||||
|
data: SliderThemeData(
|
||||||
|
trackHeight: 3, // ✅ 进度条高度 3
|
||||||
|
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 1),
|
||||||
|
overlayShape: const RoundSliderOverlayShape(overlayRadius: 3),
|
||||||
|
activeTrackColor: Colors.white, // ✅ 已播放 = 白色
|
||||||
|
inactiveTrackColor: Colors.white.withAlpha(126), // ✅ 未播放 = 灰色
|
||||||
|
thumbColor: Colors.white,
|
||||||
|
overlayColor: Colors.white.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
child: Slider(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
value: 0.5,
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
onChanged: (value) {
|
||||||
|
// _controller.seekTo(Duration(milliseconds: value.toInt()));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
0
lib/pages/home/players/widget/sky_player_view.dart
Normal file
0
lib/pages/home/players/widget/sky_player_view.dart
Normal file
@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:skywood/global/const/common_color.dart';
|
import 'package:skywood/global/const/common_color.dart';
|
||||||
|
import 'package:skywood/main/lib_file.dart';
|
||||||
|
import 'package:skywood/pages/home/players/page/drama_detail_page.dart';
|
||||||
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||||
import 'package:skywood/utils/tools/sky_time_tool.dart';
|
import 'package:skywood/utils/tools/sky_time_tool.dart';
|
||||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||||
@ -91,6 +93,9 @@ class DailyQuestWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
SizedBox(height: 3.h),
|
SizedBox(height: 3.h),
|
||||||
],
|
],
|
||||||
);
|
).addInkWell(onTap: () {
|
||||||
|
|
||||||
|
HelpNav.to(DramaDetailPage(dramaBean: drama));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:skywood/main/lib_file.dart';
|
import 'package:skywood/main/lib_file.dart';
|
||||||
import 'package:skywood/utils/models/drama/skywood_category.dart';
|
import 'package:skywood/utils/models/drama/skywood_category.dart';
|
||||||
|
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||||
|
|
||||||
class HomeCateWidget extends StatelessWidget {
|
class HomeCateWidget extends StatelessWidget {
|
||||||
|
|
||||||
@ -12,15 +13,20 @@ class HomeCateWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SliverGrid.builder(
|
return SizedBox(
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
height: 270, width: double.infinity,
|
||||||
crossAxisCount: 2, // 🔥 上下2行
|
child: GridView.builder(
|
||||||
crossAxisSpacing: 10, // 行间距(垂直方向)
|
scrollDirection: Axis.horizontal,
|
||||||
mainAxisSpacing: 10, // 列间距(水平方向)
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
childAspectRatio: 0.7, // 宽高比(宽度/高度)
|
crossAxisCount: 2, // 🔥 上下2行
|
||||||
), itemBuilder: (context, index) {
|
crossAxisSpacing: 14, // 行间距(垂直方向)
|
||||||
return _buildCateItem(categories[index], index);
|
mainAxisSpacing: 10, // 列间距(水平方向)
|
||||||
}
|
childAspectRatio: 140 / 131.0, // 宽高比(宽度/高度)
|
||||||
|
),
|
||||||
|
itemCount: categories.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return _buildCateItem(categories[index], index);}
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,8 +34,69 @@ class HomeCateWidget extends StatelessWidget {
|
|||||||
String imgName = index%3 == 0 ? ConstImg.purpleBg : (index%3 == 1 ? ConstImg.yellowBg : ConstImg.redBg);
|
String imgName = index%3 == 0 ? ConstImg.purpleBg : (index%3 == 1 ? ConstImg.yellowBg : ConstImg.redBg);
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
HelpImg.asset(imgName, width: 140.w, height: 131.h, fit: BoxFit.cover),
|
HelpImg.asset(imgName, width: 140.w, height: 131.w, fit: BoxFit.fill),
|
||||||
|
Positioned(
|
||||||
|
bottom: 24.h, left: 10.w, right: 10.w,
|
||||||
|
child: _customText(category.name,),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
_customText(String text) {
|
||||||
|
return Text(
|
||||||
|
text,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white, // 内部字体白色
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
shadows: [
|
||||||
|
Shadow(
|
||||||
|
color: Colors.black, // 描边颜色
|
||||||
|
offset: Offset(1, 1), // 偏移量
|
||||||
|
blurRadius: 0, // 不模糊,形成硬边描边
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
color: Colors.black,
|
||||||
|
offset: Offset(-1, -1),
|
||||||
|
blurRadius: 0,
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
color: Colors.black,
|
||||||
|
offset: Offset(1, -1),
|
||||||
|
blurRadius: 0,
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
color: Colors.black,
|
||||||
|
offset: Offset(-1, 1),
|
||||||
|
blurRadius: 0,
|
||||||
|
),
|
||||||
|
// 四个角再加一些,让描边更饱满
|
||||||
|
// Shadow(
|
||||||
|
// color: Colors.black,
|
||||||
|
// offset: Offset(2, 0),
|
||||||
|
// blurRadius: 0,
|
||||||
|
// ),
|
||||||
|
// Shadow(
|
||||||
|
// color: Colors.black,
|
||||||
|
// offset: Offset(-2, 0),
|
||||||
|
// blurRadius: 0,
|
||||||
|
// ),
|
||||||
|
// Shadow(
|
||||||
|
// color: Colors.black,
|
||||||
|
// offset: Offset(0, 2),
|
||||||
|
// blurRadius: 0,
|
||||||
|
// ),
|
||||||
|
// Shadow(
|
||||||
|
// color: Colors.black,
|
||||||
|
// offset: Offset(0, -2),
|
||||||
|
// blurRadius: 0,
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class HotCartridgeWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 1, left: 5.w, right: 5.w,
|
top: 1, left: 5.w, right: 5.w,
|
||||||
child: HelpImg.network(drama.imageUrl, width: 100.w, height: 130.h),
|
child: HelpImg.network(drama.imageUrl, width: 100.w, height: 130.h, fit: BoxFit.cover),
|
||||||
),
|
),
|
||||||
HelpImg.asset(index<3?ConstImg.hotBg1:ConstImg.hotBg2, width: 110.w, height: 174.h)
|
HelpImg.asset(index<3?ConstImg.hotBg1:ConstImg.hotBg2, width: 110.w, height: 174.h)
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:skywood/global/client/play_client.dart';
|
||||||
import 'package:skywood/main/lib_file.dart';
|
import 'package:skywood/main/lib_file.dart';
|
||||||
|
|
||||||
class CollectionPage extends StatefulWidget {
|
class CollectionPage extends StatefulWidget {
|
||||||
@ -13,6 +14,19 @@ class CollectionPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _CollectionPageState extends State<CollectionPage> {
|
class _CollectionPageState extends State<CollectionPage> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// TODO: implement initState
|
||||||
|
super.initState();
|
||||||
|
_getCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _getCollection() {
|
||||||
|
PlayClient.getCollections().then((value) {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:skywood/global/client/play_client.dart';
|
||||||
import 'package:skywood/main/lib_file.dart';
|
import 'package:skywood/main/lib_file.dart';
|
||||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||||
|
|
||||||
@ -14,6 +15,16 @@ class PlayLogPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _CollectionPageState extends State<PlayLogPage> {
|
class _CollectionPageState extends State<PlayLogPage> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_getHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _getHistory() {
|
||||||
|
PlayClient.getHistory().then((value) {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:skywood/global/client/play_client.dart';
|
||||||
import 'package:skywood/main/lib_file.dart';
|
import 'package:skywood/main/lib_file.dart';
|
||||||
import 'package:skywood/pages/search/page/result_page.dart';
|
import 'package:skywood/pages/search/page/result_page.dart';
|
||||||
import 'package:skywood/pages/search/widget/search_util.dart';
|
import 'package:skywood/pages/search/widget/search_util.dart';
|
||||||
import 'package:skywood/pages/search/widget/search_view.dart';
|
import 'package:skywood/pages/search/widget/search_view.dart';
|
||||||
import 'package:skywood/pages/widgets/pixel_widgets.dart';
|
import 'package:skywood/pages/widgets/pixel_widgets.dart';
|
||||||
|
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||||
import 'package:skywood/utils/tools/help_nav.dart';
|
import 'package:skywood/utils/tools/help_nav.dart';
|
||||||
|
import 'package:skywood/utils/tools/sky_time_tool.dart';
|
||||||
import 'package:skywood/utils/tools/widgets/sky_app_bar.dart';
|
import 'package:skywood/utils/tools/widgets/sky_app_bar.dart';
|
||||||
import 'package:skywood/utils/tools/widgets/sky_text_field.dart';
|
import 'package:skywood/utils/tools/widgets/sky_text_field.dart';
|
||||||
|
|
||||||
@ -33,6 +36,7 @@ class _MainRootState extends State<SearchPage> {
|
|||||||
'Who Dared Touch My Bride',
|
'Who Dared Touch My Bride',
|
||||||
'Famous Families Fled',
|
'Famous Families Fled',
|
||||||
];
|
];
|
||||||
|
List<SkywoodDrama> _searchHots = [];
|
||||||
|
|
||||||
Future<void> _loadSearchHistory() async {
|
Future<void> _loadSearchHistory() async {
|
||||||
final list = await SearchUtil.getSearchList();
|
final list = await SearchUtil.getSearchList();
|
||||||
@ -61,6 +65,16 @@ class _MainRootState extends State<SearchPage> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
_loadSearchHistory();
|
_loadSearchHistory();
|
||||||
|
_getSearchHots();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _getSearchHots() {
|
||||||
|
PlayClient.getSearchHots().then((value) {
|
||||||
|
print('object: ${value.length}');
|
||||||
|
setState(() {
|
||||||
|
_searchHots = value;
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -129,76 +143,119 @@ class _MainRootState extends State<SearchPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTrendingList() {
|
Widget _buildTrendingList() {
|
||||||
|
if (_searchHots.isEmpty) return const SizedBox.shrink();
|
||||||
|
|
||||||
|
// 每4个一组
|
||||||
|
final List<List<SkywoodDrama>> groupedData = [];
|
||||||
|
for (int i = 0; i < _searchHots.length; i += 4) {
|
||||||
|
final end = (i + 4 > _searchHots.length) ? _searchHots.length : i + 4;
|
||||||
|
groupedData.add(_searchHots.sublist(i, end));
|
||||||
|
}
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 446.h,
|
height: 114 * 4 + 20, // 104 * 4 + 10 * 3
|
||||||
child: SingleChildScrollView(
|
child: ListView.separated(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 15.w),
|
padding: EdgeInsets.symmetric(horizontal: 15.w),
|
||||||
child: Wrap(
|
itemBuilder: (context, index) {
|
||||||
direction: Axis.vertical,
|
final group = groupedData[index];
|
||||||
spacing: 10.h,
|
return _buildGroup(group);
|
||||||
runSpacing: 10.w,
|
},
|
||||||
children: _titles
|
separatorBuilder: (context, index) => SizedBox(width: 10.w),
|
||||||
.map((title) => _TrendingItem(title: title))
|
itemCount: groupedData.length,
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 一组卡片(垂直排列,最多4个)
|
||||||
|
Widget _buildGroup(List<SkywoodDrama> group) {
|
||||||
|
return SizedBox(
|
||||||
|
width: 236,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: group.map((drama) {
|
||||||
|
return _TrendingItem(drama: drama);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Widget _buildTrendingList() {
|
||||||
|
// return Container(
|
||||||
|
// height: 114 * 4, width: AppScreen.screenWidth,
|
||||||
|
// color: Colors.green,
|
||||||
|
// child: GridView.builder(
|
||||||
|
// padding: EdgeInsets.only(left: 15.w),
|
||||||
|
// scrollDirection: Axis.horizontal,
|
||||||
|
// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
// crossAxisCount: 4, // 上下4行
|
||||||
|
// crossAxisSpacing: 10, // 行间距(垂直方向)
|
||||||
|
// mainAxisSpacing: 10, // 列间距(水平方向)
|
||||||
|
// childAspectRatio: 236 / 104.0, // 宽高比(宽度/高度)
|
||||||
|
// ),
|
||||||
|
// itemCount: _searchHots.length,
|
||||||
|
// itemBuilder: (context, index) {
|
||||||
|
// final drama = _searchHots[index];
|
||||||
|
// return Container(width: 236, height: 104, color: index%2==0 ? Colors.orangeAccent : Colors.redAccent,);
|
||||||
|
// return _TrendingItem(drama: drama);
|
||||||
|
// }
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TrendingItem extends StatelessWidget {
|
class _TrendingItem extends StatelessWidget {
|
||||||
const _TrendingItem({required this.title});
|
const _TrendingItem({required this.drama});
|
||||||
|
|
||||||
final String title;
|
final SkywoodDrama drama;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return Container(
|
||||||
width: 236.w,
|
margin: EdgeInsets.only(bottom: 10),
|
||||||
height: 104.h,
|
// width: 236.w,
|
||||||
|
// height: 104.w,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const PosterImage(
|
|
||||||
width: 80,
|
HelpImg.network(drama.imageUrl, width: 80, height: 104, fit: BoxFit.cover),
|
||||||
height: 104,
|
SizedBox(width: 8),
|
||||||
borderRadius: 4,
|
|
||||||
showBorder: false,
|
|
||||||
),
|
|
||||||
SizedBox(width: 8.w),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.h),
|
padding: EdgeInsets.symmetric(vertical: 10),
|
||||||
child: Column(
|
child: SizedBox(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
height: 84,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
title,
|
children: [
|
||||||
maxLines: 2,
|
Text(
|
||||||
overflow: TextOverflow.ellipsis,
|
drama.name,
|
||||||
style: TextStyle(
|
maxLines: 2,
|
||||||
color: CommonColor.black,
|
overflow: TextOverflow.ellipsis,
|
||||||
fontSize: 13.sp,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
color: CommonColor.black,
|
||||||
height: 1.2,
|
fontSize: 13.sp,
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
),
|
height: 1.2,
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
HelpImg.asset(ConstImg.hot, width: 15.w, height: 15.w),
|
|
||||||
SizedBox(width: 2.w),
|
|
||||||
Text(
|
|
||||||
'17.3K',
|
|
||||||
style: TextStyle(
|
|
||||||
color: const Color(0x80000000),
|
|
||||||
fontSize: 13.w,
|
|
||||||
height: 1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
Spacer(),
|
||||||
],
|
Row(
|
||||||
|
children: [
|
||||||
|
HelpImg.asset(ConstImg.hot, width: 15.w, height: 15.w),
|
||||||
|
SizedBox(width: 2.w),
|
||||||
|
Text(
|
||||||
|
SkyTimeTool.formatNum(drama.watchTotal, unit: 'w'),
|
||||||
|
style: TextStyle(
|
||||||
|
color: const Color(0x80000000),
|
||||||
|
fontSize: 13.w,
|
||||||
|
height: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -58,6 +58,9 @@ class ConstImg {
|
|||||||
static const String likeSelectedBg = "assets/pics/like_selected_bg.png";
|
static const String likeSelectedBg = "assets/pics/like_selected_bg.png";
|
||||||
static const String likeUnselectBg = "assets/pics/like_unselect_bg.png";
|
static const String likeUnselectBg = "assets/pics/like_unselect_bg.png";
|
||||||
static const String continueWatch = "assets/pics/continue_watch.png";
|
static const String continueWatch = "assets/pics/continue_watch.png";
|
||||||
|
static const String playLt = "assets/pics/player_lt.png";
|
||||||
|
static const String liked = "assets/pics/liked.png";
|
||||||
|
static const String unlike = "assets/pics/unlike.png";
|
||||||
|
|
||||||
|
|
||||||
// nodata
|
// nodata
|
||||||
|
|||||||
@ -16,6 +16,7 @@ class SkyTimeTool {
|
|||||||
num value, {
|
num value, {
|
||||||
int decimalDigits = 1,
|
int decimalDigits = 1,
|
||||||
String separator = ',',
|
String separator = ',',
|
||||||
|
String unit = 'K',
|
||||||
}) {
|
}) {
|
||||||
String removeTrailingZeros(String str) {
|
String removeTrailingZeros(String str) {
|
||||||
if (str.contains('.')) {
|
if (str.contains('.')) {
|
||||||
@ -43,8 +44,9 @@ class SkyTimeTool {
|
|||||||
if (value < 1000) {
|
if (value < 1000) {
|
||||||
return addSeparator(value.toStringAsFixed(0));
|
return addSeparator(value.toStringAsFixed(0));
|
||||||
} else if (value < 1000000) {
|
} else if (value < 1000000) {
|
||||||
final formatted = (value / 10000).toStringAsFixed(decimalDigits);
|
int mu = unit=='w' ? 10000 : 1000;
|
||||||
return '${removeTrailingZeros(formatted)}w';
|
final formatted = (value / mu).toStringAsFixed(decimalDigits);
|
||||||
|
return '${removeTrailingZeros(formatted)}$unit';
|
||||||
} else {
|
} else {
|
||||||
final formatted = (value / 1000000).toStringAsFixed(decimalDigits);
|
final formatted = (value / 1000000).toStringAsFixed(decimalDigits);
|
||||||
return '${removeTrailingZeros(formatted)}m';
|
return '${removeTrailingZeros(formatted)}m';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user