diff --git a/assets/imgs/home_rank_bg.png b/assets/imgs/home_rank_bg.png new file mode 100644 index 0000000..73d9487 Binary files /dev/null and b/assets/imgs/home_rank_bg.png differ diff --git a/assets/imgs/hot_bg1.png b/assets/imgs/hot_bg1.png new file mode 100644 index 0000000..bcd2f15 Binary files /dev/null and b/assets/imgs/hot_bg1.png differ diff --git a/assets/imgs/hot_bg2.png b/assets/imgs/hot_bg2.png new file mode 100644 index 0000000..f5f2390 Binary files /dev/null and b/assets/imgs/hot_bg2.png differ diff --git a/assets/imgs/purple_bg.png b/assets/imgs/purple_bg.png new file mode 100644 index 0000000..21fd76d Binary files /dev/null and b/assets/imgs/purple_bg.png differ diff --git a/assets/imgs/red_bg.png b/assets/imgs/red_bg.png new file mode 100644 index 0000000..06afb78 Binary files /dev/null and b/assets/imgs/red_bg.png differ diff --git a/assets/imgs/yellow_bg.png b/assets/imgs/yellow_bg.png new file mode 100644 index 0000000..50f8018 Binary files /dev/null and b/assets/imgs/yellow_bg.png differ diff --git a/assets/pics/back.png b/assets/pics/back.png new file mode 100644 index 0000000..2efc7da Binary files /dev/null and b/assets/pics/back.png differ diff --git a/assets/pics/collection_bg.png b/assets/pics/collection_bg.png new file mode 100644 index 0000000..9754d1d Binary files /dev/null and b/assets/pics/collection_bg.png differ diff --git a/assets/pics/continue_watch.png b/assets/pics/continue_watch.png new file mode 100644 index 0000000..24b4b86 Binary files /dev/null and b/assets/pics/continue_watch.png differ diff --git a/assets/pics/delete.png b/assets/pics/delete.png new file mode 100644 index 0000000..891d2c0 Binary files /dev/null and b/assets/pics/delete.png differ diff --git a/assets/pics/hot.png b/assets/pics/hot.png new file mode 100644 index 0000000..17fd66c Binary files /dev/null and b/assets/pics/hot.png differ diff --git a/assets/pics/like_bg.png b/assets/pics/like_bg.png new file mode 100644 index 0000000..4afdb36 Binary files /dev/null and b/assets/pics/like_bg.png differ diff --git a/assets/pics/like_selected_bg.png b/assets/pics/like_selected_bg.png new file mode 100644 index 0000000..7a9ffa4 Binary files /dev/null and b/assets/pics/like_selected_bg.png differ diff --git a/assets/pics/like_unselect_bg.png b/assets/pics/like_unselect_bg.png new file mode 100644 index 0000000..3f682cd Binary files /dev/null and b/assets/pics/like_unselect_bg.png differ diff --git a/assets/pics/look.png b/assets/pics/look.png new file mode 100644 index 0000000..8f63232 Binary files /dev/null and b/assets/pics/look.png differ diff --git a/assets/pics/playlog_bg.png b/assets/pics/playlog_bg.png new file mode 100644 index 0000000..35daded Binary files /dev/null and b/assets/pics/playlog_bg.png differ diff --git a/assets/pics/search_icon.png b/assets/pics/search_icon.png new file mode 100644 index 0000000..f4577a8 Binary files /dev/null and b/assets/pics/search_icon.png differ diff --git a/assets/pics/star.png b/assets/pics/star.png new file mode 100644 index 0000000..7f88a30 Binary files /dev/null and b/assets/pics/star.png differ diff --git a/lib/global/client/play_client.dart b/lib/global/client/play_client.dart index 1856f15..b733c02 100644 --- a/lib/global/client/play_client.dart +++ b/lib/global/client/play_client.dart @@ -1,6 +1,8 @@ import 'package:skywood/global/request/api_const.dart'; import 'package:skywood/global/request/app_request.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_home.dart'; class PlayClient { @@ -18,4 +20,22 @@ class PlayClient { return []; }); } + + static Future> getCategoriesList() { + return AppRequest.get(ApiStringPlay.getCategoriesList).then((value) { + SkyLog.print('vvvvv: ${value.data}'); + if (!value.isSuccess) return []; + + Map data = value.data['data']; + List? list = data['list']; + + List categoryList = []; + if (list != null) { + // id 28 不显示,删除 + categoryList = list.map((e) => SkywoodCategory.fromJson(e)).toList(); + categoryList.removeWhere((element) => element.id == 28); + } + return categoryList; + }); + } } \ No newline at end of file diff --git a/lib/main/lib_file.dart b/lib/main/lib_file.dart index 8de49a1..bae59e3 100644 --- a/lib/main/lib_file.dart +++ b/lib/main/lib_file.dart @@ -6,5 +6,6 @@ export 'package:skywood/global/const/common_string.dart'; export 'package:skywood/utils/tools/app_screen.dart'; export 'package:skywood/utils/tools/help_img.dart'; +export 'package:skywood/utils/tools/help_nav.dart'; export 'package:flutter_screenutil/flutter_screenutil.dart'; diff --git a/lib/main/main_root.dart b/lib/main/main_root.dart index 093515f..5f88205 100644 --- a/lib/main/main_root.dart +++ b/lib/main/main_root.dart @@ -3,7 +3,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:skywood/pages/home/home_page.dart'; import 'package:skywood/pages/like/like_page.dart'; import 'package:skywood/pages/mine/mine_page.dart'; -import 'package:skywood/pages/search/search_page.dart'; +import 'package:skywood/pages/search/page/search_page.dart'; import 'package:skywood/utils/tools/help_img.dart'; class MainRoot extends StatefulWidget { diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 882e4a7..ab8ada1 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -1,9 +1,14 @@ import 'package:flutter/material.dart'; import 'package:skywood/global/client/play_client.dart'; import 'package:skywood/global/client/user_client.dart'; +import 'package:skywood/global/tool/sky_log.dart'; import 'package:skywood/main/lib_file.dart'; import 'package:skywood/pages/home/widgets/daily_quest_widget.dart'; +import 'package:skywood/pages/home/widgets/home_cate_widget.dart'; +import 'package:skywood/pages/home/widgets/hot_cartridge_widget.dart'; +import 'package:skywood/pages/home/widgets/rank_list_widget.dart'; import 'package:skywood/pages/widgets/common_bg_page.dart'; +import 'package:skywood/utils/models/drama/skywood_category.dart'; import 'package:skywood/utils/models/drama/skywood_home.dart'; class HomePage extends StatefulWidget { @@ -18,22 +23,19 @@ class HomePage extends StatefulWidget { class _MainRootState extends State { List _homeData = []; + List _categories = []; @override void initState() { super.initState(); - _getHomeData(); + // _getHomeData(); + // _getDramaCate(); } void _getHomeData() { - // home_v3_recommand - // new_recommand - // week_ranking - // week_highest_recommend - // marquee - // home_banner - // get_details_recommand + // home_v3_recommand new_recommand week_ranking week_highest_recommend + // marquee home_banner get_details_recommand PlayClient.getHomeData().then((value) { for (SkywoodHome element in value) { print('lele: ${element.moduleKey} : ${element.dataList.length}'); @@ -44,6 +46,15 @@ class _MainRootState extends State { }); } + void _getDramaCate() { + PlayClient.getCategoriesList().then((value) { + print('cateeeee: ${value.length}'); + setState(() { + _categories = value; + }); + }); + } + @override Widget build(BuildContext context) { @@ -67,11 +78,31 @@ class _MainRootState extends State { ), _buildSpace(height: 10), _buildTitleImg(ConstImg.homeTitleHot), - _buildSpace(height: 300), + _buildSpace(), + SliverPadding( + padding: EdgeInsets.symmetric(horizontal: 15.w), + sliver: HotCartridgeWidget( + dramaList: _homeData.firstWhere((element) => element.moduleKey == 'new_recommand').dataList, + ), + ), + _buildSpace(height: 10), _buildTitleImg(ConstImg.homeTitleWeekly), - _buildSpace(height: 300), + SliverPadding( + padding: EdgeInsets.symmetric(horizontal: 15.w), + sliver: SliverToBoxAdapter( + child: RankListWidget( + dramaList: _homeData.firstWhere((element) => element.moduleKey == 'week_ranking').dataList, + ), + ), + ), _buildTitleImg(ConstImg.homeTitleHotCate), - _buildSpace(height: 300), + SliverPadding( + padding: EdgeInsets.symmetric(horizontal: 15.w), + sliver: HomeCateWidget( + categories: _categories, + ), + ), + _buildSpace(height: 100), ], ), ), diff --git a/lib/pages/home/widgets/daily_quest_widget.dart b/lib/pages/home/widgets/daily_quest_widget.dart index b5ff244..92f000e 100644 --- a/lib/pages/home/widgets/daily_quest_widget.dart +++ b/lib/pages/home/widgets/daily_quest_widget.dart @@ -20,7 +20,8 @@ class DailyQuestWidget extends StatelessWidget { double height = 281.h; double width = 345.w; return SliverToBoxAdapter( - child: SizedBox( + child: Container( + padding: EdgeInsets.only(left: 15.w), width: width, height: height, child: Stack( @@ -66,6 +67,7 @@ class DailyQuestWidget extends StatelessWidget { ), child: HelpImg.network(drama.imageUrl, width: 300.w, height: 160.h, fit: BoxFit.cover), ), + SizedBox(height: 4.h), Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -86,7 +88,8 @@ class DailyQuestWidget extends StatelessWidget { ], ) ], - ) + ), + SizedBox(height: 3.h), ], ); } diff --git a/lib/pages/home/widgets/home_cate_widget.dart b/lib/pages/home/widgets/home_cate_widget.dart new file mode 100644 index 0000000..f6e5d67 --- /dev/null +++ b/lib/pages/home/widgets/home_cate_widget.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/main/lib_file.dart'; +import 'package:skywood/utils/models/drama/skywood_category.dart'; + +class HomeCateWidget extends StatelessWidget { + + const HomeCateWidget({super.key, + required this.categories, + }); + + final List categories; + + @override + Widget build(BuildContext context) { + return SliverGrid.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, // 🔥 上下2行 + crossAxisSpacing: 10, // 行间距(垂直方向) + mainAxisSpacing: 10, // 列间距(水平方向) + childAspectRatio: 0.7, // 宽高比(宽度/高度) + ), itemBuilder: (context, index) { + return _buildCateItem(categories[index], index); + } + ); + } + + Widget _buildCateItem(SkywoodCategory category, int index) { + String imgName = index%3 == 0 ? ConstImg.purpleBg : (index%3 == 1 ? ConstImg.yellowBg : ConstImg.redBg); + return Stack( + children: [ + HelpImg.asset(imgName, width: 140.w, height: 131.h, fit: BoxFit.cover), + ], + ); + } +} \ No newline at end of file diff --git a/lib/pages/home/widgets/hot_cartridge_widget.dart b/lib/pages/home/widgets/hot_cartridge_widget.dart new file mode 100644 index 0000000..458b11d --- /dev/null +++ b/lib/pages/home/widgets/hot_cartridge_widget.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:skywood/main/lib_file.dart'; + +import '../../../utils/models/drama/skywood_drama.dart'; + +class HotCartridgeWidget extends StatelessWidget { + + const HotCartridgeWidget({super.key, + required this.dramaList, + }); + + final List dramaList; + + @override + Widget build(BuildContext context) { + return SliverGrid.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, // 每行 2 列 + crossAxisSpacing: 12, // 列间距 + mainAxisSpacing: 5, // 行间距 + childAspectRatio: 110 / 174, // 宽高比 + ), + itemBuilder: (context, index) { + return _buildItem(dramaList[index], index); + }, itemCount: dramaList.length>6 ? 6 : dramaList.length); + } + + Widget _buildItem(SkywoodDrama drama, int index) { + return Stack( + children: [ + SizedBox( + width: 110.w, height: 174.h, + ), + Positioned( + top: 1, left: 5.w, right: 5.w, + child: HelpImg.network(drama.imageUrl, width: 100.w, height: 130.h), + ), + HelpImg.asset(index<3?ConstImg.hotBg1:ConstImg.hotBg2, width: 110.w, height: 174.h) + ], + ); + } +} \ No newline at end of file diff --git a/lib/pages/home/widgets/like_num_widget.dart b/lib/pages/home/widgets/like_num_widget.dart new file mode 100644 index 0000000..173b79a --- /dev/null +++ b/lib/pages/home/widgets/like_num_widget.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:skywood/global/const/common_color.dart'; + +import '../../../utils/tools/help_img.dart'; +import '../../../utils/tools/sky_time_tool.dart'; +import '../../../utils/tools/widgets/sky_common_widget.dart'; + +class LikeNumWidget extends StatelessWidget { + + const LikeNumWidget({super.key, required this.watchTotal, + this.tColor, + }); + + final int watchTotal; + final Color? tColor; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + // Icon(Icons.heart_broken, size: 15, color: Colors.redAccent,), + HelpImg.asset(ConstImg.heart, width: 18.w, height: 18.w), + const SizedBox(width: 3), + SkyText(text: SkyTimeTool.formatNum(watchTotal), + fontSize: 12, + fontWeight: FontWeight.w400, + color: tColor ?? CommonColor.black, + ) + ], + ); + } +} \ No newline at end of file diff --git a/lib/pages/home/widgets/rank_list_widget.dart b/lib/pages/home/widgets/rank_list_widget.dart new file mode 100644 index 0000000..ead2583 --- /dev/null +++ b/lib/pages/home/widgets/rank_list_widget.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/main/lib_file.dart'; +import 'package:skywood/pages/home/widgets/like_num_widget.dart'; +import 'package:skywood/utils/tools/widgets/sky_common_widget.dart'; + +import '../../../utils/models/drama/skywood_drama.dart'; + +class RankListWidget extends StatelessWidget { + + const RankListWidget({super.key, + required this.dramaList, + }); + + final List dramaList; + + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + HelpImg.asset(ConstImg.homeRankBg, width: 325.w, height: 481.h), + Positioned( + left: 24.5.w, right: 24.5.w, + top: 44.5.h, bottom: 34.5.h, + child: ListView.separated( + padding: EdgeInsets.only(right: 10.w), + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) => _itemBuilder(index), + separatorBuilder: (context, index) { + return Divider( + height: 1, thickness: 0.5, + color: CommonColor.withValue('7A67C1'), + ); + }, + itemCount: dramaList.length>6 ? 6:dramaList.length, + ), + ) + ], + ); + } + + Widget _itemBuilder(int index) { + SkywoodDrama drama = dramaList[index]; + return SizedBox( + height: 65.h, width: 296.w, + child: Row( + children: [ + _rankIndex(index), + HelpImg.network(drama.imageUrl, width: 40.w, height: 52.h, fit: BoxFit.cover), + const SizedBox(width: 7), + Expanded(child: SkyText(text: drama.name, + fontSize: 13.w, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + lineHeight: 1.23, + color: CommonColor.black, + maxLines: 2, + )), + SizedBox(width: 8.w), + LikeNumWidget(watchTotal: drama.watchTotal) + ], + ), + ); + } + + Widget _rankIndex(int index) { + return SizedBox( + width: 40.w, + child: SkyText(text: formatNumber(index + 1), + fontSize: 15, + color: CommonColor.black, + fontFamily: 'Anton', + fontWeight: FontWeight.bold, + lineHeight: 1, + ), + ); + } + + String formatNumber(int number) { + return number.toString().padLeft(2, '0'); + } +} \ No newline at end of file diff --git a/lib/pages/like/like_page.dart b/lib/pages/like/like_page.dart index 1e1bd5b..5bffa5f 100644 --- a/lib/pages/like/like_page.dart +++ b/lib/pages/like/like_page.dart @@ -1,6 +1,11 @@ import 'package:flutter/material.dart'; import 'package:skywood/main/lib_file.dart'; +import 'package:skywood/pages/like/pages/collection_page.dart'; +import 'package:skywood/pages/like/pages/play_log_page.dart'; +import 'package:skywood/pages/like/widget/like_header.dart'; +import 'package:skywood/pages/widgets/common_bg_page.dart'; import 'package:skywood/pages/widgets/pixel_widgets.dart'; +import 'package:skywood/utils/extend/app_keep_alive.dart'; class LikePage extends StatefulWidget { const LikePage({super.key}); @@ -13,6 +18,7 @@ class LikePage extends StatefulWidget { class _MainRootState extends State { int _tabIndex = 0; + final _pageController = PageController(initialPage: 0); final List _collectionTitles = const [ 'Undercover Play', @@ -25,32 +31,44 @@ class _MainRootState extends State { @override Widget build(BuildContext context) { - return Scaffold( - body: PixelPageBackground( - child: SafeArea( + return CommonBgPage( + child: SafeArea( bottom: false, child: Padding( - padding: EdgeInsets.fromLTRB(15.w, 44.h, 15.w, 0), - child: PixelPanel( - padding: EdgeInsets.fromLTRB(10.w, 18.h, 10.w, 22.h), - child: Column( - children: [ - _buildTabs(), - SizedBox(height: 20.h), - Expanded( - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 180), - child: _tabIndex == 0 - ? _buildCollection() - : _buildPlayLog(), - ), + padding: EdgeInsets.only(left: 15.w, right: 15.w, top: 20.h, bottom: 10.h+50.h), + child: Stack( + children: [ + HelpImg.asset(ConstImg.likeBg, width: AppScreen.screenWidth - 30.w, height: 700.h, + fit: BoxFit.fill, + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(left: 10, top: 10), + child: LikeHeader( + onSelectedIndex: (index) { + _pageController.jumpToPage(index); + }, + ), + ), + // 主页面 + Expanded( + child: PageView( + controller: _pageController, + children: [ + AppKeepAlive(child: CollectionPage()), + AppKeepAlive(child: PlayLogPage()) + ], + ), + ) + ], ), - ], - ), + ), + ], ), - ), - ), - ), + )), ); } diff --git a/lib/pages/like/pages/collection_page.dart b/lib/pages/like/pages/collection_page.dart new file mode 100644 index 0000000..216dc26 --- /dev/null +++ b/lib/pages/like/pages/collection_page.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/main/lib_file.dart'; + +class CollectionPage extends StatefulWidget { + + const CollectionPage({super.key}); + + @override + State createState() { + return _CollectionPageState(); + } +} + +class _CollectionPageState extends State { + + @override + Widget build(BuildContext context) { + return GridView.builder( + padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 10.h), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 4, + crossAxisSpacing: 10, + childAspectRatio: 100 / 174.0 + ), + itemBuilder: (context, index) { + return _buildItem(index); + }, + itemCount: 5, + ); + } + + Widget _buildItem(int index) { + return Stack( + children: [ + HelpImg.asset(ConstImg.collectionBg, width: 100.w, height: 174.w, fit: BoxFit.fill,), + + ], + ); + } +} \ No newline at end of file diff --git a/lib/pages/like/pages/play_log_page.dart b/lib/pages/like/pages/play_log_page.dart new file mode 100644 index 0000000..5a1174a --- /dev/null +++ b/lib/pages/like/pages/play_log_page.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/main/lib_file.dart'; +import 'package:skywood/utils/tools/widgets/sky_common_widget.dart'; + +class PlayLogPage extends StatefulWidget { + + const PlayLogPage({super.key}); + + @override + State createState() { + return _CollectionPageState(); + } +} + +class _CollectionPageState extends State { + + @override + Widget build(BuildContext context) { + return ListView.separated( + padding: const EdgeInsets.only(top: 20, left: 12, right: 12), + itemCount: 5, + itemBuilder: (context, index) { + return index == 0 ? _buildFirstItem() : _buildNormalItem(); + }, + separatorBuilder: (context, index) { + return SizedBox(height: 5); + } + ); + } + + Widget _buildNormalItem() { + return Stack( + children: [ + HelpImg.asset(ConstImg.playlogBg, width: double.infinity, height: 90, fit: BoxFit.fill), + _ProgressLine(value: 0.2) + ], + ); + } + + Widget _buildFirstItem() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + HelpImg.asset(ConstImg.playlogBg, width: double.infinity, height: 140, fit: BoxFit.fill), + const SizedBox(height: 20), + SkyText(text: 'Watch History', + color: CommonColor.black, + fontSize: 16, + fontWeight: FontWeight.bold, + ) + ], + ); + } + +} + + +class _ProgressLine extends StatelessWidget { + const _ProgressLine({required this.value}); + + final double value; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: Container( + height: 6.h, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.7), + border: Border.all(color: const Color(0xFF1C1E1F), width: 0.5.w), + borderRadius: BorderRadius.circular(10.r), + ), + alignment: Alignment.centerLeft, + child: FractionallySizedBox( + widthFactor: value, + child: Container( + decoration: BoxDecoration( + color: const Color(0xFF00EE02), + borderRadius: BorderRadius.circular(10.r), + ), + ), + ), + ), + ), + SizedBox(width: 4.w), + Text('${(value * 100.0).toInt()}%', style: TextStyle(fontSize: 10.sp, height: 1)), + ], + ); + } +} \ No newline at end of file diff --git a/lib/pages/like/widget/like_header.dart b/lib/pages/like/widget/like_header.dart new file mode 100644 index 0000000..43f4bb3 --- /dev/null +++ b/lib/pages/like/widget/like_header.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/main/lib_file.dart'; +import 'package:skywood/utils/tools/widgets/sky_common_widget.dart'; + +class LikeHeader extends StatefulWidget { + + const LikeHeader({super.key, + required this.onSelectedIndex, + }); + + final Function(int) onSelectedIndex; + + @override + State createState() { + return _LikeHeaderState(); + } +} + +class _LikeHeaderState extends State { + + int _selectedIndex = 0; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 55, + child: Row( + children: [ + _buildCollectionBg(0, 'My collection'), + _buildCollectionBg(1, 'Play Log'), + ], + ), + ); + } + + + // shadows: [Shadow(offset: Offset(1, 1), blurRadius: 0, color: Color(0xFF040408).withOpacity(1.00))], + Widget _buildCollectionBg(int index, String title) { + String img = index == _selectedIndex ? ConstImg.likeSelectedBg : ConstImg.likeUnselectBg; + return SizedBox( + width: 155.w, height: 55, + child: Stack( + children: [ + HelpImg.asset(img, width: 155.w, height: 55, fit: BoxFit.fill), + Positioned( + right: 22, bottom: 12, left: 28, + child: SkyText(text: title, + fontSize: 14, + fontWeight: FontWeight.w800, + color: index == _selectedIndex ? Colors.white : CommonColor.withValue('B1B1B1'), + shadow: index == _selectedIndex + ? Shadow(offset: Offset(1, 1), blurRadius: 0, color: Color(0xFF040408).withOpacity(1.00)) + : null, + textAlign: TextAlign.center, + ), + ) + ], + ), + ).addInkWell(onTap: () { + if (_selectedIndex == index) return; + setState(() { + _selectedIndex = index; + }); + widget.onSelectedIndex(index); + },); + } + + _buildLogBg() { + return Container( + width: 160.w, height: 55, + child: HelpImg.asset(ConstImg.playlogBg, fit: BoxFit.fill), + ).addInkWell(onTap: () { + if (_selectedIndex == 1) return; + setState(() { + _selectedIndex = 1; + }); + },); + } +} \ No newline at end of file diff --git a/lib/pages/search/page/result_page.dart b/lib/pages/search/page/result_page.dart new file mode 100644 index 0000000..d4993c8 --- /dev/null +++ b/lib/pages/search/page/result_page.dart @@ -0,0 +1,155 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/main/lib_file.dart'; +import 'package:skywood/pages/widgets/pixel_widgets.dart'; +import 'package:skywood/utils/tools/widgets/sky_app_bar.dart'; +import 'package:skywood/utils/tools/widgets/sky_common_widget.dart'; + +import '../../../utils/tools/widgets/sky_text_field.dart'; + +class ResultPage extends StatefulWidget { + + const ResultPage({super.key, + required this.keyword, + }); + + final String keyword; + + @override + State createState() { + return _ResultPageState(); + } +} + +class _ResultPageState extends State { + + List _searchHistory = []; + final _searchController = TextEditingController(); + final _searchFocus = FocusNode(); + + @override + void initState() { + super.initState(); + + _searchController.text = widget.keyword; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: SkyAppBar( + + titleWidget: SkyTextField( + controller: _searchController, + focusNode: _searchFocus, + border: Border.all(width: 0.6, color: CommonColor.black), + borderRadius: BorderRadius.circular(20), + width: 300, height: 36, + placeholder: 'Search', + prefix: HelpImg.asset(ConstImg.search, width: 14.w, height: 14.w), + textInputAction: TextInputAction.search, + onEditingComplete: () { + print('object'); + _searchFocus.unfocus(); + if (_searchController.text.trim().isEmpty) return; + // _onGoSearch(_searchController.text); + }, + ), + ), + body: ListView.separated( + itemBuilder: (context, index) => ResultItemView(), + separatorBuilder: (context, index) => SizedBox(height: 12), + itemCount: 2 + ), + ); + } +} + +class ResultItemView extends StatelessWidget { + + const ResultItemView({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 140.h, + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Row( + children: [ + PosterImage(width: 105.w, height: 140.h), + const SizedBox(width: 10), + Expanded(child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SkyText(text: 'Love in the City of Twilight', + fontSize: 14, + fontWeight: FontWeight.bold, + color: CommonColor.black, + ), + const SizedBox(height: 12), + _tagView(), + const SizedBox(height: 12), + SkyText(text: 'Haunted by fading memories, a man navigates a labyrinth of dreams and reality ds dsajkj', + fontSize: 11, + color: CommonColor.withValue('4D4047'), + maxLines: 2, + ), + Spacer(), + Row( + children: [ + _tipView(true, text: '10.0'), + const SizedBox(width: 15), + _tipView(false, text: '10.2k'), + ], + ) + ], + )) + ], + ), + ); + } + + Widget _tipView(bool isStar, {required String text}) { + String imgName = isStar ? ConstImg.star : ConstImg.look; + return Row( + children: [ + HelpImg.asset(imgName, width: 12.w, height: 12.w), + const SizedBox(width: 2), + SkyText(text: text, fontSize: 10, color: Colors.grey) + ], + ); + } + + Widget _tagView() { + return Container( + padding: const EdgeInsets.only(top: 3, left: 6, right: 6, bottom: 4), + decoration: ShapeDecoration( + color: const Color(0xFFEBFFE5), + shape: RoundedRectangleBorder( + side: BorderSide( + width: 0.50, + color: const Color(0xFF9BE89C), + ), + borderRadius: BorderRadius.circular(1), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 10, + children: [ + Text( + 'Satisfying', + style: TextStyle( + color: const Color(0xFF040408) /* 主题黑 */, + fontSize: 10, + fontFamily: 'Inter', + fontWeight: FontWeight.w400, + height: 1, + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/pages/search/page/search_page.dart b/lib/pages/search/page/search_page.dart new file mode 100644 index 0000000..bfea2dd --- /dev/null +++ b/lib/pages/search/page/search_page.dart @@ -0,0 +1,209 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/main/lib_file.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_view.dart'; +import 'package:skywood/pages/widgets/pixel_widgets.dart'; +import 'package:skywood/utils/tools/help_nav.dart'; +import 'package:skywood/utils/tools/widgets/sky_app_bar.dart'; +import 'package:skywood/utils/tools/widgets/sky_text_field.dart'; + +class SearchPage extends StatefulWidget { + const SearchPage({super.key}); + + @override + State createState() { + return _MainRootState(); + } +} + +class _MainRootState extends State { + + List _searchHistory = []; + final _searchController = TextEditingController(); + final _searchFocus = FocusNode(); + + final List _titles = const [ + "Mr. Gu's Sweet Weakness", + 'Secret Wife with a Scar', + 'A Second Chance at Love', + 'Shadows of the Heart', + 'Boarding Pass To Love', + 'The Love Blueprint', + 'Who Dared Touch My Bride', + 'Famous Families Fled', + ]; + + Future _loadSearchHistory() async { + final list = await SearchUtil.getSearchList(); + if (!mounted) return; + setState(() { + _searchHistory = list; + }); + } + + Future _onGoSearch(String keyword, {bool isTapKey = false}) async { + if (isTapKey == true) { + + } else { + await SearchUtil.addSearch(keyword); + await _loadSearchHistory(); + } + HelpNav.to(ResultPage( + keyword: keyword, + )); + // 发起 搜索 + + } + + @override + void initState() { + super.initState(); + + _loadSearchHistory(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: SkyAppBar( + showBackButton: false, + titleWidget: SkyTextField( + controller: _searchController, + focusNode: _searchFocus, + border: Border.all(width: 0.6, color: CommonColor.black), + borderRadius: BorderRadius.circular(20), + width: 300, height: 36, + placeholder: 'Search', + prefix: HelpImg.asset(ConstImg.search, width: 14.w, height: 14.w), + textInputAction: TextInputAction.search, + onEditingComplete: () { + print('object'); + _searchFocus.unfocus(); + if (_searchController.text.trim().isEmpty) return; + _onGoSearch(_searchController.text); + }, + ), + ), + body: CustomScrollView( + slivers: [ + SliverToBoxAdapter(child: _buildHistory()), + SliverToBoxAdapter(child: SizedBox(height: 30.h)), + SliverToBoxAdapter(child: _buildTitle('Trending Now')), + SliverToBoxAdapter(child: SizedBox(height: 10.h)), + SliverToBoxAdapter(child: _buildTrendingList()), + SliverToBoxAdapter(child: SizedBox(height: 20.h)), + ], + ), + ); + } + + + Widget _buildHistory() { + return SearchView( + searchHistory: _searchHistory, + onTapClear: () { + _searchHistory.clear(); + setState(() {}); + }, + onTapHistory: (value) { + print('object: $value'); + HelpNav.to(ResultPage(keyword: value)); + }, + ); + } + + Widget _buildTitle(String title, {double fontSize = 18}) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 15.w), + child: Text( + title, + style: TextStyle( + color: CommonColor.black, + fontSize: fontSize.sp, + fontWeight: FontWeight.w700, + height: 1.1, + ), + ), + ); + } + + Widget _buildTrendingList() { + return SizedBox( + height: 446.h, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + padding: EdgeInsets.symmetric(horizontal: 15.w), + child: Wrap( + direction: Axis.vertical, + spacing: 10.h, + runSpacing: 10.w, + children: _titles + .map((title) => _TrendingItem(title: title)) + .toList(), + ), + ), + ); + } +} + +class _TrendingItem extends StatelessWidget { + const _TrendingItem({required this.title}); + + final String title; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 236.w, + height: 104.h, + child: Row( + children: [ + const PosterImage( + width: 80, + height: 104, + borderRadius: 4, + showBorder: false, + ), + SizedBox(width: 8.w), + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 10.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: CommonColor.black, + 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, + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/search/search_page.dart b/lib/pages/search/search_page.dart deleted file mode 100644 index af6f9f9..0000000 --- a/lib/pages/search/search_page.dart +++ /dev/null @@ -1,234 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:skywood/main/lib_file.dart'; -import 'package:skywood/pages/widgets/pixel_widgets.dart'; - -class SearchPage extends StatefulWidget { - const SearchPage({super.key}); - - @override - State createState() { - return _MainRootState(); - } -} - -class _MainRootState extends State { - final List _titles = const [ - "Mr. Gu's Sweet Weakness", - 'Secret Wife with a Scar', - 'A Second Chance at Love', - 'Shadows of the Heart', - 'Boarding Pass To Love', - 'The Love Blueprint', - 'Who Dared Touch My Bride', - 'Famous Families Fled', - ]; - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white, - body: SafeArea( - bottom: false, - child: CustomScrollView( - slivers: [ - SliverToBoxAdapter(child: _buildSearchBar()), - SliverToBoxAdapter(child: SizedBox(height: 16.h)), - SliverToBoxAdapter(child: _buildHistory()), - SliverToBoxAdapter(child: SizedBox(height: 30.h)), - SliverToBoxAdapter(child: _buildTitle('Trending Now')), - SliverToBoxAdapter(child: SizedBox(height: 10.h)), - SliverToBoxAdapter(child: _buildTrendingList()), - SliverToBoxAdapter(child: SizedBox(height: 120.h)), - ], - ), - ), - ); - } - - Widget _buildSearchBar() { - return Padding( - padding: EdgeInsets.fromLTRB(15.w, 10.h, 15.w, 0), - child: Row( - children: [ - Container( - width: 34.w, - height: 34.w, - decoration: BoxDecoration( - color: const Color(0xFFB6FFA7), - border: Border.all(color: CommonColor.black, width: 1.w), - boxShadow: const [ - BoxShadow(color: Color(0xFF91F473), offset: Offset(2, 2)), - ], - ), - child: Icon( - Icons.arrow_back_ios_new, - size: 16.w, - color: CommonColor.black, - ), - ), - SizedBox(width: 10.w), - Expanded( - child: Container( - height: 36.h, - padding: EdgeInsets.symmetric(horizontal: 12.w), - decoration: BoxDecoration( - border: Border.all(color: CommonColor.black, width: 1.w), - borderRadius: BorderRadius.circular(40.r), - ), - child: Row( - children: [ - Icon(Icons.search, size: 16.w, color: CommonColor.black), - SizedBox(width: 7.w), - Text( - 'Search', - style: TextStyle( - color: const Color(0xFF3E3D40), - fontSize: 12.sp, - height: 1, - ), - ), - ], - ), - ), - ), - ], - ), - ); - } - - Widget _buildHistory() { - return Padding( - padding: EdgeInsets.symmetric(horizontal: 15.w), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded(child: _buildTitle('Search History', fontSize: 14)), - Icon( - Icons.delete_outline, - size: 18.w, - color: const Color(0xFF888888), - ), - ], - ), - SizedBox(height: 10.h), - Container( - padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h), - decoration: BoxDecoration( - color: const Color(0x0D000000), - borderRadius: BorderRadius.circular(20.r), - ), - child: Text( - 'Love', - style: TextStyle( - color: const Color(0x80000000), - fontSize: 12.sp, - fontWeight: FontWeight.w500, - height: 1, - ), - ), - ), - ], - ), - ); - } - - Widget _buildTitle(String title, {double fontSize = 18}) { - return Padding( - padding: EdgeInsets.symmetric(horizontal: 15.w), - child: Text( - title, - style: TextStyle( - color: CommonColor.black, - fontSize: fontSize.sp, - fontWeight: FontWeight.w700, - height: 1.1, - ), - ), - ); - } - - Widget _buildTrendingList() { - return SizedBox( - height: 446.h, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - padding: EdgeInsets.symmetric(horizontal: 15.w), - child: Wrap( - direction: Axis.vertical, - spacing: 10.h, - runSpacing: 10.w, - children: _titles - .map((title) => _TrendingItem(title: title)) - .toList(), - ), - ), - ); - } -} - -class _TrendingItem extends StatelessWidget { - const _TrendingItem({required this.title}); - - final String title; - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 236.w, - height: 104.h, - child: Row( - children: [ - const PosterImage( - width: 80, - height: 104, - borderRadius: 4, - showBorder: false, - ), - SizedBox(width: 8.w), - Expanded( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 10.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - title, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: CommonColor.black, - fontSize: 13.sp, - fontWeight: FontWeight.w500, - height: 1.2, - ), - ), - Row( - children: [ - Icon( - Icons.local_fire_department, - size: 15.w, - color: const Color(0xFFFF6A00), - ), - SizedBox(width: 2.w), - Text( - '17.3K', - style: TextStyle( - color: const Color(0x80000000), - fontSize: 10.sp, - height: 1, - ), - ), - ], - ), - ], - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/pages/search/widget/search_util.dart b/lib/pages/search/widget/search_util.dart new file mode 100644 index 0000000..20b9b09 --- /dev/null +++ b/lib/pages/search/widget/search_util.dart @@ -0,0 +1,63 @@ +import 'dart:convert'; + +import 'package:shared_preferences/shared_preferences.dart'; +import '../../../utils/tools/app_sp.dart'; + +/// 本地搜索历史(SharedPreferences),key 与 [AppSp] 规则一致(`.search.history`)。 +class SearchUtil { + SearchUtil._(); + + static const String _storageKey = 'sky.search.history'; + static const int _maxHistory = 20; + + static Future> _readList(SharedPreferences prefs) async { + final raw = prefs.getString(_storageKey); + if (raw == null || raw.isEmpty) return []; + try { + final decoded = jsonDecode(raw); + if (decoded is! List) return []; + return decoded.map((e) => e.toString()).where((s) => s.isNotEmpty).toList(); + } catch (_) { + return []; + } + } + + static Future _writeList(SharedPreferences prefs, List list) async { + await prefs.setString(_storageKey, jsonEncode(list)); + } + + /// 查询搜索历史列表(新记录在前)。 + static Future> getSearchList() async { + final prefs = await AppSp.getInstance(); + return _readList(prefs); + } + + /// 添加一条搜索;去重、去空白,并移到最前。 + static Future addSearch(String keyword) async { + final q = keyword.trim(); + if (q.isEmpty) return; + + final prefs = await AppSp.getInstance(); + final list = await _readList(prefs); + list.removeWhere((e) => e == q); + list.insert(0, q); + if (list.length > _maxHistory) { + list.removeRange(_maxHistory, list.length); + } + await _writeList(prefs, list); + } + + /// 删除单条搜索记录。 + static Future deleteSearch(String keyword) async { + final prefs = await AppSp.getInstance(); + final list = await _readList(prefs); + list.removeWhere((e) => e == keyword); + await _writeList(prefs, list); + } + + /// 清空全部搜索历史。 + static Future clearAllSearch() async { + final prefs = await AppSp.getInstance(); + await prefs.remove(_storageKey); + } +} diff --git a/lib/pages/search/widget/search_view.dart b/lib/pages/search/widget/search_view.dart new file mode 100644 index 0000000..c18f888 --- /dev/null +++ b/lib/pages/search/widget/search_view.dart @@ -0,0 +1,89 @@ + +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:skywood/global/const/common_color.dart'; +import 'package:skywood/main/lib_file.dart'; +import 'package:skywood/utils/tools/widgets/sky_common_widget.dart'; + +class SearchView extends StatelessWidget { + const SearchView({super.key, + required this.searchHistory, + this.onTapHistory, + this.onTapClear, + }); + + final List searchHistory; + final ValueChanged? onTapHistory; + final Function()? onTapClear; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 15.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded(child: SkyText(text: 'Search History', + fontSize: 14, + color: CommonColor.black, + fontWeight: FontWeight.bold, + lineHeight: 1, + )), + IconButton(onPressed: () { + onTapClear?.call(); + }, icon: HelpImg.asset(ConstImg.delete, width: 16.w, height: 16.w)), + ], + ), + Wrap( + spacing: 16.w, + runSpacing: 14.h, + children: searchHistory + .map( + (item) => _HistoryChip( + text: item, + onTap: () => onTapHistory?.call(item), + ), + ) + .toList(), + ), + ], + ), + ); + } +} + + +class _HistoryChip extends StatelessWidget { + const _HistoryChip({ + required this.text, + this.onTap, + }); + + final String text; + final VoidCallback? onTap; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h), + decoration: BoxDecoration( + color: const Color(0x0D000000), + borderRadius: BorderRadius.circular(20.r), + ), + child: Text( + text, + style: TextStyle( + color: const Color(0x80000000), + fontSize: 12.sp, + fontWeight: FontWeight.w500, + height: 1, + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/utils/tools/help_img.dart b/lib/utils/tools/help_img.dart index 8936843..37f36b5 100644 --- a/lib/utils/tools/help_img.dart +++ b/lib/utils/tools/help_img.dart @@ -32,7 +32,33 @@ class ConstImg { // splash_page static const String splashPage = "assets/imgs/splash_page.png"; // heart - static const String heart = "assets/images/heart.png"; + static const String heart = "assets/imgs/heart.png"; + // hot_bg1 + static const String hotBg1 = "assets/imgs/hot_bg1.png"; + static const String hotBg2 = "assets/imgs/hot_bg2.png"; + // home_rank_bg + static const String homeRankBg = "assets/imgs/home_rank_bg.png"; + // yellow_bg purple red + static const String yellowBg = "assets/imgs/yellow_bg.png"; + static const String purpleBg = "assets/imgs/purple_bg.png"; + static const String redBg = "assets/imgs/red_bg.png"; + + /// pics + static const String delete = "assets/pics/delete.png"; + static const String back = "assets/pics/back.png"; + static const String search = "assets/pics/search_icon.png"; + static const String hot = "assets/pics/hot.png"; + static const String star = "assets/pics/star.png"; + static const String look = "assets/pics/look.png"; + // bgs + static const String likeBg = "assets/pics/like_bg.png"; + static const String collectionBg = "assets/pics/collection_bg.png"; + static const String playlogBg = "assets/pics/playlog_bg.png"; + // like_selected_bg + static const String likeSelectedBg = "assets/pics/like_selected_bg.png"; + static const String likeUnselectBg = "assets/pics/like_unselect_bg.png"; + static const String continueWatch = "assets/pics/continue_watch.png"; + // nodata static const String noData = "assets/images/nodata.png"; diff --git a/lib/utils/tools/sky_time_tool.dart b/lib/utils/tools/sky_time_tool.dart index a93e7e5..26edb65 100644 --- a/lib/utils/tools/sky_time_tool.dart +++ b/lib/utils/tools/sky_time_tool.dart @@ -43,11 +43,11 @@ class SkyTimeTool { if (value < 1000) { return addSeparator(value.toStringAsFixed(0)); } else if (value < 1000000) { - final formatted = (value / 1000).toStringAsFixed(decimalDigits); - return '${removeTrailingZeros(formatted)}K'; + final formatted = (value / 10000).toStringAsFixed(decimalDigits); + return '${removeTrailingZeros(formatted)}w'; } else { final formatted = (value / 1000000).toStringAsFixed(decimalDigits); - return '${removeTrailingZeros(formatted)}M'; + return '${removeTrailingZeros(formatted)}m'; } } } \ No newline at end of file diff --git a/lib/utils/tools/widgets/sky_app_bar.dart b/lib/utils/tools/widgets/sky_app_bar.dart new file mode 100644 index 0000000..8b422e6 --- /dev/null +++ b/lib/utils/tools/widgets/sky_app_bar.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/main/lib_file.dart'; +import 'package:skywood/utils/tools/widgets/sky_common_widget.dart'; + +import '../help_nav.dart'; + + +class SkyAppBar extends StatelessWidget implements PreferredSizeWidget { + const SkyAppBar({ + super.key, + this.title, + this.titleWidget, + this.leading, + this.actions, + this.onBack, + this.showBackButton = true, + this.centerTitle = true, + this.backgroundColor = Colors.white, + this.foregroundColor, + this.elevation = 0, + this.bottom, + this.toolbarHeight = kToolbarHeight, + }); + + final String? title; + final Widget? titleWidget; + final Widget? leading; + final List? actions; + final VoidCallback? onBack; + final bool showBackButton; + final bool centerTitle; + final Color? backgroundColor; + final Color? foregroundColor; + final double elevation; + final PreferredSizeWidget? bottom; + final double toolbarHeight; + + @override + Size get preferredSize => + Size.fromHeight(toolbarHeight + (bottom?.preferredSize.height ?? 0)); + + @override + Widget build(BuildContext context) { + + return _buildNormalAppBar(context); + } + + PreferredSizeWidget _buildNormalAppBar(BuildContext context) { + + final canPop = Navigator.of(context).canPop(); + + Widget? resolvedLeading = leading; + if (resolvedLeading == null && showBackButton && canPop) { + resolvedLeading = IconButton( + icon: HelpImg.asset(ConstImg.back, width: 32), + onPressed: onBack ?? () => HelpNav.back(), + ); + } + + return AppBar( + title: titleWidget ?? (title != null + ? SkyText(text: title!, fontSize: 18, fontWeight: FontWeight.bold) + : null), + leading: resolvedLeading, + actions: actions, + centerTitle: centerTitle, + backgroundColor: backgroundColor, + foregroundColor: foregroundColor, + elevation: elevation, + scrolledUnderElevation: 0, + toolbarHeight: toolbarHeight, + ); + } + + +} diff --git a/lib/utils/tools/widgets/sky_text_field.dart b/lib/utils/tools/widgets/sky_text_field.dart new file mode 100644 index 0000000..d43e8e5 --- /dev/null +++ b/lib/utils/tools/widgets/sky_text_field.dart @@ -0,0 +1,131 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../../global/const/common_color.dart'; + +class SkyTextField extends StatefulWidget { + + const SkyTextField({ + super.key, + this.controller, + this.focusNode, + this.borderRadius = const BorderRadius.all(Radius.circular(7)), + this.padding = const EdgeInsets.only(left: 10, right: 10, top: 2, bottom: 2), + this.placeholder, + this.clearButtonMode = OverlayVisibilityMode.never, + this.prefix, + this.suffix, + this.keyboardType, + this.fontSize, + this.maxLines = 1, + this.maxLength, + this.onChanged, + this.inputFormatters, + this.height = 40.0, + this.width, + this.obscureText = false, + this.bgColor, + this.textInputAction, + this.onSubmitted, + this.onTap, + this.readOnly, + this.border, + this.onEditingComplete, + this.textColor, + this.showBottomBorder, + }); + + final Function()? onEditingComplete; + final Color? textColor; + final double? width; + final bool? readOnly; + final GestureTapCallback? onTap; + final ValueChanged? onSubmitted; + final TextInputAction? textInputAction; + final TextEditingController? controller; + final FocusNode? focusNode; + + final BorderRadius borderRadius; + final Border? border; + final EdgeInsetsGeometry padding; + final String? placeholder; + // final Color? placeholderColor; + final OverlayVisibilityMode clearButtonMode; + final Widget? prefix; + final Widget? suffix; + final TextInputType? keyboardType; + final double? fontSize; + final int? maxLines; + final int? maxLength; + final ValueChanged? onChanged; + final List? inputFormatters; + final double? height; + final bool obscureText; + final Color? bgColor; + final bool? showBottomBorder; + + @override + State createState() { + return _ComTextFieldState(); + } +} + +class _ComTextFieldState extends State { + + + @override + Widget build(BuildContext context) { + + + return Container( + height: widget.height, width: widget.width, + decoration: BoxDecoration( + color: widget.bgColor ?? Colors.white, + borderRadius: widget.borderRadius, + border: widget.border + ), + padding: widget.padding, + child: Row( + children: [ + Expanded( + child: CupertinoTextField( + controller: widget.controller, + readOnly: widget.readOnly ?? false, + focusNode: widget.focusNode, + decoration: widget.showBottomBorder==true ? BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey.shade400, + width: 0.5, + ), + ), + ) : BoxDecoration(), + placeholder: widget.placeholder, + placeholderStyle: TextStyle( + fontSize: widget.fontSize ?? 14, + color: CommonColor.withValue('#707488'), + ), + style: TextStyle( + fontSize: widget.fontSize ?? 14, + color: widget.textColor ?? Colors.black, + ), + prefix: widget.prefix, + clearButtonMode: widget.clearButtonMode, + keyboardType: widget.keyboardType, + maxLines: widget.maxLines, + maxLength: widget.maxLength, + onChanged: widget.onChanged, + inputFormatters: widget.inputFormatters, + obscureText: widget.obscureText, + textInputAction: widget.textInputAction ?? TextInputAction.done, + onSubmitted: widget.onSubmitted, + onTap: widget.onTap, + onEditingComplete: widget.onEditingComplete, + )), + if (widget.suffix != null) widget.suffix!, + ], + ), + ); + } +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 51e0a05..8702e58 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,6 +71,8 @@ flutter: assets: - assets/common/ - assets/imgs/ + - assets/pics/ + - assets/mine/ # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see