diff --git a/assets/icon/outline/arrow-right.svg b/assets/icon/outline/arrow-right.svg new file mode 100644 index 0000000..0bf5006 --- /dev/null +++ b/assets/icon/outline/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icon/outline/empty-wallet.svg b/assets/icon/outline/empty-wallet.svg new file mode 100644 index 0000000..3e5c996 --- /dev/null +++ b/assets/icon/outline/empty-wallet.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icon/outline/frame.svg b/assets/icon/outline/frame.svg new file mode 100644 index 0000000..d4253ed --- /dev/null +++ b/assets/icon/outline/frame.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icon/outline/logout.svg b/assets/icon/outline/logout.svg new file mode 100644 index 0000000..9bc1219 --- /dev/null +++ b/assets/icon/outline/logout.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icon/outline/message-question.svg b/assets/icon/outline/message-question.svg new file mode 100644 index 0000000..5d7c520 --- /dev/null +++ b/assets/icon/outline/message-question.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icon/outline/notification.svg b/assets/icon/outline/notification.svg new file mode 100644 index 0000000..ca6c260 --- /dev/null +++ b/assets/icon/outline/notification.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icon/outline/profile.svg b/assets/icon/outline/profile.svg new file mode 100644 index 0000000..098203e --- /dev/null +++ b/assets/icon/outline/profile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icon/outline/shield.svg b/assets/icon/outline/shield.svg new file mode 100644 index 0000000..25a5895 --- /dev/null +++ b/assets/icon/outline/shield.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icon/outline/shop-add.svg b/assets/icon/outline/shop-add.svg new file mode 100644 index 0000000..8a946c1 --- /dev/null +++ b/assets/icon/outline/shop-add.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icon/outline/star.svg b/assets/icon/outline/star.svg new file mode 100644 index 0000000..ece9bd4 --- /dev/null +++ b/assets/icon/outline/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icon/outline/ticket-discount.svg b/assets/icon/outline/ticket-discount.svg new file mode 100644 index 0000000..5616812 --- /dev/null +++ b/assets/icon/outline/ticket-discount.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/core/gen/assets.gen.dart b/lib/core/gen/assets.gen.dart index 5537cbd..b0119ec 100644 --- a/lib/core/gen/assets.gen.dart +++ b/lib/core/gen/assets.gen.dart @@ -40,20 +40,62 @@ class $AssetsLogoGen { class $AssetsIconOutlineGen { const $AssetsIconOutlineGen(); + /// File path: assets/icon/outline/arrow-right.svg + SvgGenImage get arrowRight => const SvgGenImage( + 'assets/icon/outline/arrow-right.svg', + size: Size(18.0, 18.0), + ); + + /// File path: assets/icon/outline/empty-wallet.svg + SvgGenImage get emptyWallet => const SvgGenImage( + 'assets/icon/outline/empty-wallet.svg', + size: Size(18.0, 18.0), + ); + + /// File path: assets/icon/outline/frame.svg + SvgGenImage get frame => const SvgGenImage( + 'assets/icon/outline/frame.svg', + size: Size(18.0, 18.0), + ); + /// File path: assets/icon/outline/home.svg SvgGenImage get home => const SvgGenImage('assets/icon/outline/home.svg', size: Size(24.0, 24.0)); + /// File path: assets/icon/outline/logout.svg + SvgGenImage get logout => const SvgGenImage( + 'assets/icon/outline/logout.svg', + size: Size(18.0, 18.0), + ); + /// File path: assets/icon/outline/map.svg SvgGenImage get map => const SvgGenImage('assets/icon/outline/map.svg', size: Size(24.0, 24.0)); + /// File path: assets/icon/outline/message-question.svg + SvgGenImage get messageQuestion => const SvgGenImage( + 'assets/icon/outline/message-question.svg', + size: Size(18.0, 18.0), + ); + /// File path: assets/icon/outline/notification-bing.svg SvgGenImage get notificationBing => const SvgGenImage( 'assets/icon/outline/notification-bing.svg', size: Size(24.0, 24.0), ); + /// File path: assets/icon/outline/notification.svg + SvgGenImage get notification => const SvgGenImage( + 'assets/icon/outline/notification.svg', + size: Size(18.0, 18.0), + ); + + /// File path: assets/icon/outline/profile.svg + SvgGenImage get profile => const SvgGenImage( + 'assets/icon/outline/profile.svg', + size: Size(18.0, 18.0), + ); + /// File path: assets/icon/outline/search.svg SvgGenImage get search => const SvgGenImage( 'assets/icon/outline/search.svg', @@ -66,17 +108,50 @@ class $AssetsIconOutlineGen { size: Size(24.0, 24.0), ); + /// File path: assets/icon/outline/shield.svg + SvgGenImage get shield => const SvgGenImage( + 'assets/icon/outline/shield.svg', + size: Size(18.0, 18.0), + ); + + /// File path: assets/icon/outline/shop-add.svg + SvgGenImage get shopAdd => const SvgGenImage( + 'assets/icon/outline/shop-add.svg', + size: Size(18.0, 18.0), + ); + + /// File path: assets/icon/outline/star.svg + SvgGenImage get star => + const SvgGenImage('assets/icon/outline/star.svg', size: Size(18.0, 18.0)); + + /// File path: assets/icon/outline/ticket-discount.svg + SvgGenImage get ticketDiscount => const SvgGenImage( + 'assets/icon/outline/ticket-discount.svg', + size: Size(18.0, 18.0), + ); + /// File path: assets/icon/outline/user.svg SvgGenImage get user => const SvgGenImage('assets/icon/outline/user.svg', size: Size(24.0, 24.0)); /// List of all assets List get values => [ + arrowRight, + emptyWallet, + frame, home, + logout, map, + messageQuestion, notificationBing, + notification, + profile, search, setting, + shield, + shopAdd, + star, + ticketDiscount, user, ]; } diff --git a/lib/core/routes/app_router.dart b/lib/core/routes/app_router.dart index 77c4ef6..429095f 100644 --- a/lib/core/routes/app_router.dart +++ b/lib/core/routes/app_router.dart @@ -5,20 +5,24 @@ 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/user_info_cubit.dart'; import 'package:proxibuy/presentation/ui/screens/auth/auth_page.dart'; +import 'package:proxibuy/presentation/ui/screens/setting/edit_profile_page.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/screens/home_screen.dart'; import 'package:proxibuy/presentation/ui/screens/home/screens/setting_screen.dart'; import 'package:proxibuy/presentation/ui/screens/product/product_page.dart'; +import 'package:proxibuy/presentation/ui/screens/setting/notifications_etting_page.dart'; import 'package:proxibuy/presentation/ui/theme/responsive.dart'; class AppRouter { static final initial = '/'; + static final auth = '/auth'; static final explore = '/explore'; static final setting = '/settings'; + static final editProfile = 'edit-profile'; + static final notificationsSetting = 'notification'; static final product = '/product'; static final categories = '/categories'; @@ -30,51 +34,21 @@ class AppRouter { static GoRouter createRouter = GoRouter( navigatorKey: navigatorKey, initialLocation: initial, + redirect: (context, state) { + return null; + }, routes: [ - // GoRoute( - // path: initial, - // builder: (BuildContext context, GoRouterState state) { - // return BlocConsumer( - // listener: (context, state) { - // if (state is UserInfoSuccess || state is UserInfoFail) { - // FlutterNativeSplash.remove(); - // } - // }, - // builder: (context, state) { - // if (state is UserInfoSuccess) { - // return const HomePage(); - // } else { - // return const AuthPage(); - // } - // }, - // ); - // }, - // routes: [ - // GoRoute( - // path: product, - // builder: (BuildContext context, GoRouterState state) { - // return ProductPage(); - // }, - // routes: [], - // ), - // ], - // ), - + GoRoute( + path: auth, + builder: (context, state) { + return AuthPage(); + }, + ), StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) => - BlocBuilder( - builder: (context, userState) { - if (userState is UserInfoSuccess) { - if (home.contains(state.fullPath)) { - return HomePage(child: navigationShell); - } else { - return navigationShell; - } - } else { - return AuthPage(); - } - }, - ), + home.contains(state.fullPath) + ? HomePage(child: navigationShell) + : navigationShell, branches: [ StatefulShellBranch(routes: [ GoRoute( @@ -140,9 +114,24 @@ class AppRouter { ]), StatefulShellBranch(routes: [ GoRoute( - path: setting, - builder: (context, state) => SettingScreen(), - ), + path: setting, + builder: (context, state) => SettingScreen(), + routes: [ + GoRoute( + path: editProfile, + name: editProfile, + builder: (context, state) { + return EditProfilePage(); + }, + ), + GoRoute( + path: notificationsSetting, + name: notificationsSetting, + builder: (context, state) { + return NotificationsEttingPage(); + }, + ), + ]), ]), ], ), diff --git a/lib/data/models/setting_btns_model.dart b/lib/data/models/setting_btns_model.dart new file mode 100644 index 0000000..de9f8e0 --- /dev/null +++ b/lib/data/models/setting_btns_model.dart @@ -0,0 +1,10 @@ +import 'package:flutter/cupertino.dart'; +import 'package:proxibuy/core/gen/assets.gen.dart'; + +class SettingBtnsModel { + final String title; + final SvgGenImage icon; + final Function(BuildContext context)? onPressed; + + SettingBtnsModel({required this.title, required this.icon, this.onPressed}); +} diff --git a/lib/main.dart b/lib/main.dart index 9b71de0..4fa3aba 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,10 +22,8 @@ void main() async { BlocProvider(create: (context) => ThemModeCubit()), BlocProvider( create: (context) => CategoriesChildrenCubit()), - BlocProvider( - create: (context) => CategoriesCubit()..getAllCategories()), - BlocProvider( - create: (context) => UserInfoCubit()..getUserInfo()), + BlocProvider(create: (context) => CategoriesCubit()), + BlocProvider(create: (context) => UserInfoCubit()), ], child: const MyApp(), )); diff --git a/lib/presentation/ui/screens/auth/auth_page.dart b/lib/presentation/ui/screens/auth/auth_page.dart index 1f3ecfb..0f4c405 100644 --- a/lib/presentation/ui/screens/auth/auth_page.dart +++ b/lib/presentation/ui/screens/auth/auth_page.dart @@ -26,7 +26,10 @@ class _AuthPageState extends State { appBar: AppBar( title: Text( 'Auth', - style: Theme.of(context).textTheme.titleLarge, + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(color: Theme.of(context).colorScheme.onSurface), ), backgroundColor: Colors.transparent, surfaceTintColor: Colors.transparent, diff --git a/lib/presentation/ui/screens/home/home_desk_page.dart b/lib/presentation/ui/screens/home/home_desk_page.dart index 4ad3756..29f075b 100644 --- a/lib/presentation/ui/screens/home/home_desk_page.dart +++ b/lib/presentation/ui/screens/home/home_desk_page.dart @@ -18,7 +18,6 @@ class HomeDeskPage extends StatefulWidget { } class _HomeDeskPageState extends State { - int selectedIndex = 0; List deskScreens = [ ScreenModel( title: 'Home', @@ -34,10 +33,14 @@ class _HomeDeskPageState extends State { route: AppRouter.setting), ]; + int _getSelectedIndex(BuildContext context) { + final g = GoRouterState.of(context); + final String location = g.fullPath.toString(); + return deskScreens + .indexWhere((element) => element.route.startsWith(location)); + } + void _onItemTapped(BuildContext context, int index) { - setState(() { - selectedIndex = index; - }); switch (index) { case 0: context.go(AppRouter.initial); @@ -64,10 +67,6 @@ class _HomeDeskPageState 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( @@ -89,7 +88,7 @@ class _HomeDeskPageState extends State { Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), - color: selectedIndex == index + color: _getSelectedIndex(context) == index ? themeColor(context) ?.primaryLightSurface .withAlpha(90) @@ -103,7 +102,7 @@ class _HomeDeskPageState extends State { crossAxisAlignment: CrossAxisAlignment.end, children: [ deskScreens[index].icon.svg( - color: selectedIndex == index + color: _getSelectedIndex(context) == index ? Theme.of(context).primaryColor : Theme.of(context) .colorScheme @@ -115,7 +114,8 @@ class _HomeDeskPageState extends State { .textTheme .labelLarge ?.copyWith( - color: selectedIndex == index + color: _getSelectedIndex(context) == + index ? Theme.of(context).primaryColor : Theme.of(context) .colorScheme @@ -142,18 +142,16 @@ class _HomeDeskPageState extends State { 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), - ), + decoration: defaultInputDecoration(context).copyWith( + 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), + ), + )), )), 32.w, IconButton( diff --git a/lib/presentation/ui/screens/home/home_page.dart b/lib/presentation/ui/screens/home/home_page.dart index 4b5d2e7..492a400 100644 --- a/lib/presentation/ui/screens/home/home_page.dart +++ b/lib/presentation/ui/screens/home/home_page.dart @@ -44,7 +44,7 @@ class _HomePageState extends State { int _getSelectedIndex(BuildContext context) { final g = GoRouterState.of(context); final String location = g.fullPath.toString(); - return screens.indexWhere((element) => element.route == location); + return screens.indexWhere((element) => element.route.startsWith(location)); } @override diff --git a/lib/presentation/ui/screens/home/screens/home_screen.dart b/lib/presentation/ui/screens/home/screens/home_screen.dart index 50434e8..32d15e6 100644 --- a/lib/presentation/ui/screens/home/screens/home_screen.dart +++ b/lib/presentation/ui/screens/home/screens/home_screen.dart @@ -163,7 +163,10 @@ class _HomeScreenState extends State { padding: const EdgeInsets.only(left: 2.0), child: SelectableText( "Proxibuy", - style: Theme.of(context).textTheme.headlineSmall, + style: Theme.of(context) + .textTheme + .headlineSmall + ?.copyWith(color: Theme.of(context).colorScheme.onSurface), ), ), actions: [ diff --git a/lib/presentation/ui/screens/home/screens/setting_screen.dart b/lib/presentation/ui/screens/home/screens/setting_screen.dart index 998fbaf..2eda973 100644 --- a/lib/presentation/ui/screens/home/screens/setting_screen.dart +++ b/lib/presentation/ui/screens/home/screens/setting_screen.dart @@ -1,13 +1,178 @@ import 'package:flutter/material.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/data/models/setting_btns_model.dart'; +import 'package:proxibuy/presentation/ui/theme/responsive.dart'; -class SettingScreen extends StatelessWidget { +class SettingScreen extends StatefulWidget { const SettingScreen({super.key}); + @override + State createState() => _SettingScreenState(); +} + +class _SettingScreenState extends State { + final List _generalBtns = [ + SettingBtnsModel( + title: 'Edit Profile', + icon: Assets.icon.outline.profile, + onPressed: (context) { + context.goNamed(AppRouter.editProfile); + }, + ), + SettingBtnsModel( + title: 'Notifications', + icon: Assets.icon.outline.notification, + onPressed: (context) { + context.goNamed(AppRouter.notificationsSetting); + }, + ), + SettingBtnsModel(title: 'Security', icon: Assets.icon.outline.frame), + SettingBtnsModel( + title: 'Transactions & Wallets', icon: Assets.icon.outline.emptyWallet), + SettingBtnsModel( + title: 'Discounts & Prizes', icon: Assets.icon.outline.ticketDiscount), + SettingBtnsModel(title: 'Favorite Stores', icon: Assets.icon.outline.star), + SettingBtnsModel( + title: 'Store Introduction', icon: Assets.icon.outline.shopAdd), + ]; + + final List _preferencesBtns = [ + SettingBtnsModel( + title: 'Legal & Policies', icon: Assets.icon.outline.shield), + SettingBtnsModel( + title: 'Help & Support', icon: Assets.icon.outline.messageQuestion), + SettingBtnsModel(title: 'Logout', icon: Assets.icon.outline.logout), + ]; @override Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: Text('Setting'), + return Responsive(context).builder( + mobile: Scaffold( + appBar: AppBar( + title: Text( + 'Setting', + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(color: Theme.of(context).colorScheme.onSurface), + ), + ), + body: SingleChildScrollView( + child: Column( + children: [ + ListTile( + title: SelectableText( + 'General', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ListView.builder( + itemCount: _generalBtns.length, + physics: BouncingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return settingItem(context, item: _generalBtns[index]); + }, + ), + ListTile( + title: SelectableText( + 'Preferences', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ListView.builder( + itemCount: _preferencesBtns.length, + physics: BouncingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return settingItem(context, item: _preferencesBtns[index]); + }, + ) + ], + ), + ), + ), + desktop: Responsive(context).maxWidthInDesktop( + maxWidth: 1200, + child: (contxet, mw) => Scaffold( + body: SingleChildScrollView( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + children: [ + ListTile( + title: SelectableText( + 'General', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ListView.builder( + itemCount: _generalBtns.length, + physics: BouncingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return settingItem(context, + item: _generalBtns[index]); + }, + ), + ], + ), + ), + Expanded( + child: Column( + children: [ + ListTile( + title: SelectableText( + 'Preferences', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ListView.builder( + itemCount: _preferencesBtns.length, + physics: BouncingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return settingItem(context, + item: _preferencesBtns[index]); + }, + ) + ], + ), + ), + ], + ), + ), + ), + ), + ); + } + + Container settingItem(BuildContext context, + {required final SettingBtnsModel item}) { + return Container( + margin: EdgeInsets.symmetric(vertical: 8, horizontal: 16), + padding: EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + spreadRadius: 2, + blurRadius: 12, + offset: Offset(0, 4), + ), + ], + borderRadius: BorderRadius.circular(8), + color: Theme.of(context).colorScheme.surface, + ), + child: ListTile( + onTap: () => item.onPressed?.call(context), + leading: item.icon.svg(color: Theme.of(context).colorScheme.onSurface), + trailing: Assets.icon.outline.arrowRight + .svg(color: Theme.of(context).colorScheme.onSurface), + title: Text(item.title), ), ); } diff --git a/lib/presentation/ui/screens/setting/edit_profile_page.dart b/lib/presentation/ui/screens/setting/edit_profile_page.dart new file mode 100644 index 0000000..dea8c7b --- /dev/null +++ b/lib/presentation/ui/screens/setting/edit_profile_page.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:proxibuy/core/gen/assets.gen.dart'; +import 'package:proxibuy/core/utils/empty_space.dart'; +import 'package:proxibuy/presentation/ui/theme/responsive.dart'; +import 'package:proxibuy/presentation/ui/theme/theme.dart'; +import 'package:proxibuy/presentation/ui/widgets/dialog/dialog_manager.dart'; + +class EditProfilePage extends StatelessWidget { + const EditProfilePage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + 'Edit Profile', + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(color: Theme.of(context).colorScheme.onSurface), + ), + ), + body: Responsive(context).builder( + mobile: Form( + child: Padding( + padding: const EdgeInsets.all(16.0).copyWith(bottom: 32), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + Center( + child: SizedBox( + width: 64, height: 64, child: CircleAvatar())), + 16.h, + Text( + 'Username', + style: Theme.of(context).textTheme.titleMedium, + ), + 32.h, + TextFormField( + decoration: defaultInputDecoration(context).copyWith( + labelText: 'First Name', + )), + 16.h, + TextFormField( + decoration: defaultInputDecoration(context).copyWith( + labelText: 'Last Name', + )), + 16.h, + TextFormField( + decoration: defaultInputDecoration(context).copyWith( + labelText: 'Phone', + )), + 16.h, + TextFormField( + decoration: defaultInputDecoration(context).copyWith( + labelText: 'Email', + )), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Login with Password', + style: Theme.of(context).textTheme.titleLarge, + ), + subtitle: Text('Change Password'), + value: false, + onChanged: (value) { + DialogManager(context).changePasswordBS(); + }, + ), + ], + ), + SizedBox( + width: double.infinity, + height: 46, + child: ElevatedButton( + onPressed: () {}, + child: Text( + 'Confirm Changes', + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith(color: Colors.white), + )), + ) + ], + ), + ), + ), + desktop: Responsive(context).maxWidthInDesktop( + maxWidth: 800, + child: (context, mw) { + return Form( + child: Padding( + padding: const EdgeInsets.all(16.0).copyWith(bottom: 32), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + Center( + child: SizedBox( + width: 128, + height: 128, + child: CircleAvatar())), + 16.h, + Text( + 'Username', + style: Theme.of(context).textTheme.titleLarge, + ), + 32.h, + Row( + children: [ + Expanded( + child: TextFormField( + decoration: + defaultInputDecoration(context) + .copyWith( + labelText: 'First Name', + )), + ), + 16.w, + Expanded( + child: TextFormField( + decoration: + defaultInputDecoration(context) + .copyWith( + labelText: 'Last Name', + )), + ), + ], + ), + 16.h, + Row( + children: [ + Expanded( + child: TextFormField( + decoration: + defaultInputDecoration(context) + .copyWith( + labelText: 'Phone', + )), + ), + 16.w, + Expanded( + child: TextFormField( + decoration: + defaultInputDecoration(context) + .copyWith( + labelText: 'Email', + )), + ), + ], + ), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Login with Password Only', + style: Theme.of(context).textTheme.titleLarge, + ), + subtitle: Text('Change Password'), + value: false, + onChanged: (value) {}, + ), + ], + ), + SizedBox( + width: double.infinity, + height: 46, + child: ElevatedButton( + onPressed: () {}, + child: Text( + 'Confirm Changes', + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith(color: Colors.white), + )), + ) + ], + ), + ), + ); + })), + ); + } +} diff --git a/lib/presentation/ui/screens/setting/notifications_etting_page.dart b/lib/presentation/ui/screens/setting/notifications_etting_page.dart new file mode 100644 index 0000000..7e59a01 --- /dev/null +++ b/lib/presentation/ui/screens/setting/notifications_etting_page.dart @@ -0,0 +1,194 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:proxibuy/core/utils/empty_space.dart'; +import 'package:proxibuy/presentation/ui/theme/responsive.dart'; + +class NotificationsEttingPage extends StatefulWidget { + const NotificationsEttingPage({super.key}); + + @override + State createState() => + _NotificationsEttingPageState(); +} + +class _NotificationsEttingPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + 'Notifications', + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(color: Theme.of(context).colorScheme.onSurface), + ), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Responsive(context).builder( + desktop: Responsive(context).maxWidthInDesktop( + maxWidth: 1000, + child: (context, mw) { + return Row( + children: [ + Expanded( + child: Column( + children: [ + ListTile( + title: Text( + 'Notification Categories', + style: Theme.of(context).textTheme.titleLarge, + ), + ), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Special Offers', + ), + value: true, + onChanged: (value) {}, + ), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Order Status', + ), + value: false, + onChanged: (value) {}, + ), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Account Notifications', + ), + value: false, + onChanged: (value) {}, + ), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Product Updates', + ), + value: false, + onChanged: (value) {}, + ), + ], + )), + 16.w, + Expanded( + child: Column( + children: [ + ListTile( + title: Text( + 'Notification Frequency Control', + style: Theme.of(context).textTheme.titleLarge, + ), + ), + 16.h, + CheckboxListTile.adaptive( + title: Text( + 'Daily', + ), + value: false, + onChanged: (value) {}, + ), + 16.h, + CheckboxListTile.adaptive( + title: Text( + 'Weekly', + ), + value: false, + onChanged: (value) {}, + ), + 16.h, + CheckboxListTile.adaptive( + title: Text( + 'monthly', + ), + value: true, + onChanged: (value) {}, + ), + ], + )) + ], + ); + }), + mobile: Column( + children: [ + ListTile( + title: Text( + 'Notification Categories', + style: Theme.of(context).textTheme.titleLarge, + ), + ), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Special Offers', + ), + value: false, + onChanged: (value) {}, + ), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Order Status', + ), + value: true, + onChanged: (value) {}, + ), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Account Notifications', + ), + value: false, + onChanged: (value) {}, + ), + 16.h, + SwitchListTile.adaptive( + title: Text( + 'Product Updates', + ), + value: false, + onChanged: (value) {}, + ), + 16.h, + ListTile( + title: Text( + 'Notification Frequency Control', + style: Theme.of(context).textTheme.titleLarge, + ), + ), + 16.h, + CheckboxListTile.adaptive( + title: Text( + 'Daily', + ), + value: false, + onChanged: (value) {}, + ), + 16.h, + CheckboxListTile.adaptive( + title: Text( + 'Weekly', + ), + value: false, + onChanged: (value) {}, + ), + 16.h, + CheckboxListTile.adaptive( + title: Text( + 'monthly', + ), + value: true, + onChanged: (value) {}, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/ui/theme/theme.dart b/lib/presentation/ui/theme/theme.dart index 8338785..179207b 100644 --- a/lib/presentation/ui/theme/theme.dart +++ b/lib/presentation/ui/theme/theme.dart @@ -21,11 +21,29 @@ final ThemeData appTheme = ThemeData( appBarTheme: const AppBarTheme( surfaceTintColor: lightBackground, backgroundColor: lightBackground, + toolbarTextStyle: TextStyle(color: fontDark), + centerTitle: true, elevation: 2, titleTextStyle: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold), ), + switchTheme: SwitchThemeData( + thumbColor: MaterialStateProperty.resolveWith( + (Set states) { + return Colors.white; // Thumb color when switch is off + }, + ), + trackColor: MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.selected)) { + return primarySwatch; // Thumb color when switch is on + } + return Colors.grey[300]!; // Thumb color when switch is off + }, + ), + ), + // Text Theme textTheme: const TextTheme( displayLarge: @@ -102,6 +120,8 @@ final ThemeData darkTheme = ThemeData( appBarTheme: const AppBarTheme( backgroundColor: darkBackground, surfaceTintColor: darkBackground, + centerTitle: true, + toolbarTextStyle: TextStyle(color: fontLight), elevation: 2, titleTextStyle: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold), @@ -168,3 +188,21 @@ final ThemeData darkTheme = ThemeData( CustomColors? themeColor(BuildContext context) { return Theme.of(context).extension(); } + +OutlineInputBorder defaultBorder(BuildContext context) => OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: + BorderSide(color: Theme.of(context).colorScheme.onSurface, width: 2)); +OutlineInputBorder defaultFocusedBorder( + BuildContext context) => + OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: themeColor(context)!.primarySwatch.shade400, width: 2)); + +InputDecoration defaultInputDecoration(BuildContext context) => InputDecoration( + floatingLabelStyle: + TextStyle(color: themeColor(context)!.primarySwatch.shade400), + enabledBorder: defaultBorder(context), + border: defaultBorder(context), + focusedBorder: defaultFocusedBorder(context)); diff --git a/lib/presentation/ui/widgets/dialog/dialog_manager.dart b/lib/presentation/ui/widgets/dialog/dialog_manager.dart index c498efe..5b0a633 100644 --- a/lib/presentation/ui/widgets/dialog/dialog_manager.dart +++ b/lib/presentation/ui/widgets/dialog/dialog_manager.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; +import 'package:proxibuy/core/utils/empty_space.dart'; +import 'package:proxibuy/presentation/ui/theme/theme.dart'; class DialogManager { final BuildContext context; - DialogManager({required this.context}); + DialogManager(this.context); // Future _showDialog(Widget widget) async { // await showDialog( @@ -16,4 +19,74 @@ class DialogManager { // }, // ); // } + Future _showBottomSheet(Widget widget) async { + await showModalBottomSheet( + context: context, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + builder: (context) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: widget, + ); + }, + ); + } + + void changePasswordBS() async { + await _showBottomSheet(Form( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + ListTile( + title: Text( + 'Login with Password', + style: Theme.of(context).textTheme.titleLarge, + ), + subtitle: Text('"To confirm, please enter a password."'), + ), + 32.h, + TextFormField( + decoration: defaultInputDecoration(context).copyWith( + labelText: 'Password', + )), + 16.h, + TextFormField( + decoration: defaultInputDecoration(context).copyWith( + labelText: 're-Password', + )), + 32.h, + TextButton(onPressed: () {}, child: Text('Forgot Your Password?')) + ], + ), + Row( + children: [ + Expanded( + child: SizedBox( + height: 46, + child: ElevatedButton(onPressed: () {}, child: Text('Confirm')), + )), + 12.w, + Expanded( + child: SizedBox( + height: 46, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + side: BorderSide( + color: Theme.of(context).primaryColor, + ), + ), + onPressed: () {}, + child: Text('Cancel')), + )) + ], + ) + ], + ), + )); + } }