diff --git a/lib/core/routes/app_router.dart b/lib/core/routes/app_router.dart index 12fc2f3..abc08ac 100644 --- a/lib/core/routes/app_router.dart +++ b/lib/core/routes/app_router.dart @@ -1,14 +1,19 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:proxibuy/presentation/providers/category/cubit/categories_children_cubit.dart'; +import 'package:proxibuy/presentation/providers/category/cubit/categories_cubit.dart'; import 'package:proxibuy/presentation/providers/category/cubit/category_cubit.dart'; import 'package:proxibuy/presentation/providers/user_info_cubit.dart'; import 'package:proxibuy/presentation/ui/screens/auth/auth_page.dart'; -import 'package:proxibuy/presentation/ui/screens/categories/categories_page.dart'; -import 'package:proxibuy/presentation/ui/screens/home/explore_screen.dart'; +import 'package:proxibuy/presentation/ui/screens/home/screens/categories_screen.dart'; +import 'package:proxibuy/presentation/ui/screens/home/screens/explore_screen.dart'; import 'package:proxibuy/presentation/ui/screens/home/home_page.dart'; -import 'package:proxibuy/presentation/ui/screens/home/home_screen.dart'; -import 'package:proxibuy/presentation/ui/screens/home/setting_screen.dart'; +import 'package:proxibuy/presentation/ui/screens/home/screens/home_screen.dart'; +import 'package:proxibuy/presentation/ui/screens/home/screens/setting_screen.dart'; +import 'package:proxibuy/presentation/ui/theme/responsive.dart'; import 'package:proxibuy/presentation/ui/widgets/navigations/drop_down_demo2.dart'; class AppRouter { @@ -18,7 +23,7 @@ class AppRouter { static final product = '/product'; static final categories = '/categories'; - static List home = [initial, explore, setting]; + static List home = [initial, explore, setting, categories]; static final GlobalKey navigatorKey = GlobalKey(); @@ -85,20 +90,49 @@ class AppRouter { return DropDownDemo3(); }, ), - GoRoute( - path: '$categories/:id', - builder: (BuildContext context, GoRouterState state) { - final id = state.pathParameters['id']!; - return BlocProvider( - create: (context) => CategoryCubit()..getCategory(id), - child: CategoriesPage( - id: id, - ), - ); - }, - ), ]), ]), + StatefulShellBranch(routes: [ + GoRoute( + path: categories, + redirect: (context, state) { + if (Responsive(context).isDesktop()) { + return initial; + } + return null; + }, + builder: (BuildContext context, GoRouterState state) { + StreamSubscription? categoriesSubscription; + + String? id = state.uri.queryParameters['id']; + if (id != null) { + catId.value = id; + context.read().resetPagination(); + context + .read() + .getAllChildCategories(id); + } else { + categoriesSubscription = + context.read().stream.listen( + (state) { + if (state is CategoriesLoaded) { + if (state.categories.isNotEmpty) { + id = state.categories.first.id; + catId.value = id; + // ignore: use_build_context_synchronously + context + .read() + .getAllChildCategories(id ?? ''); + categoriesSubscription?.cancel(); + } + } + }, + ); + } + return CategoriesScreen(); + }, + ), + ]), StatefulShellBranch(routes: [ GoRoute( path: explore, diff --git a/lib/core/services/api/api_service.dart b/lib/core/services/api/api_service.dart index 6723fa9..782928a 100644 --- a/lib/core/services/api/api_service.dart +++ b/lib/core/services/api/api_service.dart @@ -28,7 +28,7 @@ class ApiService { enabled: kDebugMode, )); setAuthToken( - 'eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJheC1zX3N3Tm5fU1hDTVkzWFowSDVKekNhQ0psVXh6bmZ0WHBxSk1YUEF3In0.eyJleHAiOjE3Mzk5NzkwNDUsImlhdCI6MTczOTk0MzA0NSwianRpIjoiMzY2NDllZWQtNTBiNy00ZWYyLWIyNmUtOTcxZDUwODBhODc1IiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay5saWFyYS5ydW4vcmVhbG1zL2xiYSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJmMzljODIxNi0zODhhLTQ0ZTEtODVhOC00Zjk5NmU2NmU2MDQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJmcm9udGVuZCIsInNpZCI6IjcwZDlhNDhmLTcxMDktNGYxNi1hNTQ5LTVmNjE3MDk4ZTJmMiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cHM6Ly9sYmEtYXBpLmxpYXJhLnJ1bi8qIiwiaHR0cDovL2xvY2FsaG9zdDozMDAwLyoiLCIvKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1sYmEiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19LCJmcm9udGVuZCI6eyJyb2xlcyI6WyJzaG9wIiwidXNlciJdfX0sInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6ImRlbW8gZGVtbyIsInByZWZlcnJlZF91c2VybmFtZSI6ImRlbW8iLCJnaXZlbl9uYW1lIjoiZGVtbyIsImZhbWlseV9uYW1lIjoiZGVtbyIsImVtYWlsIjoiZGVtb0BnbWFpbC5jb20ifQ.H3MssiP94FiWyc7FqfoSK7Zt58VOZmunM1D-wrseDiTQF2lFIfCMYbWkKv1ko8hn-zn-1ExV_6auFCNS0C_wTyFbGq_IHwYQr9nGqji_cr4dum19doASpfZRQiR_oR5RM96Ht5lV3nY_X7o-ksJEiRDOHUZ-xmDLkxhGWfeTO90DXYWv_S39mS55R7SsQz7PI83B7ya9qgp-5GND_oY3iNjDYVTI46EQuGOTiNyLgUrRk64IFy4Bbhp-EVj7QhGwkDEOosAytzE5aqW98-1GUSfUS77P36Ln0olQEb_uYed8EDkdauAIPN-iN8Eg4q7QmT-fpBCP61dcy04FRfTzPw'); + 'eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJheC1zX3N3Tm5fU1hDTVkzWFowSDVKekNhQ0psVXh6bmZ0WHBxSk1YUEF3In0.eyJleHAiOjE3NDAwNjQ5NDcsImlhdCI6MTc0MDAyODk0NywianRpIjoiNDVhYTQ0YWItYTEyMC00N2M3LWIyMWUtNjExNDllMWZlM2EyIiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay5saWFyYS5ydW4vcmVhbG1zL2xiYSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJmMzljODIxNi0zODhhLTQ0ZTEtODVhOC00Zjk5NmU2NmU2MDQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJmcm9udGVuZCIsInNpZCI6ImEwNjUwMmU3LTY4MzItNDVhNC04MmUwLTdhZWI4ODBlZTc5ZiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cHM6Ly9sYmEtYXBpLmxpYXJhLnJ1bi8qIiwiaHR0cDovL2xvY2FsaG9zdDozMDAwLyoiLCIvKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1sYmEiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19LCJmcm9udGVuZCI6eyJyb2xlcyI6WyJzaG9wIiwidXNlciJdfX0sInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6ImRlbW8gZGVtbyIsInByZWZlcnJlZF91c2VybmFtZSI6ImRlbW8iLCJnaXZlbl9uYW1lIjoiZGVtbyIsImZhbWlseV9uYW1lIjoiZGVtbyIsImVtYWlsIjoiZGVtb0BnbWFpbC5jb20ifQ.iwAMzwyuQu9BkNXA65u8XfZIYoGtGvQ99qpCwTCPLDfe2DFZU_1wcOu5TV_7My80UHifzLR587CPGH1xwlJmD1JFtrEh0wk_sBe0iYKOmiz7OhFCLUNAuLkV5dvwYPfqyzPFYQPe8F0QdnK9107pF-15uyjn8w1MLfSE4uCRtc8Arh43WeZqoYALlLkHvQgltxvuBE_FXt_AulIBzmrhwDhA_Aw7gS4J05mGAkMAAX1B4mXxB-OCBN-ZPFEAU14YPQPHAjUdKtJ1scC4VYworjaEA3kkx_gYWdfi-o_3YykDlaE6fxOm4yQnFzg2DjJhE_7i8Lsjchc3GrBNzt4UeQ'); } /// 🔹 Handle GET requests diff --git a/lib/data/models/screen_model.dart b/lib/data/models/screen_model.dart index d7923fd..9e4dee6 100644 --- a/lib/data/models/screen_model.dart +++ b/lib/data/models/screen_model.dart @@ -2,7 +2,8 @@ import 'package:proxibuy/core/gen/assets.gen.dart'; class ScreenModel { final String title; + final String route; final SvgGenImage icon; - ScreenModel({required this.title, required this.icon}); + ScreenModel({required this.title, required this.icon, required this.route}); } diff --git a/lib/main.dart b/lib/main.dart index 2f818a0..9b71de0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,7 +22,8 @@ void main() async { BlocProvider(create: (context) => ThemModeCubit()), BlocProvider( create: (context) => CategoriesChildrenCubit()), - BlocProvider(create: (context) => CategoriesCubit()), + BlocProvider( + create: (context) => CategoriesCubit()..getAllCategories()), BlocProvider( create: (context) => UserInfoCubit()..getUserInfo()), ], diff --git a/lib/presentation/ui/screens/categories/categories_page.dart b/lib/presentation/ui/screens/categories/categories_page.dart deleted file mode 100644 index 9b0f5a1..0000000 --- a/lib/presentation/ui/screens/categories/categories_page.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:proxibuy/presentation/providers/category/cubit/categories_children_cubit.dart'; -import 'package:proxibuy/presentation/providers/category/cubit/category_cubit.dart'; -import 'package:proxibuy/presentation/ui/widgets/default_placeholder.dart'; - -class CategoriesPage extends StatefulWidget { - final String id; - const CategoriesPage({super.key, required this.id}); - - @override - State createState() => _CategoriesPageState(); -} - -class _CategoriesPageState extends State { - final ScrollController _scrollController = ScrollController(); - - @override - void initState() { - super.initState(); - _scrollController.addListener(_onScroll); - WidgetsBinding.instance.addPostFrameCallback((_) async { - await context.read().resetPagination(); - if (mounted) { - context - .read() - .getAllChildCategories(widget.id); - } - }); - } - - void _onScroll() { - if (_scrollController.position.pixels == - _scrollController.position.maxScrollExtent) { - context.read().getAllChildCategories(widget.id); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: BlocBuilder( - builder: (context, state) { - if (state is CategorySuccess) { - return Text(state.category.name ?? '...'); - } - return SizedBox(); - }, - )), - body: BlocBuilder( - builder: (context, state) { - if (state is CategoriesChildrenLoaded) { - if (state.categories.isEmpty) { - return Center(child: Text('Empty')); - } - return SingleChildScrollView( - child: Column( - children: [ - ListView.builder( - // controller: _scrollController, - itemCount: state.categories.length, - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - if (index >= state.categories.length) { - return Center(child: CircularProgressIndicator()); - } - final category = state.categories[index]; - return ListTile( - leading: SvgPicture.network( - '${state.categories[index].url}', - color: Theme.of(context).colorScheme.onSurface, - placeholderBuilder: (context) => DefaultPlaceHolder( - child: Container( - width: 24, - height: 24, - decoration: BoxDecoration( - shape: BoxShape.circle, color: Colors.white), - )), - errorBuilder: (context, error, stackTrace) => - Icon(Icons.category_outlined), - ), - title: Text(category.name ?? ''), - ); - }, - ), - if (state.isLoading) LinearProgressIndicator() - ], - ), - ); - } else if (state is CategoriesChildrenError) { - return Center(child: Text(state.errorMessage!)); - } else { - return ListView.builder( - // controller: _scrollController, - itemCount: 20, - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - return DefaultPlaceHolder( - child: Container( - margin: EdgeInsets.symmetric(vertical: 8), - color: Colors.white, - child: ListTile( - leading: Icon(Icons.abc_outlined), - title: Text(''), - ), - ), - ); - }, - ); - } - }, - ), - ); - } - - @override - void dispose() { - _scrollController.dispose(); - super.dispose(); - } -} diff --git a/lib/presentation/ui/screens/home/home_desk_page.dart b/lib/presentation/ui/screens/home/home_desk_page.dart new file mode 100644 index 0000000..4ad3756 --- /dev/null +++ b/lib/presentation/ui/screens/home/home_desk_page.dart @@ -0,0 +1,178 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:proxibuy/core/gen/assets.gen.dart'; +import 'package:proxibuy/core/routes/app_router.dart'; +import 'package:proxibuy/core/utils/empty_space.dart'; +import 'package:proxibuy/data/models/screen_model.dart'; +import 'package:proxibuy/presentation/providers/them_mode_cubit.dart'; +import 'package:proxibuy/presentation/ui/theme/theme.dart'; +import 'package:proxibuy/presentation/ui/widgets/navigations/categories_mega_menu.dart'; + +class HomeDeskPage extends StatefulWidget { + final Widget child; + const HomeDeskPage({super.key, required this.child}); + + @override + State createState() => _HomeDeskPageState(); +} + +class _HomeDeskPageState extends State { + int selectedIndex = 0; + List deskScreens = [ + ScreenModel( + title: 'Home', + icon: Assets.icon.outline.home, + route: AppRouter.initial), + ScreenModel( + title: 'Explore', + icon: Assets.icon.outline.map, + route: AppRouter.explore), + ScreenModel( + title: 'Settings', + icon: Assets.icon.outline.setting, + route: AppRouter.setting), + ]; + + void _onItemTapped(BuildContext context, int index) { + setState(() { + selectedIndex = index; + }); + switch (index) { + case 0: + context.go(AppRouter.initial); + break; + case 1: + context.go(AppRouter.explore); + break; + case 2: + context.go(AppRouter.setting); + break; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Column( + children: [ + navBar(context), + Expanded(child: widget.child), + ], + ), + ); + } + + Padding navBar(BuildContext context) { + final defaultBorder = OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: + BorderSide(color: Theme.of(context).colorScheme.surface, width: 2)); + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + SelectableText( + "Proxibuy", + style: Theme.of(context).textTheme.displaySmall, + ), + 32.w, + Row( + children: [ + ...List.generate( + deskScreens.length, + (index) => Row( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: selectedIndex == index + ? themeColor(context) + ?.primaryLightSurface + .withAlpha(90) + : null), + child: IconButton( + splashRadius: 12, + onPressed: () { + _onItemTapped(context, index); + }, + icon: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + deskScreens[index].icon.svg( + color: selectedIndex == index + ? Theme.of(context).primaryColor + : Theme.of(context) + .colorScheme + .onSurface), + 12.w, + Text( + deskScreens[index].title, + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith( + color: selectedIndex == index + ? Theme.of(context).primaryColor + : Theme.of(context) + .colorScheme + .onSurface), + ) + ], + )), + ), + 24.w + ], + ), + ), + CategoriesMegaMenu() + ], + ), + ], + ), + Flexible( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + 8.w, + Flexible( + child: Container( + constraints: BoxConstraints(maxWidth: 800), + child: TextField( + decoration: InputDecoration( + hintText: 'what are you looking for?', + suffixIcon: Padding( + padding: const EdgeInsets.all(8.0), + child: Assets.icon.outline.search.svg( + color: Theme.of(context).colorScheme.onSurface, + width: 16, + height: 16), + ), + enabledBorder: defaultBorder, + border: defaultBorder), + ), + )), + 32.w, + IconButton( + icon: Assets.icon.outline.notificationBing + .svg(color: Theme.of(context).colorScheme.onSurface), + onPressed: () {}, + ), + 12.w, + IconButton( + icon: Icon(Icons.brightness_6), + onPressed: () { + context.read().changeTheme(); + }, + ), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/presentation/ui/screens/home/home_page.dart b/lib/presentation/ui/screens/home/home_page.dart index ea7f2e5..53804bd 100644 --- a/lib/presentation/ui/screens/home/home_page.dart +++ b/lib/presentation/ui/screens/home/home_page.dart @@ -7,6 +7,7 @@ import 'package:proxibuy/core/routes/app_router.dart'; import 'package:proxibuy/core/utils/empty_space.dart'; import 'package:proxibuy/data/models/screen_model.dart'; import 'package:proxibuy/presentation/providers/them_mode_cubit.dart'; +import 'package:proxibuy/presentation/ui/screens/home/home_desk_page.dart'; import 'package:proxibuy/presentation/ui/theme/responsive.dart'; import 'package:proxibuy/presentation/ui/theme/theme.dart'; import 'package:proxibuy/presentation/ui/widgets/navigations/categories_mega_menu.dart'; @@ -21,24 +22,40 @@ class HomePage extends StatefulWidget { } class _HomePageState extends State { - int selectedIndex = 0; + @override + initState() { + super.initState(); + } + List screens = [ - ScreenModel(title: 'Home', icon: Assets.icon.outline.home), - ScreenModel(title: 'Explore', icon: Assets.icon.outline.map), - ScreenModel(title: 'Settings', icon: Assets.icon.outline.setting), + ScreenModel( + title: 'Home', + icon: Assets.icon.outline.home, + route: AppRouter.initial), + ScreenModel( + title: 'Categories', + icon: Assets.icon.outline.search, + route: AppRouter.categories), + ScreenModel( + title: 'Explore', + icon: Assets.icon.outline.map, + route: AppRouter.explore), + ScreenModel( + title: 'Settings', + icon: Assets.icon.outline.setting, + route: AppRouter.setting), ]; + int _getSelectedIndex(BuildContext context) { + final g = GoRouterState.of(context); + final String location = g.fullPath.toString(); + return screens.indexWhere((element) => element.route == location); + } + @override Widget build(BuildContext context) { return Responsive(context).builder( - desktop: Scaffold( - body: Column( - children: [ - navBar(context), - Expanded(child: body()), - ], - ), - ), + desktop: HomeDeskPage(child: body()), mobile: Scaffold( body: body(), bottomNavigationBar: bottomNavigationBar(context), @@ -46,127 +63,14 @@ class _HomePageState extends State { ); } - Padding navBar(BuildContext context) { - final defaultBorder = OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: - BorderSide(color: Theme.of(context).colorScheme.surface, width: 2)); - return Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - SelectableText( - "Proxibuy", - style: Theme.of(context).textTheme.displaySmall, - ), - 32.w, - Row( - children: [ - ...List.generate( - screens.length, - (index) => Row( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: selectedIndex == index - ? themeColor(context) - ?.primaryLightSurface - .withAlpha(90) - : null), - child: IconButton( - splashRadius: 12, - onPressed: () { - _onItemTapped(context, index); - }, - icon: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - screens[index].icon.svg( - color: selectedIndex == index - ? Theme.of(context).primaryColor - : Theme.of(context) - .colorScheme - .onSurface), - 12.w, - Text( - screens[index].title, - style: Theme.of(context) - .textTheme - .labelLarge - ?.copyWith( - color: selectedIndex == index - ? Theme.of(context).primaryColor - : Theme.of(context) - .colorScheme - .onSurface), - ) - ], - )), - ), - 24.w - ], - ), - ), - CategoriesMegaMenu() - ], - ), - ], - ), - Flexible( - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - 8.w, - Flexible( - child: Container( - constraints: BoxConstraints(maxWidth: 800), - child: TextField( - decoration: InputDecoration( - hintText: 'what are you looking for?', - suffixIcon: Padding( - padding: const EdgeInsets.all(8.0), - child: Assets.icon.outline.search.svg( - color: Theme.of(context).colorScheme.onSurface, - width: 16, - height: 16), - ), - enabledBorder: defaultBorder, - border: defaultBorder), - ), - )), - 32.w, - IconButton( - icon: Assets.icon.outline.notificationBing - .svg(color: Theme.of(context).colorScheme.onSurface), - onPressed: () {}, - ), - 12.w, - IconButton( - icon: Icon(Icons.brightness_6), - onPressed: () { - context.read().changeTheme(); - }, - ), - ], - ), - ) - ], - ), - ); - } - Padding bottomNavigationBar(BuildContext context) { return Padding( padding: const EdgeInsets.all(16.0), child: BottomNavyBar( - selectedIndex: selectedIndex, + selectedIndex: _getSelectedIndex(context), // landscapeLayout: BottomNavigationBarLandscapeLayout.centered, // showUnselectedLabels: false, - onItemSelected: (index) => _onItemTapped(context, index), + onItemSelected: (index) => _onItemTapped(context, screens[index].route), backgroundColor: Theme.of(context).colorScheme.surface, itemCornerRadius: 12, itemPadding: EdgeInsets.symmetric(horizontal: 16), @@ -179,7 +83,7 @@ class _HomePageState extends State { screens.length, (index) => BottomNavyBarItem( icon: screens[index].icon.svg( - color: selectedIndex == index + color: _getSelectedIndex(context) == index ? Theme.of(context).primaryColor : Theme.of(context).colorScheme.onSurface), activeColor: Theme.of(context).primaryColor, @@ -195,20 +99,7 @@ class _HomePageState extends State { return widget.child; } - void _onItemTapped(BuildContext context, int index) { - setState(() { - selectedIndex = index; - }); - switch (index) { - case 0: - context.go(AppRouter.initial); - break; - case 1: - context.go(AppRouter.explore); - break; - case 2: - context.go(AppRouter.setting); - break; - } + void _onItemTapped(BuildContext context, String route) { + context.go(route); } } diff --git a/lib/presentation/ui/screens/home/screens/categories_screen.dart b/lib/presentation/ui/screens/home/screens/categories_screen.dart new file mode 100644 index 0000000..2a31a04 --- /dev/null +++ b/lib/presentation/ui/screens/home/screens/categories_screen.dart @@ -0,0 +1,239 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:proxibuy/presentation/providers/category/cubit/categories_children_cubit.dart'; +import 'package:proxibuy/presentation/providers/category/cubit/categories_cubit.dart'; +import 'package:proxibuy/presentation/ui/widgets/default_placeholder.dart'; + +ValueNotifier catId = ValueNotifier(null); + +class CategoriesScreen extends StatefulWidget { + const CategoriesScreen({super.key}); + + @override + State createState() => _CategoriesScreenState(); +} + +class _CategoriesScreenState extends State { + final ScrollController _scrollController = ScrollController(); + StreamSubscription? _categoriesSubscription; + + @override + void initState() { + super.initState(); + _scrollController.addListener(_onScroll); + } + + void _onScroll() { + if (_scrollController.position.pixels == + _scrollController.position.maxScrollExtent) { + context + .read() + .getAllChildCategories(catId.value ?? ''); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: BlocBuilder( + builder: (context, state) { + if (state is CategoriesLoaded) { + if (state.categories.isEmpty) { + return Center(child: Text('Empty')); + } + return SingleChildScrollView( + child: Container( + color: Theme.of(context).colorScheme.surface, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ListView.builder( + // controller: _scrollController, + itemCount: state.categories.length, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + final category = state.categories[index]; + return InkWell( + onTap: () { + context + .read() + .resetPagination(); + setState(() { + catId.value = category.id; + }); + context + .read() + .getAllChildCategories(catId.value ?? ''); + }, + child: ValueListenableBuilder( + valueListenable: catId, + builder: (context, selectedId, _) { + return Container( + color: selectedId == category.id + ? Theme.of(context) + .scaffoldBackgroundColor + : Theme.of(context) + .colorScheme + .surface, + child: ListTile( + leading: SvgPicture.network( + '${category.url}', + color: Theme.of(context) + .colorScheme + .onSurface, + placeholderBuilder: (context) => + DefaultPlaceHolder( + child: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white), + )), + errorBuilder: (context, error, + stackTrace) => + Icon(Icons.category_outlined), + ), + title: Text(category.name ?? ''), + ), + ); + }), + ); + }, + ), + if (state.isLoading) LinearProgressIndicator() + ], + ), + ), + ); + } else if (state is CategoriesChildrenError) { + return Center(child: Text(state.errorMessage!)); + } else { + return ListView.builder( + // controller: _scrollController, + itemCount: 20, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return DefaultPlaceHolder( + child: Container( + margin: EdgeInsets.symmetric(vertical: 8), + color: Colors.white, + child: ListTile( + leading: Icon(Icons.abc_outlined), + title: Text(''), + ), + ), + ); + }, + ); + } + }, + ), + ), + VerticalDivider(), + Expanded( + flex: 2, + child: + BlocBuilder( + builder: (context, state) { + if (state is CategoriesChildrenLoaded) { + if (state.categories.isEmpty) { + return Center(child: Text('Empty')); + } + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: TextButton( + onPressed: () {}, + child: Text( + 'See all', + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith( + color: Theme.of(context).primaryColor), + )), + ), + ListView.builder( + // controller: _scrollController, + itemCount: state.categories.length, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + if (index >= state.categories.length) { + return Center(child: CircularProgressIndicator()); + } + final category = state.categories[index]; + return ListTile( + leading: SvgPicture.network( + '${state.categories[index].url}', + color: Theme.of(context).colorScheme.onSurface, + placeholderBuilder: (context) => + DefaultPlaceHolder( + child: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white), + )), + errorBuilder: (context, error, stackTrace) => + Icon(Icons.category_outlined), + ), + title: Text(category.name ?? ''), + ); + }, + ), + if (state.isLoading) LinearProgressIndicator() + ], + ), + ); + } else if (state is CategoriesChildrenError) { + return Center(child: Text(state.errorMessage!)); + } else { + return ListView.builder( + // controller: _scrollController, + itemCount: 20, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return DefaultPlaceHolder( + child: Container( + margin: EdgeInsets.symmetric(vertical: 8), + color: Colors.white, + child: ListTile( + leading: Icon(Icons.abc_outlined), + title: Text(''), + ), + ), + ); + }, + ); + } + }, + ), + ), + ], + ), + ); + } + + @override + void dispose() { + _scrollController.dispose(); + _categoriesSubscription?.cancel(); + super.dispose(); + } +} diff --git a/lib/presentation/ui/screens/home/explore_screen.dart b/lib/presentation/ui/screens/home/screens/explore_screen.dart similarity index 100% rename from lib/presentation/ui/screens/home/explore_screen.dart rename to lib/presentation/ui/screens/home/screens/explore_screen.dart diff --git a/lib/presentation/ui/screens/home/home_screen.dart b/lib/presentation/ui/screens/home/screens/home_screen.dart similarity index 99% rename from lib/presentation/ui/screens/home/home_screen.dart rename to lib/presentation/ui/screens/home/screens/home_screen.dart index 4f63e23..628018a 100644 --- a/lib/presentation/ui/screens/home/home_screen.dart +++ b/lib/presentation/ui/screens/home/screens/home_screen.dart @@ -26,9 +26,9 @@ class _HomeScreenState extends State { @override void initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - context.read().getAllCategories(); - }); + // WidgetsBinding.instance.addPostFrameCallback((_) { + // context.read().getAllCategories(); + // }); } @override @@ -590,6 +590,7 @@ class _HomeScreenState extends State { Column categories(BuildContext context) { return Column( + mainAxisAlignment: MainAxisAlignment.start, children: [ titleDivider(context, title: 'what\'s on your mind?', top: 16), BlocBuilder( @@ -615,7 +616,7 @@ class _HomeScreenState extends State { child: InkWell( onTap: () { context.go( - '${AppRouter.categories}/${state.categories[index].id}'); + '${AppRouter.categories}?id=${state.categories[index].id}'); }, onHover: (value) { isHovered.value = value; diff --git a/lib/presentation/ui/screens/home/setting_screen.dart b/lib/presentation/ui/screens/home/screens/setting_screen.dart similarity index 100% rename from lib/presentation/ui/screens/home/setting_screen.dart rename to lib/presentation/ui/screens/home/screens/setting_screen.dart diff --git a/lib/presentation/ui/widgets/navigations/categories_mega_menu.dart b/lib/presentation/ui/widgets/navigations/categories_mega_menu.dart index e51d6f4..a399496 100644 --- a/lib/presentation/ui/widgets/navigations/categories_mega_menu.dart +++ b/lib/presentation/ui/widgets/navigations/categories_mega_menu.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:proxibuy/core/utils/empty_space.dart'; import 'package:proxibuy/presentation/providers/category/cubit/categories_children_cubit.dart'; import 'package:proxibuy/presentation/providers/category/cubit/categories_cubit.dart'; +import 'package:proxibuy/presentation/ui/screens/home/screens/categories_screen.dart'; import 'package:proxibuy/presentation/ui/theme/colors.dart'; import 'package:proxibuy/presentation/ui/theme/theme.dart'; +import 'package:proxibuy/presentation/ui/widgets/default_placeholder.dart'; class CategoriesMegaMenu extends StatefulWidget { const CategoriesMegaMenu({super.key}); @@ -61,6 +64,7 @@ class _CategoriesMegaMenuState extends State { } OverlayEntry _createMegaMenu() { + catId.value ??= context.read().state.categories.first.id; return OverlayEntry( builder: (context) => Positioned( width: 800, // Adjust width of the mega menu @@ -72,121 +76,18 @@ class _CategoriesMegaMenuState extends State { child: Material( elevation: 4, child: Container( - padding: EdgeInsets.all(10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - color: Theme.of(context).colorScheme.surface, - boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], - ), - child: Row( - children: [ - Expanded( - child: BlocBuilder( - builder: (context, state) { - if (state.isLoading && state.categories.isEmpty) { - return Center(child: CircularProgressIndicator()); - } else if (state.errorMessage != null) { - return Center(child: Text(state.errorMessage!)); - } else { - return SingleChildScrollView( - child: ValueListenableBuilder( - valueListenable: selectedId, - builder: (context, id, _) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - ...List.generate( - context - .read() - .state - .categories - .length, - (index) { - final cat = context - .read() - .state - .categories[index]; - return InkWell( - onTap: () { - if (cat.id != null) { - selectedId.value = cat.id!; - context - .read< - CategoriesChildrenCubit>() - .resetPagination(); - context - .read< - CategoriesChildrenCubit>() - .getAllChildCategories( - id!); - } - }, - child: Container( - color: cat.id == id - ? semanticBlue - : null, - child: _menuItem( - cat.name ?? ''))); - }, - ) - ], - ); - }), - ); - } - }, - ), - ), - Expanded( - child: BlocBuilder( - builder: (context, state) { - if (state.isLoading && state.categories.isEmpty) { - return Center(child: CircularProgressIndicator()); - } else if (state.errorMessage != null) { - return Center(child: Text(state.errorMessage!)); - } else { - return NotificationListener( - onNotification: (scrollNotification) { - if (scrollNotification.metrics.pixels == - scrollNotification - .metrics.maxScrollExtent) { - context - .read() - .getAllChildCategories(selectedId.value!); - } - return false; - }, - child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: 300), // Adjust height as needed - child: ListView.builder( - shrinkWrap: true, - itemCount: state.categories.length + - (state.isLoading ? 1 : 0), - itemBuilder: (context, index) { - if (index >= state.categories.length) { - return Center( - child: CircularProgressIndicator()); - } - final category = state.categories[index]; - return ListTile( - title: Text(category.name ?? ''), - onTap: () { - print('${category.name} clicked'); - }, - ); - }, - ), - ), - ); - } - }, - ), - ) - ], - ), - ), + constraints: BoxConstraints( + maxHeight: 600, + ), + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: Theme.of(context).scaffoldBackgroundColor, + boxShadow: [ + BoxShadow(color: Colors.black26, blurRadius: 4) + ], + ), + child: CategoriesScreen()), ), ), ), @@ -194,17 +95,14 @@ class _CategoriesMegaMenuState extends State { ); } - Widget _menuItem(String title) { - return ListTile( - title: Text(title), - ); - } - @override Widget build(BuildContext context) { return CompositedTransformTarget( link: _layerLink, child: InkWell( + splashColor: Colors.transparent, + borderRadius: BorderRadius.zero, + hoverColor: Colors.transparent, onTap: () {}, onHover: (value) { onHovered.value = value;