diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index bcde246e..afcfcbc0 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -30,7 +30,7 @@ class _HomePageState extends State stream = _homeController.searchBarStream.stream; } - showUserBottonSheet() { + showUserBottomSheet() { feedBack(); showModalBottomSheet( context: context, @@ -49,47 +49,47 @@ class _HomePageState extends State return Scaffold( extendBody: true, extendBodyBehindAppBar: true, - appBar: AppBar(toolbarHeight: 0, elevation: 0), - body: Column( + body: Stack( children: [ - CustomAppBar( - stream: _homeController.hideSearchBar - ? stream - : StreamController.broadcast().stream, - ctr: _homeController, - callback: showUserBottonSheet, - ), - const SizedBox(height: 8), - SizedBox( - width: double.infinity, - height: 42, - child: Align( - alignment: Alignment.center, - child: TabBar( - controller: _homeController.tabController, - tabs: [ - for (var i in _homeController.tabs) Tab(text: i['label']) - ], - isScrollable: true, - dividerColor: Colors.transparent, - enableFeedback: true, - splashBorderRadius: BorderRadius.circular(10), - tabAlignment: TabAlignment.center, - onTap: (value) { - feedBack(); - if (_homeController.initialIndex == value) { - _homeController.tabsCtrList[value]().animateToTop(); - } - _homeController.initialIndex = value; - }, + // gradient background + Align( + alignment: Alignment.topLeft, + child: Opacity( + opacity: 0.6, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.primary.withOpacity(0.9), + Theme.of(context).colorScheme.primary.withOpacity(0.5), + Theme.of(context).colorScheme.surface + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + stops: const [0, 0.0034, 0.34]), + ), ), ), ), - Expanded( - child: TabBarView( - controller: _homeController.tabController, - children: _homeController.tabsPageList, - ), + Column( + children: [ + CustomAppBar( + stream: _homeController.hideSearchBar + ? stream + : StreamController.broadcast().stream, + ctr: _homeController, + callback: showUserBottomSheet, + ), + const CustomTabs(), + Expanded( + child: TabBarView( + controller: _homeController.tabController, + children: _homeController.tabsPageList, + ), + ), + ], ), ], ), @@ -128,24 +128,33 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { duration: const Duration(milliseconds: 500), height: snapshot.data ? MediaQuery.of(context).padding.top + 52 - : MediaQuery.of(context).padding.top - 10, + : MediaQuery.of(context).padding.top + 5, child: Container( padding: EdgeInsets.only( - left: 20, - right: 20, + left: 8, + right: 8, bottom: 0, top: MediaQuery.of(context).padding.top + 4, ), child: Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ - const Expanded(child: SearchPage()), + Expanded( + child: Row( + children: [ + Image.asset( + 'assets/images/logo/logo_android_2.png', + height: 48, + ), + ], + )), + const SearchPage(), if (ctr!.userLogin.value) ...[ - const SizedBox(width: 6), IconButton( onPressed: () => Get.toNamed('/whisper'), icon: const Icon(Icons.notifications_none)) ], - const SizedBox(width: 6), + const SizedBox(width: 8), Obx( () => ctr!.userLogin.value ? Stack( @@ -191,7 +200,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { ), onPressed: () => callback!(), icon: Icon( - Icons.person_rounded, + Icons.account_circle, size: 22, color: Theme.of(context).colorScheme.primary, ), @@ -207,3 +216,108 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { ); } } + +class CustomTabs extends StatefulWidget { + const CustomTabs({super.key}); + + @override + State createState() => _CustomTabsState(); +} + +class _CustomTabsState extends State { + final HomeController _homeController = Get.put(HomeController()); + int currentTabIndex = 1; + + @override + void initState() { + super.initState(); + _homeController.tabController.addListener(listen); + currentTabIndex = _homeController.tabController.index; + } + + void listen() { + setState(() { + currentTabIndex = _homeController.tabController.index; + }); + } + + void onTap(int index) { + feedBack(); + if (_homeController.initialIndex == index) { + _homeController.tabsCtrList[index]().animateToTop(); + } + _homeController.initialIndex = index; + setState(() { + _homeController.tabController.index = index; + currentTabIndex = index; + }); + } + + @override + void dispose() { + super.dispose(); + _homeController.tabController.removeListener(listen); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 56, + child: ListView.separated( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + scrollDirection: Axis.horizontal, + itemCount: _homeController.tabs.length, + separatorBuilder: (BuildContext context, int index) { + return const SizedBox(width: 8.0); + }, + itemBuilder: (BuildContext context, int index) { + bool selected = index == currentTabIndex; + String label = _homeController.tabs[index]['label']; + // add margins to first and last tab; + return CustomChip( + onTap: () { + onTap(index); + }, + label: label, + selected: selected); + }, + ), + ); + } +} + +class CustomChip extends StatelessWidget { + final Function onTap; + final String label; + final bool selected; + const CustomChip( + {super.key, + required this.onTap, + required this.label, + required this.selected}); + + @override + Widget build(BuildContext context) { + TextStyle? chipTextStyle = + selected ? const TextStyle(fontWeight: FontWeight.bold) : null; + + return InputChip( + side: const BorderSide(color: Colors.transparent), + color: MaterialStateProperty.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + return Theme.of(context).colorScheme.tertiaryContainer; // 当按钮被按下时的颜色 + } + return Theme.of(context).colorScheme.surfaceVariant; // 默认颜色 + }), + label: Text( + label, + style: chipTextStyle, + ), + onPressed: () { + onTap(); + }, + selected: selected, + showCheckmark: false, + ); + } +} diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index b15a6aed..2673e84c 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -70,68 +70,66 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); - return Scaffold( - body: RefreshIndicator( - onRefresh: () async { - return await _hotController.onRefresh(); - }, - child: CustomScrollView( - controller: _hotController.scrollController, - slivers: [ - SliverPadding( - // 单列布局 EdgeInsets.zero - padding: - const EdgeInsets.fromLTRB(0, StyleString.safeSpace - 5, 0, 0), - sliver: FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { - return Obx( - () => SliverList( - delegate: - SliverChildBuilderDelegate((context, index) { - return VideoCardH( - videoItem: _hotController.videoList[index], - showPubdate: true, - longPress: () { - _hotController.popupDialog = _createPopupDialog( - _hotController.videoList[index]); - Overlay.of(context) - .insert(_hotController.popupDialog!); - }, - longPressEnd: () { - _hotController.popupDialog?.remove(); - }, - ); - }, childCount: _hotController.videoList.length), - ), - ); - } else { - return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ); - } + return RefreshIndicator( + onRefresh: () async { + return await _hotController.onRefresh(); + }, + child: CustomScrollView( + controller: _hotController.scrollController, + slivers: [ + SliverPadding( + // 单列布局 EdgeInsets.zero + padding: + const EdgeInsets.fromLTRB(0, StyleString.safeSpace - 5, 0, 0), + sliver: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (data['status']) { + return Obx( + () => SliverList( + delegate: + SliverChildBuilderDelegate((context, index) { + return VideoCardH( + videoItem: _hotController.videoList[index], + showPubdate: true, + longPress: () { + _hotController.popupDialog = _createPopupDialog( + _hotController.videoList[index]); + Overlay.of(context) + .insert(_hotController.popupDialog!); + }, + longPressEnd: () { + _hotController.popupDialog?.remove(); + }, + ); + }, childCount: _hotController.videoList.length), + ), + ); } else { - // 骨架屏 - return SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - return const VideoCardHSkeleton(); - }, childCount: 10), + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), ); } - }, - ), + } else { + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoCardHSkeleton(); + }, childCount: 10), + ); + } + }, ), - SliverToBoxAdapter( - child: SizedBox( - height: MediaQuery.of(context).padding.bottom + 10, - ), - ) - ], - ), + ), + SliverToBoxAdapter( + child: SizedBox( + height: MediaQuery.of(context).padding.bottom + 10, + ), + ) + ], ), ); } diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index a322c5f3..15962da4 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -19,11 +19,11 @@ class MainController extends GetxController { RxList navigationBars = [ { 'icon': const Icon( - Icons.favorite_outline, + Icons.home_outlined, size: 21, ), 'selectIcon': const Icon( - Icons.favorite, + Icons.home, size: 21, ), 'label': "首页", @@ -41,11 +41,11 @@ class MainController extends GetxController { }, { 'icon': const Icon( - Icons.folder_outlined, + Icons.video_collection_outlined, size: 20, ), 'selectIcon': const Icon( - Icons.folder, + Icons.video_collection, size: 21, ), 'label': "媒体库", diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart index 55ee3b22..f1b56b2d 100644 --- a/lib/pages/search/view.dart +++ b/lib/pages/search/view.dart @@ -48,53 +48,54 @@ class _SearchPageState extends State with RouteAware { onClosed: (_) => _searchController.onClear(), openColor: Theme.of(context).colorScheme.background, middleColor: Theme.of(context).colorScheme.background, - closedColor: Theme.of(context).colorScheme.background, + closedColor: Colors.transparent, closedShape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(30.0))), openShape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(30.0))), closedBuilder: (BuildContext context, VoidCallback openContainer) { - return Container( - width: 250, - height: 44, - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(25)), - ), - child: Material( - color: - Theme.of(context).colorScheme.secondaryContainer.withAlpha(115), - child: InkWell( - splashColor: Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.3), - onTap: openContainer, - child: Row( - children: [ - const SizedBox(width: 14), - Icon( - Icons.search_outlined, - color: Theme.of(context).colorScheme.onSecondaryContainer, - ), - const SizedBox(width: 10), - Expanded( - child: Obx( - () => Text( - _searchController.defaultSearch.value, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - ), - ], - ), - ), - ), - ); + return IconButton(onPressed: openContainer, icon: Icon(Icons.search)); + // return Container( + // width: 250, + // height: 44, + // clipBehavior: Clip.hardEdge, + // decoration: const BoxDecoration( + // borderRadius: BorderRadius.all(Radius.circular(25)), + // ), + // child: Material( + // color: + // Theme.of(context).colorScheme.secondaryContainer.withAlpha(115), + // child: InkWell( + // splashColor: Theme.of(context) + // .colorScheme + // .primaryContainer + // .withOpacity(0.3), + // onTap: openContainer, + // child: Row( + // children: [ + // const SizedBox(width: 14), + // Icon( + // Icons.search_outlined, + // color: Theme.of(context).colorScheme.onSecondaryContainer, + // ), + // const SizedBox(width: 10), + // Expanded( + // child: Obx( + // () => Text( + // _searchController.defaultSearch.value, + // maxLines: 1, + // overflow: TextOverflow.ellipsis, + // style: TextStyle( + // color: Theme.of(context).colorScheme.outline, + // ), + // ), + // ), + // ), + // ], + // ), + // ), + // ), + // ); }, openBuilder: (BuildContext context, VoidCallback _) { return Scaffold(