import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/pages/mine/index.dart'; import 'package:pilipala/pages/search/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import './controller.dart'; class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @override State createState() => _HomePageState(); } class _HomePageState extends State with AutomaticKeepAliveClientMixin, TickerProviderStateMixin { final HomeController _homeController = Get.put(HomeController()); List videoList = []; late Stream stream; @override bool get wantKeepAlive => true; @override void initState() { super.initState(); stream = _homeController.searchBarStream.stream; } showUserBottomSheet() { feedBack(); showModalBottomSheet( context: context, builder: (_) => const SizedBox( height: 450, child: MinePage(), ), clipBehavior: Clip.hardEdge, isScrollControlled: true, ); } @override Widget build(BuildContext context) { super.build(context); return Scaffold( extendBody: true, extendBodyBehindAppBar: true, body: Stack( children: [ // 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]), ), ), ), ), 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, ), ), ], ), ], ), ); } } class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { final double height; final Stream? stream; final HomeController? ctr; final Function? callback; const CustomAppBar({ super.key, this.height = kToolbarHeight, this.stream, this.ctr, this.callback, }); @override Size get preferredSize => Size.fromHeight(height); @override Widget build(BuildContext context) { return StreamBuilder( stream: stream, initialData: true, builder: (BuildContext context, AsyncSnapshot snapshot) { final RxBool isUserLoggedIn = ctr!.userLogin; final double top = MediaQuery.of(context).padding.top; return AnimatedOpacity( opacity: snapshot.data ? 1 : 0, duration: const Duration(milliseconds: 300), child: AnimatedContainer( curve: Curves.easeInOutCubicEmphasized, duration: const Duration(milliseconds: 500), height: snapshot.data ? top + 52 : top, padding: EdgeInsets.fromLTRB(14, top, 14, 0), child: UserInfoWidget( top: top, userLogin: isUserLoggedIn, userFace: ctr?.userFace.value, callback: () => callback!(), ), ), ); }, ); } } class UserInfoWidget extends StatelessWidget { const UserInfoWidget({ Key? key, required this.top, required this.userLogin, required this.userFace, required this.callback, }) : super(key: key); final double top; final RxBool userLogin; final String? userFace; final VoidCallback? callback; @override Widget build(BuildContext context) { return Row( children: [ const Expanded(child: SearchPage()), if (userLogin.value) ...[ const SizedBox(width: 4), ClipRect( child: IconButton( onPressed: () => Get.toNamed('/whisper'), icon: const Icon(Icons.notifications_none), ), ) ], const SizedBox(width: 8), Obx( () => userLogin.value ? Stack( children: [ NetworkImgLayer( type: 'avatar', width: 34, height: 34, src: userFace, ), Positioned.fill( child: Material( color: Colors.transparent, child: InkWell( onTap: () => callback?.call(), splashColor: Theme.of(context) .colorScheme .primaryContainer .withOpacity(0.3), borderRadius: const BorderRadius.all( Radius.circular(50), ), ), ), ) ], ) : DefaultUser(callback: () => callback), ), ], ); } } class DefaultUser extends StatelessWidget { const DefaultUser({super.key, this.callback}); final Function? callback; @override Widget build(BuildContext context) { return SizedBox( width: 38, height: 38, child: IconButton( style: ButtonStyle( padding: MaterialStateProperty.all(EdgeInsets.zero), backgroundColor: MaterialStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.onInverseSurface; }), ), onPressed: () => callback?.call(), icon: Icon( Icons.person_rounded, size: 22, color: Theme.of(context).colorScheme.primary, ), ), ); } } 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); } void listen() { _homeController.initialIndex.value = _homeController.tabController.index; } void onTap(int index) { feedBack(); if (_homeController.initialIndex.value == index) { _homeController.tabsCtrList[index]().animateToTop(); } _homeController.initialIndex.value = index; _homeController.tabController.index = index; } @override void dispose() { super.dispose(); _homeController.tabController.removeListener(listen); } @override Widget build(BuildContext context) { return Container( height: 44, margin: const EdgeInsets.only(top: 4), child: ListView.separated( padding: const EdgeInsets.symmetric(horizontal: 14.0), scrollDirection: Axis.horizontal, itemCount: _homeController.tabs.length, separatorBuilder: (BuildContext context, int index) { return const SizedBox(width: 10); }, itemBuilder: (BuildContext context, int index) { String label = _homeController.tabs[index]['label']; return Obx( () => CustomChip( onTap: () => onTap(index), label: label, selected: index == _homeController.initialIndex.value, ), ); }, ), ); } } 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) { final ColorScheme colorTheme = Theme.of(context).colorScheme; final Color secondaryContainer = colorTheme.secondaryContainer; final TextStyle chipTextStyle = selected ? const TextStyle(fontWeight: FontWeight.bold, fontSize: 13) : const TextStyle(fontSize: 13); final ColorScheme colorScheme = Theme.of(context).colorScheme; const VisualDensity visualDensity = VisualDensity(horizontal: -4.0, vertical: -2.0); return InputChip( side: BorderSide( color: selected ? colorScheme.onSecondaryContainer.withOpacity(0.2) : Colors.transparent, ), backgroundColor: secondaryContainer, selectedColor: secondaryContainer, color: MaterialStateProperty.resolveWith( (Set states) => secondaryContainer.withAlpha(200)), padding: const EdgeInsets.fromLTRB(7, 1, 7, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), selected: selected, showCheckmark: false, visualDensity: visualDensity, ); } }