feat: 视频详情页,分类页。个人中心
This commit is contained in:
1 parent
b5bce6edcf
commit
a3426831ed
26 files changed
+2878
-1077
No files matched your search
@@ -0,0 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:novyronst/common/help/app_nav.dart';
|
||||
import 'package:novyronst/common/help/help_image.dart';
|
||||
import 'package:novyronst/common/model/ny_drama_model.dart';
|
||||
import 'package:novyronst/pages/first/player_detail/video_detail_page.dart';
|
||||
import 'package:novyronst/pages/widgets/ny_tag_widget.dart';
|
||||
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
|
||||
|
||||
import '../../../tools/util/app_screen.dart';
|
||||
import '../../../tools/widgets/novy_button_bg.dart';
|
||||
import '../../widgets/cut_corner_clipper.dart';
|
||||
import 'home_remaining_sections.dart';
|
||||
|
||||
class HomeCardWidget extends StatelessWidget {
|
||||
const HomeCardWidget({
|
||||
super.key,
|
||||
required this.dramaList,
|
||||
required this.title,
|
||||
});
|
||||
|
||||
final List<NyDramaModel> dramaList;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CutCornerClipper(
|
||||
width: AppScreen.screenWidth - 32,
|
||||
height: 308.h,
|
||||
cutCorner: CutCorner.topRight,
|
||||
cutSize: 30,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_threeLine(),
|
||||
const SizedBox(height: 10),
|
||||
OutlinedTitle(title, fontSize: 17),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(child: _listView()),
|
||||
const SizedBox(height: 6),
|
||||
NovyButtonBg(text: 'Click to watch', width: 327.w),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _listView() {
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: dramaList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final drama = dramaList[index];
|
||||
var tag = '';
|
||||
if (drama.category != null && drama.category!.isNotEmpty) {
|
||||
tag = drama.category!.first;
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => AppNav.to(VideoDetailPage(drama: drama)),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(right: 7),
|
||||
width: 104.w,
|
||||
child: Column(
|
||||
children: [
|
||||
HelpImage.network(drama.imageUrl, width: 104.w, height: 157.h),
|
||||
const SizedBox(height: 5),
|
||||
NovyText(
|
||||
drama.name,
|
||||
fontSize: 12.sp,
|
||||
color: Colors.black,
|
||||
maxLines: 2,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
if (tag != '') Row(children: [NyTagWidget(text: tag)]),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _threeLine() {
|
||||
return Row(
|
||||
children: List.generate(3, (index) {
|
||||
return Container(
|
||||
width: 3,
|
||||
height: 8,
|
||||
color: Colors.black,
|
||||
margin: const EdgeInsets.only(right: 3),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:novyronst/common/help/app_nav.dart';
|
||||
import 'package:novyronst/common/model/ny_category_model.dart';
|
||||
import 'package:novyronst/pages/first/pages/category_list_page.dart';
|
||||
import 'package:novyronst/pages/widgets/cut_corner_clipper.dart';
|
||||
import 'package:novyronst/pages/widgets/ny_progress_widget.dart';
|
||||
import 'package:novyronst/pages/widgets/ny_tag_widget.dart';
|
||||
import 'package:novyronst/root/lib_export.dart';
|
||||
import 'package:novyronst/tools/widgets/extend_widget.dart';
|
||||
import 'package:novyronst/tools/widgets/novy_button_bg.dart';
|
||||
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
|
||||
import 'package:novyronst/tools/widgets/novy_img_bg.dart';
|
||||
|
||||
class HomeFirstView extends StatefulWidget {
|
||||
|
||||
const HomeFirstView({super.key,
|
||||
required this.categories,
|
||||
});
|
||||
const HomeFirstView({super.key, required this.categories});
|
||||
|
||||
final List<NyCategoryModel> categories;
|
||||
|
||||
@@ -24,21 +24,22 @@ class HomeFirstView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _HomeFirstViewState extends State<HomeFirstView> {
|
||||
|
||||
bool _hasHistory = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const double _space = 25;
|
||||
const double space = 25;
|
||||
return Stack(
|
||||
children: [
|
||||
// 151
|
||||
SizedBox(
|
||||
height: 151 - _space + 200 + 52 + 20,
|
||||
SizedBox(height: 151 - space + 200 + 52 + 20),
|
||||
HelpImage.asset(
|
||||
ConstImage.homeFirst,
|
||||
width: AppScreen.screenWidth,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
HelpImage.asset(ConstImage.homeFirst, width: AppScreen.screenWidth, fit: BoxFit.fill),
|
||||
Positioned(
|
||||
top: 151-_space, left: 16, right: 16,
|
||||
top: 151 - space,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: Column(
|
||||
children: [
|
||||
CutCornerClipper(
|
||||
@@ -52,40 +53,46 @@ class _HomeFirstViewState extends State<HomeFirstView> {
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: List.generate(widget.categories.length, (index) {
|
||||
final _item = widget.categories[index];
|
||||
final item = widget.categories[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
child: NovyImgBg(imgBg: ConstImage.cateBg,
|
||||
child: NovyImgBg(
|
||||
imgBg: ConstImage.cateBg,
|
||||
height: 52,
|
||||
child: Stack(
|
||||
children:[
|
||||
NovyText(_item.name, color: ConstColor.btGreen,
|
||||
children: [
|
||||
NovyText(
|
||||
item.name,
|
||||
color: ConstColor.btGreen,
|
||||
fontSize: 21,
|
||||
textAlign: TextAlign.center,
|
||||
fontFamily: ConstFont.montBlack,
|
||||
shadow: Shadow(
|
||||
offset: const Offset(1, 1), // X: 1, Y: 1
|
||||
blurRadius: 0, // Blur: 0
|
||||
color: Colors.black, // #000000
|
||||
blurRadius: 0, // Blur: 0
|
||||
color: Colors.black, // #000000
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0, left: 0, right: 0,
|
||||
child: Container(
|
||||
height: 2,
|
||||
color: Colors.black,
|
||||
),
|
||||
)
|
||||
]
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(height: 2, color: Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
).addInkWell(
|
||||
onTap: () {
|
||||
AppNav.to(CategoryListPage(preCategory: item));
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -97,13 +104,14 @@ class _HomeFirstViewState extends State<HomeFirstView> {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
NovyText('Current Viewing Progress',
|
||||
NovyText(
|
||||
'Current Viewing Progress',
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontFamily: ConstFont.montBlack,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
HelpImage.asset(ConstImage.homeCurrentTag, height: 10)
|
||||
HelpImage.asset(ConstImage.homeCurrentTag, height: 10),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
@@ -111,7 +119,8 @@ class _HomeFirstViewState extends State<HomeFirstView> {
|
||||
children: [
|
||||
Container(
|
||||
color: Colors.grey.withAlpha(200),
|
||||
width: 60, height: 90,
|
||||
width: 60,
|
||||
height: 90,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
SizedBox(
|
||||
@@ -119,13 +128,15 @@ class _HomeFirstViewState extends State<HomeFirstView> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
NovyText('Jdksyjs dsaoy',
|
||||
NovyText(
|
||||
'Jdksyjs dsaoy',
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
fontFamily: ConstFont.montMedium,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
NovyText('Jdksyjs dsaoy dasku dsaldsa dljas',
|
||||
NovyText(
|
||||
'Jdksyjs dsaoy dasku dsaldsa dljas',
|
||||
color: Colors.grey,
|
||||
fontSize: 10,
|
||||
fontFamily: ConstFont.montRegular,
|
||||
@@ -138,18 +149,18 @@ class _HomeFirstViewState extends State<HomeFirstView> {
|
||||
const SizedBox(width: 8),
|
||||
NyHotWidget(text: '12.4k'),
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
NyProgressWidget(),
|
||||
const SizedBox(height: 6),
|
||||
NovyButtonBg(text: 'Keep Watching', type: 2, width: 327.w,)
|
||||
NovyButtonBg(text: 'Keep Watching', type: 2, width: 327.w),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:novyronst/common/const/const_color.dart';
|
||||
import 'package:novyronst/common/const/const_string.dart';
|
||||
import 'package:novyronst/common/help/app_nav.dart';
|
||||
import 'package:novyronst/common/help/help_image.dart';
|
||||
import 'package:novyronst/common/model/ny_drama_model.dart';
|
||||
import 'package:novyronst/pages/first/player_detail/video_detail_page.dart';
|
||||
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
|
||||
|
||||
import '../../../tools/util/app_screen.dart';
|
||||
@@ -12,11 +14,7 @@ import '../../widgets/cut_corner_clipper.dart';
|
||||
import '../../widgets/ny_tag_widget.dart';
|
||||
|
||||
class HomeRankWidget extends StatelessWidget {
|
||||
|
||||
const HomeRankWidget({
|
||||
super.key,
|
||||
required this.dramaRanks,
|
||||
});
|
||||
const HomeRankWidget({super.key, required this.dramaRanks});
|
||||
|
||||
final List<NyDramaModel> dramaRanks;
|
||||
|
||||
@@ -28,24 +26,25 @@ class HomeRankWidget extends StatelessWidget {
|
||||
cutSize: 40,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: dramaRanks.isEmpty ? SizedBox(height: 20,) : Column(
|
||||
children: [
|
||||
_buildRankList(),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 20.w),
|
||||
NovyButtonBg(text: 'Keep Watching', width: 308.w,)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
child: dramaRanks.isEmpty
|
||||
? const SizedBox(height: 20)
|
||||
: Column(
|
||||
children: [
|
||||
_buildRankList(),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 20.w),
|
||||
NovyButtonBg(text: 'Keep Watching', width: 308.w),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Row _buildRankList() {
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
_firstVideoView(),
|
||||
@@ -56,85 +55,91 @@ class HomeRankWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _firstVideoView() {
|
||||
NyDramaModel _item = dramaRanks.first;
|
||||
String _tag = "Safiray";
|
||||
if (_item.categoryList!=null && _item.categoryList!.isNotEmpty) {
|
||||
_tag = _item.categoryList!.first.name;
|
||||
final item = dramaRanks.first;
|
||||
var tag = 'Safiray';
|
||||
if (item.categoryList != null && item.categoryList!.isNotEmpty) {
|
||||
tag = item.categoryList!.first.name;
|
||||
}
|
||||
return SizedBox(
|
||||
width: 160.w,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 160.w, height: 240.w,
|
||||
child: HelpImage.network(_item.imageUrl, fit: BoxFit.cover),
|
||||
),
|
||||
Positioned(
|
||||
top: -10, left: 1,
|
||||
child: _colorsText(1),
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
NovyText(_item.name, fontSize: 15,
|
||||
color: Colors.black,
|
||||
fontFamily: ConstFont.montMedium,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
NyTagWidget(text: _tag),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => AppNav.to(VideoDetailPage(drama: item)),
|
||||
child: SizedBox(
|
||||
width: 160.w,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 160.w,
|
||||
height: 240.w,
|
||||
child: HelpImage.network(item.imageUrl, fit: BoxFit.cover),
|
||||
),
|
||||
Positioned(top: -10, left: 1, child: _colorsText(1)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
NovyText(
|
||||
item.name,
|
||||
fontSize: 15,
|
||||
color: Colors.black,
|
||||
fontFamily: ConstFont.montMedium,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(children: [NyTagWidget(text: tag)]),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _otherVideoView() {
|
||||
int _count = dramaRanks.length - 1;
|
||||
if (_count > 3) _count = 3;
|
||||
var count = dramaRanks.length - 1;
|
||||
if (count > 3) count = 3;
|
||||
return Column(
|
||||
children: List.generate(_count, (index) {
|
||||
NyDramaModel _item = dramaRanks[index + 1];
|
||||
String _tag = "";
|
||||
if (_item.categoryList!=null && _item.categoryList!.isNotEmpty) {
|
||||
_tag = _item.categoryList!.first.name;
|
||||
children: List.generate(count, (index) {
|
||||
final item = dramaRanks[index + 1];
|
||||
var tag = '';
|
||||
if (item.categoryList != null && item.categoryList!.isNotEmpty) {
|
||||
tag = item.categoryList!.first.name;
|
||||
}
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
width: 160.w, height: 93.w,
|
||||
child: Row(
|
||||
children: [
|
||||
HelpImage.network(_item.imageUrl, width: 62.w, height: 93.w),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_colorsText(index + 2),
|
||||
const SizedBox(height: 8),
|
||||
NovyText(_item.name, fontSize: 13,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
_tag.isNotEmpty ? NyTagWidget(text: _tag) : SizedBox()
|
||||
],
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => AppNav.to(VideoDetailPage(drama: item)),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
width: 160.w,
|
||||
height: 93.w,
|
||||
child: Row(
|
||||
children: [
|
||||
HelpImage.network(item.imageUrl, width: 62.w, height: 93.w),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_colorsText(index + 2),
|
||||
const SizedBox(height: 8),
|
||||
NovyText(item.name, fontSize: 13, color: Colors.black),
|
||||
const SizedBox(height: 5),
|
||||
tag.isNotEmpty
|
||||
? NyTagWidget(text: tag)
|
||||
: const SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
_colorsText(int index) {
|
||||
Widget _colorsText(int index) {
|
||||
if (index == 4) {
|
||||
return Text(
|
||||
'$index',
|
||||
@@ -160,7 +165,7 @@ class HomeRankWidget extends StatelessWidget {
|
||||
child: Text(
|
||||
'$index',
|
||||
style: TextStyle(
|
||||
fontSize: index==1 ? 40: 20,
|
||||
fontSize: index == 1 ? 40 : 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
fontStyle: FontStyle.italic,
|
||||
@@ -168,4 +173,4 @@ class HomeRankWidget extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:novyronst/common/api/video_request.dart';
|
||||
import 'package:novyronst/common/const/const_color.dart';
|
||||
import 'package:novyronst/common/const/const_string.dart';
|
||||
import 'package:novyronst/common/help/app_nav.dart';
|
||||
import 'package:novyronst/common/help/help_image.dart';
|
||||
import 'package:novyronst/common/model/ny_drama_model.dart';
|
||||
import 'package:novyronst/common/model/ny_episode_model.dart';
|
||||
import 'package:novyronst/pages/first/player_detail/video_detail_page.dart';
|
||||
import 'package:novyronst/pages/widgets/cut_corner_clipper.dart';
|
||||
import 'package:novyronst/pages/widgets/ny_tag_widget.dart';
|
||||
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
|
||||
|
||||
class HomeRemainingSections extends StatelessWidget {
|
||||
const HomeRemainingSections({super.key});
|
||||
import '../../../common/model/ny_detail_model.dart';
|
||||
|
||||
const int episodeSeriesPageSize = 6;
|
||||
|
||||
static const _ActorInfo _actor = _ActorInfo(
|
||||
class HomeRemainingSections extends StatefulWidget {
|
||||
const HomeRemainingSections({super.key, required this.dramaOne});
|
||||
|
||||
final NyDramaModel dramaOne;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _HomeRemainingSectionsState();
|
||||
}
|
||||
}
|
||||
|
||||
class _HomeRemainingSectionsState extends State<HomeRemainingSections> {
|
||||
NyDetailModel? _detailModel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_getDetailsData();
|
||||
}
|
||||
|
||||
void _getDetailsData() {
|
||||
VideoRequest.getVideoDetail(
|
||||
shortPlayId: widget.dramaOne.shortPlayId ?? 0,
|
||||
shortPlayVideoId: widget.dramaOne.shortPlayVideoId ?? 0,
|
||||
).then((value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_detailModel = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final _ActorInfo _actor = _ActorInfo(
|
||||
name: 'Cheng Zi',
|
||||
tags: 'Romantic Lead | Revenge Drama | Lead Actor',
|
||||
bornYear: '2002',
|
||||
@@ -17,164 +56,32 @@ class HomeRemainingSections extends StatelessWidget {
|
||||
nationality: 'China',
|
||||
);
|
||||
|
||||
static const List<_DramaItem> _seriesItems = [
|
||||
_DramaItem(
|
||||
title: 'Cities Of Smoke Starlight',
|
||||
category: 'Satisfying',
|
||||
palette: [Color(0xFF33241D), Color(0xFFD8A65B)],
|
||||
),
|
||||
_DramaItem(
|
||||
title: 'Cities Of Smoke Starlight',
|
||||
category: 'Satisfying',
|
||||
palette: [Color(0xFF25354C), Color(0xFFC88B57)],
|
||||
),
|
||||
_DramaItem(
|
||||
title: 'Cities Of Smoke Starlight',
|
||||
category: 'Satisfying',
|
||||
palette: [Color(0xFF3E2B2C), Color(0xFFE2B36B)],
|
||||
),
|
||||
_DramaItem(
|
||||
title: 'Cities Of Smoke Starlight',
|
||||
category: 'Satisfying',
|
||||
palette: [Color(0xFF443A2C), Color(0xFFC48645)],
|
||||
),
|
||||
_DramaItem(
|
||||
title: 'Cities Of Smoke Starlight',
|
||||
category: 'Satisfying',
|
||||
palette: [Color(0xFF314559), Color(0xFFE5AA65)],
|
||||
),
|
||||
_DramaItem(
|
||||
title: 'Cities Of Smoke Starlight',
|
||||
category: 'Satisfying',
|
||||
palette: [Color(0xFF2C2637), Color(0xFFD39458)],
|
||||
),
|
||||
];
|
||||
|
||||
static const List<String> _categoryTitles = [
|
||||
'Counterattack Life',
|
||||
'Reborn And Returned',
|
||||
'God Of War Returns',
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 10, 16, 92),
|
||||
child: Column(
|
||||
padding: const EdgeInsets.fromLTRB(16, 3, 16, 16),
|
||||
child: Stack(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const _ActorSection(actor: _actor, items: _seriesItems),
|
||||
const SizedBox(height: 18),
|
||||
..._categoryTitles.map((title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 18),
|
||||
child: _DramaShelfSection(title: title, items: _seriesItems),
|
||||
);
|
||||
}),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 12),
|
||||
child: _ActorSection(actor: _actor, detailModel: _detailModel),
|
||||
),
|
||||
Positioned(
|
||||
left: -10,
|
||||
top: 0,
|
||||
child: HelpImage.asset(ConstImage.homeEyes, width: 72),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _TopRankingPoster extends StatelessWidget {
|
||||
const _TopRankingPoster({required this.item});
|
||||
|
||||
final _DramaItem item;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: _PosterBlock(item: item, showLargeTitle: true),
|
||||
),
|
||||
Positioned(
|
||||
top: 4,
|
||||
left: 5,
|
||||
child: NovyText(
|
||||
'1',
|
||||
color: ConstColor.btGreen,
|
||||
fontSize: 36,
|
||||
fontFamily: ConstFont.montBlack,
|
||||
shadow: const Shadow(
|
||||
offset: Offset(1.5, 1.5),
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
NovyText(
|
||||
item.title,
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
fontFamily: ConstFont.montMedium,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
NyTagWidget(text: item.category),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RankingListItem extends StatelessWidget {
|
||||
const _RankingListItem({required this.rank, required this.item});
|
||||
|
||||
final int rank;
|
||||
final _DramaItem item;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color rankColor = rank == 4
|
||||
? const Color(0xFFBBBBBB)
|
||||
: ConstColor.btGreen;
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(width: 42, height: 64, child: _PosterBlock(item: item)),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
NovyText(
|
||||
'$rank',
|
||||
color: rankColor,
|
||||
fontSize: 19,
|
||||
fontFamily: ConstFont.montBlack,
|
||||
lineHeight: 1,
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
NovyText(
|
||||
item.title,
|
||||
color: Colors.black,
|
||||
fontSize: 11,
|
||||
fontFamily: ConstFont.montBold,
|
||||
maxLines: 1,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
NyTagWidget(text: item.category),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ActorSection extends StatelessWidget {
|
||||
const _ActorSection({required this.actor, required this.items});
|
||||
const _ActorSection({required this.actor, required this.detailModel});
|
||||
|
||||
final _ActorInfo actor;
|
||||
final List<_DramaItem> items;
|
||||
final NyDetailModel? detailModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -184,26 +91,43 @@ class _ActorSection extends StatelessWidget {
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 18, 10, 10),
|
||||
padding: const EdgeInsets.fromLTRB(10, 16, 10, 10),
|
||||
child: Column(
|
||||
children: [
|
||||
const _CornerEyes(),
|
||||
const SizedBox(height: 4),
|
||||
_OutlinedTitle('Read This Series Of Short Plays', fontSize: 19),
|
||||
// const SizedBox(height: 10),
|
||||
Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: OutlinedTitle(
|
||||
'Read This Series Of Short Plays',
|
||||
fontSize: 17,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 26),
|
||||
const _ActorAvatar(),
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [Color(0xFFFFA6D9), Color(0xFFFFE6F5)],
|
||||
),
|
||||
),
|
||||
child: HelpImage.asset(ConstImage.homeHead, width: 100),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
NovyText(
|
||||
actor.name,
|
||||
color: Colors.black,
|
||||
fontSize: 22,
|
||||
fontSize: 18,
|
||||
fontFamily: ConstFont.montBlack,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
NovyText(
|
||||
actor.tags,
|
||||
color: const Color(0xFF888888),
|
||||
fontSize: 13,
|
||||
fontSize: 12,
|
||||
fontFamily: ConstFont.montRegular,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -211,9 +135,7 @@ class _ActorSection extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
_SectionHeader(title: 'Series'),
|
||||
const SizedBox(height: 8),
|
||||
_SeriesGrid(items: items, showTags: false),
|
||||
const SizedBox(height: 12),
|
||||
const _PagerDots(),
|
||||
_SeriesGrid(detailModel: detailModel),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -223,147 +145,144 @@ class _ActorSection extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _DramaShelfSection extends StatelessWidget {
|
||||
const _DramaShelfSection({required this.title, required this.items});
|
||||
class _SeriesGrid extends StatefulWidget {
|
||||
const _SeriesGrid({required this.detailModel});
|
||||
|
||||
final String title;
|
||||
final List<_DramaItem> items;
|
||||
final NyDetailModel? detailModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CutCornerClipper(
|
||||
cutSize: 40,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 14, 8, 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const _GripBars(),
|
||||
const SizedBox(height: 8),
|
||||
_OutlinedTitle(title, fontSize: 19),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: List.generate(3, (index) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(right: index == 2 ? 0 : 8),
|
||||
child: _SeriesCard(item: items[index], showTag: true),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const _GreenActionButton(text: 'Click to watch'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
State<_SeriesGrid> createState() => _SeriesGridState();
|
||||
}
|
||||
|
||||
class _SeriesGrid extends StatelessWidget {
|
||||
const _SeriesGrid({required this.items, required this.showTags});
|
||||
class _SeriesGridState extends State<_SeriesGrid> {
|
||||
final PageController _pageController = PageController();
|
||||
int _currentPage = 0;
|
||||
|
||||
final List<_DramaItem> items;
|
||||
final bool showTags;
|
||||
List<NyEpisodeModel> get _items => widget.detailModel?.episodeList ?? [];
|
||||
|
||||
String get _coverUrl {
|
||||
final shortPlayImage = widget.detailModel?.shortPlayInfo?.imageUrl ?? '';
|
||||
if (shortPlayImage.isNotEmpty) return shortPlayImage;
|
||||
return widget.detailModel?.videoInfo?.imageUrl ?? '';
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant _SeriesGrid oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
final pageCount = episodeSeriesPageCount(_items.length);
|
||||
if (_currentPage >= pageCount) {
|
||||
_currentPage = pageCount - 1;
|
||||
if (_pageController.hasClients) {
|
||||
_pageController.jumpToPage(_currentPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: items.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
crossAxisSpacing: 9,
|
||||
mainAxisSpacing: 10,
|
||||
childAspectRatio: 0.47,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return _SeriesCard(item: items[index], showTag: showTags);
|
||||
final pageCount = episodeSeriesPageCount(_items.length);
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final gridHeight = 200.0 * 2;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: gridHeight,
|
||||
child: PageView.builder(
|
||||
controller: _pageController,
|
||||
itemCount: pageCount,
|
||||
onPageChanged: (index) {
|
||||
setState(() {
|
||||
_currentPage = index;
|
||||
});
|
||||
},
|
||||
itemBuilder: (context, pageIndex) {
|
||||
final pageItems = episodeSeriesPageItems(_items, pageIndex);
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: pageItems.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
crossAxisSpacing: 9,
|
||||
mainAxisSpacing: 10,
|
||||
childAspectRatio: 104 / 190,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final item = pageItems[index];
|
||||
return _SeriesCard(
|
||||
item: item,
|
||||
coverUrl: _coverUrl,
|
||||
onTap: () => _openDetail(item),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_PagerDots(count: pageCount, currentIndex: _currentPage),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _openDetail(NyEpisodeModel item) {
|
||||
final detail = widget.detailModel;
|
||||
if (detail == null) return;
|
||||
|
||||
final videos = buildEpisodeDramaList(detail);
|
||||
if (videos.isEmpty) return;
|
||||
|
||||
final selected = videos.firstWhere(
|
||||
(video) => video.shortPlayVideoId == item.shortPlayVideoId,
|
||||
orElse: () => videos.first,
|
||||
);
|
||||
AppNav.to(VideoDetailPage(drama: selected));
|
||||
}
|
||||
}
|
||||
|
||||
class _SeriesCard extends StatelessWidget {
|
||||
const _SeriesCard({required this.item, required this.showTag});
|
||||
const _SeriesCard({
|
||||
required this.item,
|
||||
required this.coverUrl,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final _DramaItem item;
|
||||
final bool showTag;
|
||||
final NyEpisodeModel item;
|
||||
final String coverUrl;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 0.68,
|
||||
child: _PosterBlock(item: item, showLargeTitle: true),
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
NovyText(
|
||||
item.title,
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
lineHeight: 1.05,
|
||||
fontFamily: ConstFont.montMedium,
|
||||
maxLines: 2,
|
||||
),
|
||||
if (showTag) ...[
|
||||
const SizedBox(height: 7),
|
||||
NyTagWidget(text: item.category),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PosterBlock extends StatelessWidget {
|
||||
const _PosterBlock({required this.item, this.showLargeTitle = false});
|
||||
|
||||
final _DramaItem item;
|
||||
final bool showLargeTitle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: item.palette,
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onTap,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Positioned(
|
||||
top: 12,
|
||||
left: 10,
|
||||
right: 10,
|
||||
child: Container(
|
||||
height: showLargeTitle ? 74 : 28,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(80),
|
||||
),
|
||||
),
|
||||
AspectRatio(
|
||||
aspectRatio: 0.68,
|
||||
child: HelpImage.network(coverUrl, fit: BoxFit.cover),
|
||||
),
|
||||
Positioned(
|
||||
left: 7,
|
||||
right: 7,
|
||||
bottom: 10,
|
||||
child: NovyText(
|
||||
showLargeTitle ? 'CITIES\nOF SMOKE\nSTARLIGHT' : item.title,
|
||||
color: Colors.white,
|
||||
fontSize: showLargeTitle ? 17 : 8,
|
||||
lineHeight: 0.95,
|
||||
textAlign: TextAlign.center,
|
||||
fontFamily: ConstFont.montBlack,
|
||||
maxLines: showLargeTitle ? 3 : 2,
|
||||
shadow: const Shadow(offset: Offset(1, 1), color: Colors.black54),
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
NovyText(
|
||||
'Episode ${item.episode}',
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
lineHeight: 1.05,
|
||||
fontFamily: ConstFont.montMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -371,37 +290,6 @@ class _PosterBlock extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ActorAvatar extends StatelessWidget {
|
||||
const _ActorAvatar();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 116,
|
||||
height: 116,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [Color(0xFFFFA6D9), Color(0xFFFFE6F5)],
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 58,
|
||||
height: 58,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.55),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: const Icon(Icons.person, size: 44, color: Color(0xFF8E2A55)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ActorStats extends StatelessWidget {
|
||||
const _ActorStats({required this.actor});
|
||||
|
||||
@@ -429,14 +317,14 @@ class _ActorStats extends StatelessWidget {
|
||||
NovyText(
|
||||
stats[index][0],
|
||||
color: Colors.black,
|
||||
fontSize: 18,
|
||||
fontSize: 16,
|
||||
fontFamily: ConstFont.montMedium,
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
NovyText(
|
||||
stats[index][1],
|
||||
color: const Color(0xFF777777),
|
||||
fontSize: 13,
|
||||
fontSize: 12,
|
||||
fontFamily: ConstFont.montRegular,
|
||||
),
|
||||
],
|
||||
@@ -467,8 +355,8 @@ class _SectionHeader extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _OutlinedTitle extends StatelessWidget {
|
||||
const _OutlinedTitle(this.text, {required this.fontSize});
|
||||
class OutlinedTitle extends StatelessWidget {
|
||||
const OutlinedTitle(this.text, {super.key, required this.fontSize});
|
||||
|
||||
final String text;
|
||||
final double fontSize;
|
||||
@@ -505,151 +393,29 @@ class _OutlinedTitle extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _GreenActionButton extends StatelessWidget {
|
||||
const _GreenActionButton({required this.text});
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipPath(
|
||||
clipper: const EqualCutCornerClipper(
|
||||
cutSize: 18,
|
||||
corner: CutCorner.bottomLeft,
|
||||
),
|
||||
child: Container(
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: ConstColor.btGreen,
|
||||
border: Border.all(color: Colors.black, width: 2),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
const Positioned(left: 22, top: 6, child: _GripBars()),
|
||||
const Positioned(right: 10, bottom: 4, child: _GripBars()),
|
||||
Center(
|
||||
child: NovyText(
|
||||
text,
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
fontFamily: ConstFont.montBlack,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CornerEyes extends StatelessWidget {
|
||||
const _CornerEyes();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 18,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned(
|
||||
left: -3,
|
||||
top: -38,
|
||||
child: Transform.rotate(
|
||||
angle: 0.68,
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 84,
|
||||
color: ConstColor.btGreen,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Positioned(left: 18, top: -12, child: _EyeDot()),
|
||||
const Positioned(left: 38, top: -18, child: _EyeDot()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EyeDot extends StatelessWidget {
|
||||
const _EyeDot();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 22,
|
||||
height: 26,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Container(
|
||||
width: 10,
|
||||
height: 10,
|
||||
margin: const EdgeInsets.only(top: 2, right: 2),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.black,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PagerDots extends StatelessWidget {
|
||||
const _PagerDots();
|
||||
const _PagerDots({required this.count, required this.currentIndex});
|
||||
|
||||
final int count;
|
||||
final int currentIndex;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(7, (index) {
|
||||
children: List.generate(count, (index) {
|
||||
final active = index == currentIndex;
|
||||
return Container(
|
||||
width: index == 1 ? 26 : 7,
|
||||
width: active ? 26 : 7,
|
||||
height: 7,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 3),
|
||||
color: index == 1 ? ConstColor.btGreen : const Color(0xFFD9D9D9),
|
||||
color: active ? ConstColor.btGreen : const Color(0xFFD9D9D9),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GripBars extends StatelessWidget {
|
||||
const _GripBars();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: List.generate(4, (index) {
|
||||
return Container(
|
||||
width: 3,
|
||||
height: 8,
|
||||
margin: const EdgeInsets.only(right: 2),
|
||||
color: Colors.black,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DramaItem {
|
||||
const _DramaItem({
|
||||
required this.title,
|
||||
required this.category,
|
||||
required this.palette,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String category;
|
||||
final List<Color> palette;
|
||||
}
|
||||
|
||||
class _ActorInfo {
|
||||
const _ActorInfo({
|
||||
required this.name,
|
||||
@@ -665,3 +431,18 @@ class _ActorInfo {
|
||||
final String height;
|
||||
final String nationality;
|
||||
}
|
||||
|
||||
int episodeSeriesPageCount(int itemCount) {
|
||||
if (itemCount <= 0) return 1;
|
||||
return (itemCount / episodeSeriesPageSize).ceil();
|
||||
}
|
||||
|
||||
List<NyEpisodeModel> episodeSeriesPageItems(
|
||||
List<NyEpisodeModel> items,
|
||||
int pageIndex,
|
||||
) {
|
||||
final start = pageIndex * episodeSeriesPageSize;
|
||||
if (start >= items.length) return [];
|
||||
final end = (start + episodeSeriesPageSize).clamp(0, items.length);
|
||||
return items.sublist(start, end);
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:novyronst/root/lib_export.dart';
|
||||
import 'package:novyronst/tools/widgets/extend_widget.dart';
|
||||
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
|
||||
|
||||
class HomeSearchView extends StatefulWidget {
|
||||
const HomeSearchView({super.key,
|
||||
const HomeSearchView({
|
||||
super.key,
|
||||
this.onlyTap = false,
|
||||
this.onTap,
|
||||
this.showLogo = true,
|
||||
});
|
||||
|
||||
final bool onlyTap;
|
||||
final Function()? onTap;
|
||||
final bool showLogo;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@@ -21,66 +23,92 @@ class HomeSearchView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SearchViewState extends State<HomeSearchView> {
|
||||
|
||||
final _searchController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: AppScreen.navBarHeight,
|
||||
padding: EdgeInsets.only(top: AppScreen.statusBarHeight,
|
||||
left: 16, right: 16,
|
||||
bottom: 5
|
||||
padding: EdgeInsets.only(
|
||||
top: AppScreen.statusBarHeight,
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 5,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 36,
|
||||
child: Row(
|
||||
children: [
|
||||
HelpImage.asset(ConstImage.homeTopLogo, width: 36),
|
||||
const SizedBox(width: 16),
|
||||
if (widget.showLogo) ...[
|
||||
HelpImage.asset(ConstImage.homeTopLogo, width: 36),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
HelpImage.asset(ConstImage.searchBg, height: 36, fit: BoxFit.fill),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 11),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.search, color: Colors.white, size: 24),
|
||||
|
||||
Expanded(child:widget.onlyTap
|
||||
? NovyText('What are...... ',
|
||||
fontSize: 13,
|
||||
color: Colors.white.withAlpha(127),
|
||||
)
|
||||
: CupertinoTextField(
|
||||
controller: _searchController,
|
||||
// focusNode: widget.focusNode,
|
||||
decoration: BoxDecoration(),
|
||||
placeholder: 'What are...... ',
|
||||
placeholderStyle: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withAlpha(127),
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
),
|
||||
|
||||
textInputAction: TextInputAction.search,
|
||||
onEditingComplete: () {
|
||||
|
||||
},
|
||||
),),
|
||||
|
||||
HelpImage.asset(ConstImage.homeSearch, width: 78, height: 36, fit: BoxFit.fill)
|
||||
|
||||
],
|
||||
child: SizedBox(
|
||||
height: 36,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: HelpImage.asset(
|
||||
ConstImage.searchBg,
|
||||
height: 36,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 11, right: 78),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.search,
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: widget.onlyTap
|
||||
? NovyText(
|
||||
'What are...... ',
|
||||
fontSize: 13,
|
||||
color: Colors.white.withAlpha(127),
|
||||
)
|
||||
: CupertinoTextField(
|
||||
controller: _searchController,
|
||||
// focusNode: widget.focusNode,
|
||||
decoration: const BoxDecoration(),
|
||||
placeholder: 'What are...... ',
|
||||
placeholderStyle: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withAlpha(127),
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
),
|
||||
|
||||
textInputAction: TextInputAction.search,
|
||||
onEditingComplete: () {},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: HelpImage.asset(
|
||||
ConstImage.homeSearch,
|
||||
width: 78,
|
||||
height: 36,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -88,11 +116,10 @@ class _SearchViewState extends State<HomeSearchView> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// 搜索页面的搜索框
|
||||
class SeSearchView extends StatefulWidget {
|
||||
const SeSearchView({super.key,
|
||||
const SeSearchView({
|
||||
super.key,
|
||||
this.onClickSearch,
|
||||
required this.controller,
|
||||
this.focusNode,
|
||||
@@ -109,62 +136,77 @@ class SeSearchView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SeSearchViewState extends State<SeSearchView> {
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 44,
|
||||
padding: EdgeInsets.only(top: 4, bottom: 4),
|
||||
child: Stack(
|
||||
children: [
|
||||
HelpImage.asset(ConstImage.searchBg, height: 36, fit: BoxFit.fill),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 11),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.search, color: Colors.white, size: 24),
|
||||
|
||||
Expanded(child: CupertinoTextField(
|
||||
controller: widget.controller,
|
||||
focusNode: widget.focusNode,
|
||||
decoration: BoxDecoration(),
|
||||
placeholder: 'What are...... ',
|
||||
placeholderStyle: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withAlpha(127),
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
),
|
||||
textInputAction: TextInputAction.search,
|
||||
onEditingComplete: () {
|
||||
if (widget.controller.text.trim().isNotEmpty) {
|
||||
widget.onClickSearch?.call(widget.controller.text);
|
||||
}
|
||||
},
|
||||
),),
|
||||
|
||||
HelpImage.asset(ConstImage.homeSearch,
|
||||
width: 78, height: 36, fit: BoxFit.fill
|
||||
).addInkWell(onTap: () {
|
||||
if (widget.controller.text.trim().isNotEmpty) {
|
||||
widget.onClickSearch?.call(widget.controller.text);
|
||||
}
|
||||
})
|
||||
|
||||
],
|
||||
padding: const EdgeInsets.only(top: 4, bottom: 4),
|
||||
child: SizedBox(
|
||||
height: 36,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: HelpImage.asset(
|
||||
ConstImage.searchBg,
|
||||
height: 36,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 11, right: 78),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.search, color: Colors.white, size: 24),
|
||||
|
||||
Expanded(
|
||||
child: CupertinoTextField(
|
||||
controller: widget.controller,
|
||||
focusNode: widget.focusNode,
|
||||
decoration: const BoxDecoration(),
|
||||
placeholder: 'What are...... ',
|
||||
placeholderStyle: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withAlpha(127),
|
||||
),
|
||||
style: TextStyle(fontSize: 14, color: Colors.white),
|
||||
textInputAction: TextInputAction.search,
|
||||
onEditingComplete: () {
|
||||
if (widget.controller.text.trim().isNotEmpty) {
|
||||
widget.onClickSearch?.call(widget.controller.text);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child:
|
||||
HelpImage.asset(
|
||||
ConstImage.homeSearch,
|
||||
width: 78,
|
||||
height: 36,
|
||||
fit: BoxFit.fill,
|
||||
).addInkWell(
|
||||
onTap: () {
|
||||
if (widget.controller.text.trim().isNotEmpty) {
|
||||
widget.onClickSearch?.call(widget.controller.text);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user