From dbe65bffff0d6ad719b61e9e07be347a9d968efa Mon Sep 17 00:00:00 2001 From: ashrafzarkanisala Date: Sat, 31 Aug 2024 15:35:17 +0300 Subject: [PATCH] access management design revamp, responsiveness and buttons --- lib/main.dart | 90 +--- .../view/access_management.dart | 394 +++++++++--------- lib/pages/auth/bloc/auth_bloc.dart | 6 +- lib/pages/auth/view/login_web_page.dart | 319 +++++++++----- .../common/buttons/search_reset_buttons.dart | 19 +- lib/pages/common/date_time_widget.dart | 32 +- .../common/text_field/custom_text_field.dart | 6 +- .../text_field/custom_web_textfield.dart | 10 +- .../view/device_managment_page.dart | 4 +- .../widgets/device_managment_body.dart | 3 +- lib/utils/theme/theme.dart | 53 +++ lib/web_layout/web_app_bar.dart | 150 +++++-- lib/web_layout/web_scaffold.dart | 20 +- 13 files changed, 642 insertions(+), 464 deletions(-) create mode 100644 lib/utils/theme/theme.dart diff --git a/lib/main.dart b/lib/main.dart index a94c79c3..b4fcb72d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,30 +7,35 @@ import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.da import 'package:go_router/go_router.dart'; import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/utils/app_routes.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart'; +import 'package:syncrow_web/utils/theme/theme.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); initialSetup(); - String checkToken = await AuthBloc.getTokenAndValidate(); - GoRouter router = GoRouter( - initialLocation: - checkToken == 'Success' ? RoutesConst.home : RoutesConst.auth, - routes: AppRoutes.getRoutes(), - ); - runApp(MyApp( - router: router, - )); + runApp(MyApp()); } class MyApp extends StatelessWidget { - final GoRouter router; - const MyApp({ + MyApp({ super.key, - required this.router, }); + final GoRouter _router = GoRouter( + initialLocation: RoutesConst.auth, + routes: AppRoutes.getRoutes(), + redirect: (context, state) async { + String checkToken = await AuthBloc.getTokenAndValidate(); + final loggedIn = checkToken == 'Success'; + final goingToLogin = state.uri.toString() == RoutesConst.auth; + + if (!loggedIn && !goingToLogin) return RoutesConst.auth; + if (loggedIn && goingToLogin) return RoutesConst.home; + + return null; + }, + ); + @override Widget build(BuildContext context) { return MultiBlocProvider( @@ -41,7 +46,7 @@ class MyApp extends StatelessWidget { ) ], child: MaterialApp.router( - debugShowCheckedModeBanner: false, // Hide debug banner + debugShowCheckedModeBanner: false, scrollBehavior: const MaterialScrollBehavior().copyWith( dragDevices: { PointerDeviceKind.mouse, @@ -50,61 +55,8 @@ class MyApp extends StatelessWidget { PointerDeviceKind.unknown, }, ), - - theme: ThemeData( - fontFamily: 'Aftika', - textTheme: const TextTheme( - bodySmall: TextStyle( - fontSize: 13, - color: ColorsManager.whiteColors, - fontWeight: FontWeight.bold), - bodyMedium: TextStyle(color: Colors.black87, fontSize: 14), - bodyLarge: TextStyle(fontSize: 16, color: Colors.white), - headlineSmall: TextStyle(color: Colors.black87, fontSize: 18), - headlineMedium: TextStyle(color: Colors.black87, fontSize: 20), - headlineLarge: TextStyle( - color: Colors.white, - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - colorScheme: ColorScheme.fromSeed( - seedColor: ColorsManager.blueColor, - primary: ColorsManager.blueColor, - onSurface: Colors.grey.shade400, - ), - switchTheme: SwitchThemeData( - thumbColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return ColorsManager.blueColor; - } - return ColorsManager.whiteColors; - }), - trackColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return ColorsManager.blueColor.withOpacity(0.5); - } - return ColorsManager.whiteColors; - }), - ), - checkboxTheme: CheckboxThemeData( - fillColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return ColorsManager.blueColor; - } - return Colors.grey.shade200; - }), - checkColor: WidgetStateProperty.all(Colors.white), - side: const BorderSide(color: ColorsManager.whiteColors), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), - ), - ), - ), - - routeInformationProvider: router.routeInformationProvider, - routerDelegate: router.routerDelegate, - routeInformationParser: router.routeInformationParser, + theme: myTheme, + routerConfig: _router, )); } } diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index 76c2d26e..2f5deb6d 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -4,237 +4,86 @@ import 'package:syncrow_web/pages/access_management/bloc/access_bloc.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_event.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_state.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart'; import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/common/date_time_widget.dart'; +import 'package:syncrow_web/pages/common/filter/filter_widget.dart'; import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; -class AccessManagementPage extends StatelessWidget { +class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { const AccessManagementPage({super.key}); + @override Widget build(BuildContext context) { - Size size = MediaQuery.of(context).size; + final isLargeScreen = isLargeScreenSize(context); + final isSmallScreen = isSmallScreenSize(context); + final padding = + isLargeScreen ? const EdgeInsets.all(30) : const EdgeInsets.all(15); + return WebScaffold( enableMenuSideba: false, - appBarTitle: Row( - children: [ - Text( - 'Access Management', - style: Theme.of(context).textTheme.headlineLarge, - ) - ], - ), - appBarBody: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Physical Access', - style: Theme.of(context).textTheme.headlineMedium!.copyWith(color: Colors.white), - ), - const NavigateHomeGridView(), - ], + appBarTitle: FittedBox( + child: Text( + 'Access Management', + style: Theme.of(context).textTheme.headlineLarge, ), - ], + ), + centerBody: Text( + 'Physical Access', + style: Theme.of(context) + .textTheme + .headlineMedium! + .copyWith(color: Colors.white), + ), + rightBody: const NavigateHomeGridView(), scaffoldBody: BlocProvider( - create: (BuildContext context) => AccessBloc()..add(FetchTableData()), + create: (BuildContext context) => + AccessBloc()..add(FetchTableData()), child: BlocConsumer( listener: (context, state) {}, builder: (context, state) { final accessBloc = BlocProvider.of(context); final filteredData = accessBloc.filteredData; + return state is AccessLoaded ? const Center(child: CircularProgressIndicator()) : Container( - padding: const EdgeInsets.all(30), - height: size.height, - width: size.width, + padding: padding, + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - decoration: containerDecoration, - height: size.height * 0.05, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: BlocProvider.of(context).tabs.length, - shrinkWrap: true, - itemBuilder: (context, index) { - final isSelected = - index == BlocProvider.of(context).selectedIndex; - return InkWell( - onTap: () { - BlocProvider.of(context) - .add(TabChangedEvent(index)); - }, - child: Container( - decoration: BoxDecoration( - color: ColorsManager.boxColor, - border: Border.all( - color: isSelected ? Colors.blue : Colors.transparent, - width: 2.0, - ), - borderRadius: index == 0 - ? const BorderRadius.only( - topLeft: Radius.circular(10), - bottomLeft: Radius.circular(10)) - : index == 3 - ? const BorderRadius.only( - topRight: Radius.circular(10), - bottomRight: Radius.circular(10)) - : null, - ), - padding: const EdgeInsets.only(left: 10, right: 10), - child: Center( - child: Text( - BlocProvider.of(context).tabs[index], - style: TextStyle( - color: isSelected ? Colors.blue : Colors.black, - ), - ), - ), - ), - ); - }, - ), - ), - const SizedBox( - height: 20, - ), - Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - textBaseline: TextBaseline.ideographic, - children: [ - Container( - width: size.width * 0.15, - child: CustomWebTextField( - controller: accessBloc.passwordName, - isRequired: true, - textFieldName: 'Name', - description: '', - ), - ), - const SizedBox( - width: 15, - ), - DateTimeWebWidget( - icon: Assets.calendarIcon, - isRequired: false, - title: 'Access Time', - size: size, - endTime: () { - accessBloc.add(SelectTime(context: context, isStart: false)); - }, - startTime: () { - accessBloc.add(SelectTime(context: context, isStart: true)); - }, - firstString: BlocProvider.of(context).startTime, - secondString: BlocProvider.of(context).endTime, - ), - const SizedBox( - width: 15, - ), - SizedBox( - width: size.width * 0.06, - child: Container( - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - accessBloc.add(FilterDataEvent( - selectedTabIndex: BlocProvider.of( - context) - .selectedIndex, // Pass the selected tab index - passwordName: - accessBloc.passwordName.text.toLowerCase(), - startTime: accessBloc.effectiveTimeTimeStamp, - endTime: accessBloc.expirationTimeTimeStamp)); - }, - borderRadius: 9, - child: const Text('Search'))), - ), - const SizedBox( - width: 10, - ), - SizedBox( - width: size.width * 0.06, - child: Container( - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - accessBloc.add(ResetSearch()); - }, - backgroundColor: ColorsManager.whiteColors, - borderRadius: 9, - child: Text( - 'Reset', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black), - ), - ), - ), - ), - ], - ), - const SizedBox( - height: 20, - ), - Wrap( - children: [ - Container( - width: size.width * 0.15, - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return const VisitorPasswordDialog(); - }, - ).then((v) { - if (v != null) { - accessBloc.add(FetchTableData()); - } - }); - }, - borderRadius: 8, - child: const Text('+ Create Visitor Password ')), - ), - const SizedBox( - width: 10, - ), - Container( - width: size.width * 0.12, - decoration: containerDecoration, - child: DefaultButton( - borderRadius: 8, - backgroundColor: ColorsManager.whiteColors, - child: Text( - 'Admin Password', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black), - ))) - ], - ), - const SizedBox( - height: 20, + FilterWidget( + size: MediaQuery.of(context).size, + tabs: accessBloc.tabs, + selectedIndex: accessBloc.selectedIndex, + onTabChanged: (index) { + accessBloc.add(TabChangedEvent(index)); + }, ), + const SizedBox(height: 20), + if (isSmallScreen) + _buildSmallSearchFilters(context, accessBloc) + else + _buildNormalSearchWidgets(context, accessBloc), + const SizedBox(height: 20), + _buildVisitorAdminPasswords(context, accessBloc), + const SizedBox(height: 20), Expanded( child: DynamicTable( isEmpty: filteredData.isEmpty, withCheckBox: false, - size: size, + size: MediaQuery.of(context).size, cellDecoration: containerDecoration, headers: const [ 'Name', @@ -256,12 +105,155 @@ class AccessManagementPage extends StatelessWidget { item.passwordStatus.value, ]; }).toList(), - ) - // : const Center(child: CircularProgressIndicator()), - ) + )), ], ), ); }))); } + + Wrap _buildVisitorAdminPasswords( + BuildContext context, AccessBloc accessBloc) { + return Wrap( + spacing: 10, + runSpacing: 10, + children: [ + Container( + width: 205, + height: 35, + decoration: containerDecoration, + child: DefaultButton( + onPressed: () { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return const VisitorPasswordDialog(); + }, + ).then((v) { + if (v != null) { + accessBloc.add(FetchTableData()); + } + }); + }, + borderRadius: 8, + child: Text( + '+ Create Visitor Password ', + style: context.textTheme.titleSmall! + .copyWith(color: Colors.white, fontSize: 12), + )), + ), + Container( + width: 133, + height: 35, + decoration: containerDecoration, + child: DefaultButton( + borderRadius: 8, + backgroundColor: ColorsManager.whiteColors, + child: Text( + 'Admin Password', + style: context.textTheme.titleSmall! + .copyWith(color: Colors.black, fontSize: 12), + )), + ), + ], + ); + } + + Row _buildNormalSearchWidgets(BuildContext context, AccessBloc accessBloc) { + return Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + textBaseline: TextBaseline.ideographic, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: CustomWebTextField( + controller: accessBloc.passwordName, + height: 36, + isRequired: true, + textFieldName: 'Name', + description: '', + ), + ), + const SizedBox(width: 15), + SizedBox( + child: DateTimeWebWidget( + icon: Assets.calendarIcon, + isRequired: false, + title: 'Access Time', + size: MediaQuery.of(context).size, + endTime: () { + accessBloc.add(SelectTime(context: context, isStart: false)); + }, + startTime: () { + accessBloc.add(SelectTime(context: context, isStart: true)); + }, + firstString: BlocProvider.of(context).startTime, + secondString: BlocProvider.of(context).endTime, + ), + ), + const SizedBox(width: 15), + SearchResetButtons( + onSearch: () { + accessBloc.add(FilterDataEvent( + selectedTabIndex: + BlocProvider.of(context).selectedIndex, + passwordName: accessBloc.passwordName.text.toLowerCase(), + startTime: accessBloc.effectiveTimeTimeStamp, + endTime: accessBloc.expirationTimeTimeStamp)); + }, + onReset: () { + accessBloc.add(ResetSearch()); + }, + ), + ], + ); + } + + Widget _buildSmallSearchFilters(BuildContext context, AccessBloc accessBloc) { + return Wrap( + spacing: 20, + runSpacing: 10, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: CustomWebTextField( + controller: accessBloc.passwordName, + isRequired: true, + height: 36, + textFieldName: 'Name', + description: '', + ), + ), + DateTimeWebWidget( + icon: Assets.calendarIcon, + isRequired: false, + title: 'Access Time', + size: MediaQuery.of(context).size, + endTime: () { + accessBloc.add(SelectTime(context: context, isStart: false)); + }, + startTime: () { + accessBloc.add(SelectTime(context: context, isStart: true)); + }, + firstString: BlocProvider.of(context).startTime, + secondString: BlocProvider.of(context).endTime, + ), + SearchResetButtons( + onSearch: () { + accessBloc.add(FilterDataEvent( + selectedTabIndex: + BlocProvider.of(context).selectedIndex, + passwordName: accessBloc.passwordName.text.toLowerCase(), + startTime: accessBloc.effectiveTimeTimeStamp, + endTime: accessBloc.expirationTimeTimeStamp)); + }, + onReset: () { + accessBloc.add(ResetSearch()); + }, + ), + ], + ); + } } diff --git a/lib/pages/auth/bloc/auth_bloc.dart b/lib/pages/auth/bloc/auth_bloc.dart index ea53fdd3..a08cd0d3 100644 --- a/lib/pages/auth/bloc/auth_bloc.dart +++ b/lib/pages/auth/bloc/auth_bloc.dart @@ -135,6 +135,7 @@ class AuthBloc extends Bloc { emit(const LoginFailure(error: 'Something went wrong')); return; } + token = await AuthenticationAPI.loginWithEmail( model: LoginWithEmailModel( email: event.username, @@ -143,10 +144,10 @@ class AuthBloc extends Bloc { ); } catch (failure) { validate = 'Invalid Credentials!'; - emit(AuthInitialState()); - // emit(const LoginFailure(error: 'Something went wrong')); + emit(const LoginFailure(error: 'Invalid Credentials!')); return; } + if (token.accessTokenIsNotEmpty) { FlutterSecureStorage storage = const FlutterSecureStorage(); await storage.write( @@ -155,7 +156,6 @@ class AuthBloc extends Bloc { key: UserModel.userUuidKey, value: Token.decodeToken(token.accessToken)['uuid'].toString()); user = UserModel.fromToken(token); - debugPrint(token.accessToken); loginEmailController.clear(); loginPasswordController.clear(); emit(LoginSuccess()); diff --git a/lib/pages/auth/view/login_web_page.dart b/lib/pages/auth/view/login_web_page.dart index 09191fb1..a88aac07 100644 --- a/lib/pages/auth/view/login_web_page.dart +++ b/lib/pages/auth/view/login_web_page.dart @@ -10,7 +10,6 @@ import 'package:syncrow_web/pages/auth/model/region_model.dart'; import 'package:syncrow_web/pages/auth/view/forget_password_page.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/first_layer.dart'; -import 'package:syncrow_web/pages/home/view/home_page.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart'; @@ -32,7 +31,7 @@ class _LoginWebPageState extends State { child: BlocConsumer( listener: (context, state) { if (state is LoginSuccess) { - context.replace(RoutesConst.home); + GoRouter.of(context).go(RoutesConst.home); } else if (state is LoginFailure) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -55,7 +54,8 @@ class _LoginWebPageState extends State { late ScrollController _scrollController; _scrollController = ScrollController(); void _scrollToCenter() { - final double middlePosition = _scrollController.position.maxScrollExtent / 2; + final double middlePosition = + _scrollController.position.maxScrollExtent / 2; _scrollController.animateTo( middlePosition, duration: const Duration(seconds: 1), @@ -99,9 +99,11 @@ class _LoginWebPageState extends State { child: Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), - borderRadius: const BorderRadius.all(Radius.circular(30)), + borderRadius: const BorderRadius.all( + Radius.circular(30)), border: Border.all( - color: ColorsManager.graysColor.withOpacity(0.2))), + color: ColorsManager.graysColor + .withOpacity(0.2))), child: Form( key: loginBloc.loginFormKey, child: Padding( @@ -109,16 +111,22 @@ class _LoginWebPageState extends State { horizontal: size.width * 0.02, vertical: size.width * 0.003), child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ const SizedBox(height: 40), Text('Login', - style: Theme.of(context).textTheme.headlineLarge), + style: Theme.of(context) + .textTheme + .headlineLarge), SizedBox(height: size.height * 0.03), Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.start, children: [ Text( "Country/Region", @@ -126,57 +134,81 @@ class _LoginWebPageState extends State { .textTheme .bodySmall! .copyWith( - fontSize: 14, fontWeight: FontWeight.w400), + fontSize: 14, + fontWeight: + FontWeight.w400), ), const SizedBox( height: 10, ), SizedBox( - child: DropdownButtonFormField( + child: DropdownButtonFormField< + String>( padding: EdgeInsets.zero, - value: loginBloc.regionList!.any((region) => - region.id == loginBloc.regionUuid) + value: loginBloc.regionList! + .any((region) => + region.id == + loginBloc + .regionUuid) ? loginBloc.regionUuid : null, - validator: loginBloc.validateRegion, + validator: + loginBloc.validateRegion, icon: const Icon( - Icons.keyboard_arrow_down_outlined, + Icons + .keyboard_arrow_down_outlined, ), - decoration: textBoxDecoration()!.copyWith( - errorStyle: const TextStyle(height: 0), + decoration: textBoxDecoration()! + .copyWith( + errorStyle: const TextStyle( + height: 0), hintText: null, ), hint: SizedBox( width: size.width * 0.12, child: Align( - alignment: Alignment.centerLeft, + alignment: + Alignment.centerLeft, child: Text( 'Select your region/country', - textAlign: TextAlign.center, + textAlign: + TextAlign.center, style: Theme.of(context) .textTheme .bodySmall! .copyWith( - color: ColorsManager.grayColor, - fontWeight: FontWeight.w400), - overflow: TextOverflow.ellipsis, + color: + ColorsManager + .grayColor, + fontWeight: + FontWeight + .w400), + overflow: + TextOverflow.ellipsis, ), ), ), isDense: true, - style: const TextStyle(color: Colors.black), - items: - loginBloc.regionList!.map((RegionModel region) { - return DropdownMenuItem( + style: const TextStyle( + color: Colors.black), + items: loginBloc.regionList! + .map((RegionModel region) { + return DropdownMenuItem< + String>( value: region.id, child: SizedBox( - width: size.width * 0.08, - child: Text(region.name)), + width: + size.width * 0.08, + child: + Text(region.name)), ); }).toList(), onChanged: (String? value) { - loginBloc.add(CheckEnableEvent()); - loginBloc.add(SelectRegionEvent(val: value!)); + loginBloc + .add(CheckEnableEvent()); + loginBloc.add( + SelectRegionEvent( + val: value!)); }, ), ) @@ -184,8 +216,10 @@ class _LoginWebPageState extends State { ), const SizedBox(height: 20.0), Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.start, children: [ Text( "Email", @@ -193,7 +227,9 @@ class _LoginWebPageState extends State { .textTheme .bodySmall! .copyWith( - fontSize: 14, fontWeight: FontWeight.w400), + fontSize: 14, + fontWeight: + FontWeight.w400), ), const SizedBox( height: 10, @@ -201,29 +237,42 @@ class _LoginWebPageState extends State { SizedBox( child: TextFormField( onChanged: (value) { - loginBloc.add(CheckEnableEvent()); + loginBloc + .add(CheckEnableEvent()); }, - validator: loginBloc.loginValidateEmail, - controller: loginBloc.loginEmailController, - decoration: textBoxDecoration()!.copyWith( - errorStyle: const TextStyle( - height: 0), // Hide the error text space - hintText: 'Enter your email address', - hintStyle: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - color: ColorsManager.grayColor, - fontWeight: FontWeight.w400)), - style: const TextStyle(color: Colors.black), + validator: loginBloc + .loginValidateEmail, + controller: loginBloc + .loginEmailController, + decoration: textBoxDecoration()! + .copyWith( + errorStyle: const TextStyle( + height: + 0), // Hide the error text space + hintText: + 'Enter your email address', + hintStyle: Theme.of( + context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager + .grayColor, + fontWeight: + FontWeight + .w400)), + style: const TextStyle( + color: Colors.black), ), ), ], ), const SizedBox(height: 20.0), Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.start, children: [ Text( "Password", @@ -231,7 +280,9 @@ class _LoginWebPageState extends State { .textTheme .bodySmall! .copyWith( - fontSize: 14, fontWeight: FontWeight.w400), + fontSize: 14, + fontWeight: + FontWeight.w400), ), const SizedBox( height: 10, @@ -239,39 +290,54 @@ class _LoginWebPageState extends State { SizedBox( child: TextFormField( onChanged: (value) { - loginBloc.add(CheckEnableEvent()); + loginBloc + .add(CheckEnableEvent()); }, - validator: loginBloc.validatePassword, - obscureText: loginBloc.obscureText, - keyboardType: TextInputType.visiblePassword, - controller: loginBloc.loginPasswordController, - decoration: textBoxDecoration()!.copyWith( - hintText: 'At least 8 characters', + validator: + loginBloc.validatePassword, + obscureText: + loginBloc.obscureText, + keyboardType: TextInputType + .visiblePassword, + controller: loginBloc + .loginPasswordController, + decoration: textBoxDecoration()! + .copyWith( + hintText: + 'At least 8 characters', hintStyle: Theme.of(context) .textTheme .bodySmall! .copyWith( - color: ColorsManager.grayColor, - fontWeight: FontWeight.w400), + color: ColorsManager + .grayColor, + fontWeight: + FontWeight.w400), suffixIcon: IconButton( onPressed: () { - loginBloc.add(PasswordVisibleEvent( - newValue: loginBloc.obscureText)); + loginBloc.add( + PasswordVisibleEvent( + newValue: loginBloc + .obscureText)); }, icon: SizedBox( child: SvgPicture.asset( loginBloc.obscureText - ? Assets.visiblePassword - : Assets.invisiblePassword, + ? Assets + .visiblePassword + : Assets + .invisiblePassword, height: 15, width: 15, ), ), ), errorStyle: const TextStyle( - height: 0), // Hide the error text space + height: + 0), // Hide the error text space ), - style: const TextStyle(color: Colors.black), + style: const TextStyle( + color: Colors.black), ), ), ], @@ -281,11 +347,13 @@ class _LoginWebPageState extends State { ), SizedBox( child: Row( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisAlignment: + MainAxisAlignment.end, children: [ InkWell( onTap: () { - Navigator.of(context).push(MaterialPageRoute( + Navigator.of(context) + .push(MaterialPageRoute( builder: (context) => const ForgetPasswordPage(), )); @@ -298,7 +366,8 @@ class _LoginWebPageState extends State { .copyWith( color: Colors.black, fontSize: 14, - fontWeight: FontWeight.w400), + fontWeight: + FontWeight.w400), ), ), ], @@ -310,16 +379,18 @@ class _LoginWebPageState extends State { Row( children: [ Transform.scale( - scale: 1.2, // Adjust the scale as needed + scale: + 1.2, // Adjust the scale as needed child: Checkbox( - fillColor: - MaterialStateProperty.all(Colors.white), + fillColor: MaterialStateProperty + .all(Colors.white), activeColor: Colors.white, value: loginBloc.isChecked, checkColor: Colors.black, shape: const CircleBorder(), onChanged: (bool? newValue) { - loginBloc.add(CheckBoxEvent(newValue: newValue)); + loginBloc.add(CheckBoxEvent( + newValue: newValue)); }, ), ), @@ -328,36 +399,45 @@ class _LoginWebPageState extends State { child: RichText( text: TextSpan( text: 'Agree to ', - style: const TextStyle(color: Colors.white), + style: const TextStyle( + color: Colors.white), children: [ TextSpan( - text: '(Terms of Service)', + text: + '(Terms of Service)', style: const TextStyle( color: Colors.black, ), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL( - 'https://example.com/terms'); - }, + recognizer: + TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL( + 'https://example.com/terms'); + }, ), TextSpan( - text: ' (Legal Statement)', - style: const TextStyle(color: Colors.black), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL( - 'https://example.com/legal'); - }, + text: + ' (Legal Statement)', + style: const TextStyle( + color: Colors.black), + recognizer: + TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL( + 'https://example.com/legal'); + }, ), TextSpan( - text: ' (Privacy Statement)', - style: const TextStyle(color: Colors.black), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL( - 'https://example.com/privacy'); - }, + text: + ' (Privacy Statement)', + style: const TextStyle( + color: Colors.black), + recognizer: + TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL( + 'https://example.com/privacy'); + }, ), ], ), @@ -367,35 +447,49 @@ class _LoginWebPageState extends State { ), const SizedBox(height: 20.0), Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, children: [ SizedBox( width: size.width * 0.2, child: DefaultButton( - enabled: loginBloc.checkValidate, + enabled: + loginBloc.checkValidate, child: Text('Sign in', style: Theme.of(context) .textTheme .labelLarge! .copyWith( fontSize: 14, - color: loginBloc.checkValidate - ? ColorsManager.whiteColors - : ColorsManager.whiteColors - .withOpacity(0.2), + color: loginBloc + .checkValidate + ? ColorsManager + .whiteColors + : ColorsManager + .whiteColors + .withOpacity( + 0.2), )), onPressed: () { - if (loginBloc.loginFormKey.currentState! + if (loginBloc.loginFormKey + .currentState! .validate()) { - loginBloc.add(LoginButtonPressed( - regionUuid: loginBloc.regionUuid, - username: loginBloc.loginEmailController.text, - password: - loginBloc.loginPasswordController.text, + loginBloc + .add(LoginButtonPressed( + regionUuid: + loginBloc.regionUuid, + username: loginBloc + .loginEmailController + .text, + password: loginBloc + .loginPasswordController + .text, )); } else { - loginBloc.add(ChangeValidateEvent()); + loginBloc.add( + ChangeValidateEvent()); } }, ), @@ -404,8 +498,10 @@ class _LoginWebPageState extends State { ), const SizedBox(height: 15.0), Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, children: [ SizedBox( child: Text( @@ -430,7 +526,8 @@ class _LoginWebPageState extends State { ), ), ), - if (state is AuthLoading) const Center(child: CircularProgressIndicator()) + if (state is AuthLoading) + const Center(child: CircularProgressIndicator()) ], ); } diff --git a/lib/pages/common/buttons/search_reset_buttons.dart b/lib/pages/common/buttons/search_reset_buttons.dart index a03b889a..cdb09c21 100644 --- a/lib/pages/common/buttons/search_reset_buttons.dart +++ b/lib/pages/common/buttons/search_reset_buttons.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -23,14 +24,18 @@ class SearchResetButtons extends StatelessWidget { const SizedBox(height: 25), Center( child: Container( - height: 43, + height: 35, width: 100, decoration: containerDecoration, child: Center( child: DefaultButton( onPressed: onSearch, borderRadius: 9, - child: const Text('Search'), + child: Text( + 'Search', + style: context.textTheme.titleSmall! + .copyWith(color: Colors.white, fontSize: 12), + ), ), ), ), @@ -44,21 +49,19 @@ class SearchResetButtons extends StatelessWidget { const SizedBox(height: 25), Center( child: Container( - height: 43, + height: 35, width: 100, decoration: containerDecoration, child: Center( child: DefaultButton( backgroundColor: ColorsManager.whiteColors, borderRadius: 9, + onPressed: onReset, child: Text( 'Reset', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black), + style: context.textTheme.titleSmall! + .copyWith(color: Colors.black, fontSize: 12), ), - onPressed: onReset, ), ), ), diff --git a/lib/pages/common/date_time_widget.dart b/lib/pages/common/date_time_widget.dart index 0b8fa0da..824ab382 100644 --- a/lib/pages/common/date_time_widget.dart +++ b/lib/pages/common/date_time_widget.dart @@ -36,7 +36,10 @@ class DateTimeWebWidget extends StatelessWidget { if (isRequired) Text( '* ', - style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: Colors.red), + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), ), Text( title, @@ -51,8 +54,9 @@ class DateTimeWebWidget extends StatelessWidget { height: 8, ), Container( - height: size.height * 0.055, - padding: const EdgeInsets.only(top: 10, bottom: 10, right: 30, left: 10), + height: size.height * 0.056, + padding: + const EdgeInsets.only(top: 10, bottom: 10, right: 30, left: 10), decoration: containerDecoration, child: FittedBox( child: Column( @@ -65,10 +69,13 @@ class DateTimeWebWidget extends StatelessWidget { child: FittedBox( child: Text( firstString, - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: ColorsManager.grayColor, - fontSize: 12, - fontWeight: FontWeight.w400), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager.grayColor, + fontSize: 12, + fontWeight: FontWeight.w400), ), )), const SizedBox( @@ -83,10 +90,13 @@ class DateTimeWebWidget extends StatelessWidget { child: FittedBox( child: Text( secondString, - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: ColorsManager.grayColor, - fontSize: 12, - fontWeight: FontWeight.w400), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager.grayColor, + fontSize: 12, + fontWeight: FontWeight.w400), ), )), const SizedBox( diff --git a/lib/pages/common/text_field/custom_text_field.dart b/lib/pages/common/text_field/custom_text_field.dart index 2a379982..2c29762c 100644 --- a/lib/pages/common/text_field/custom_text_field.dart +++ b/lib/pages/common/text_field/custom_text_field.dart @@ -8,7 +8,7 @@ class StatefulTextField extends StatefulWidget { this.hintText = 'Please enter', required this.width, this.elevation = 0, - required this.controller, // Add the controller + required this.controller, }); final String title; @@ -59,7 +59,8 @@ class CustomTextField extends StatelessWidget { Text( title, style: context.textTheme.bodyMedium!.copyWith( - fontWeight: FontWeight.w600, + fontSize: 13, + fontWeight: FontWeight.w400, color: const Color(0xff000000), ), ), @@ -79,6 +80,7 @@ class CustomTextField extends StatelessWidget { style: const TextStyle(color: Colors.black), decoration: InputDecoration( hintText: hintText, + hintStyle: const TextStyle(fontSize: 12), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), border: InputBorder.none, diff --git a/lib/pages/common/text_field/custom_web_textfield.dart b/lib/pages/common/text_field/custom_web_textfield.dart index 6fe0dc49..926a20f5 100644 --- a/lib/pages/common/text_field/custom_web_textfield.dart +++ b/lib/pages/common/text_field/custom_web_textfield.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; class CustomWebTextField extends StatelessWidget { @@ -11,6 +12,7 @@ class CustomWebTextField extends StatelessWidget { this.description, this.validator, this.hintText, + this.height, }); final bool isRequired; @@ -19,6 +21,7 @@ class CustomWebTextField extends StatelessWidget { final TextEditingController? controller; final String? Function(String?)? validator; final String? hintText; + final double? height; @override Widget build(BuildContext context) { @@ -66,6 +69,7 @@ class CustomWebTextField extends StatelessWidget { height: 7, ), Container( + height: height ?? 35, decoration: containerDecoration .copyWith(color: const Color(0xFFF5F6F7), boxShadow: [ BoxShadow( @@ -80,9 +84,9 @@ class CustomWebTextField extends StatelessWidget { controller: controller, style: const TextStyle(color: Colors.black), decoration: textBoxDecoration()!.copyWith( - errorStyle: - const TextStyle(height: 0), // Hide the error text space - + errorStyle: const TextStyle(height: 0), + hintStyle: context.textTheme.titleSmall! + .copyWith(color: Colors.grey, fontSize: 12), hintText: hintText ?? 'Please enter'), ), ), diff --git a/lib/pages/device_managment/all_devices/view/device_managment_page.dart b/lib/pages/device_managment/all_devices/view/device_managment_page.dart index 04315651..93480ae5 100644 --- a/lib/pages/device_managment/all_devices/view/device_managment_page.dart +++ b/lib/pages/device_managment/all_devices/view/device_managment_page.dart @@ -20,9 +20,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout { style: Theme.of(context).textTheme.headlineLarge, ), ), - appBarBody: const [ - NavigateHomeGridView(), - ], + rightBody: const NavigateHomeGridView(), enableMenuSideba: isLargeScreenSize(context), scaffoldBody: BlocBuilder( builder: (context, state) { diff --git a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart index 9b5fcc8d..d1ee0fed 100644 --- a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart +++ b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart @@ -75,7 +75,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { const DeviceSearchFilters(), const SizedBox(height: 12), Container( - height: 43, + height: 35, width: 100, decoration: containerDecoration, child: Center( @@ -97,6 +97,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { child: Text( 'Control', style: TextStyle( + fontSize: 12, color: isControlButtonEnabled ? Colors.white : Colors.grey, diff --git a/lib/utils/theme/theme.dart b/lib/utils/theme/theme.dart new file mode 100644 index 00000000..ee868c8d --- /dev/null +++ b/lib/utils/theme/theme.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +final myTheme = ThemeData( + fontFamily: 'Aftika', + textTheme: const TextTheme( + bodySmall: TextStyle( + fontSize: 13, + color: ColorsManager.whiteColors, + fontWeight: FontWeight.bold), + bodyMedium: TextStyle(color: Colors.black87, fontSize: 14), + bodyLarge: TextStyle(fontSize: 16, color: Colors.white), + headlineSmall: TextStyle(color: Colors.black87, fontSize: 18), + headlineMedium: TextStyle(color: Colors.black87, fontSize: 20), + headlineLarge: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + colorScheme: ColorScheme.fromSeed( + seedColor: ColorsManager.blueColor, + primary: ColorsManager.blueColor, + onSurface: Colors.grey.shade400, + ), + switchTheme: SwitchThemeData( + thumbColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return ColorsManager.blueColor; + } + return ColorsManager.whiteColors; + }), + trackColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return ColorsManager.blueColor.withOpacity(0.5); + } + return ColorsManager.whiteColors; + }), + ), + checkboxTheme: CheckboxThemeData( + fillColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return ColorsManager.blueColor; + } + return Colors.grey.shade200; + }), + checkColor: WidgetStateProperty.all(Colors.white), + side: const BorderSide(color: ColorsManager.whiteColors), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + ), +); diff --git a/lib/web_layout/web_app_bar.dart b/lib/web_layout/web_app_bar.dart index 6ad54d0b..46b9c614 100644 --- a/lib/web_layout/web_app_bar.dart +++ b/lib/web_layout/web_app_bar.dart @@ -3,63 +3,125 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class WebAppBar extends StatelessWidget { +class WebAppBar extends StatelessWidget with HelperResponsiveLayout { final Widget? title; - final List? body; - const WebAppBar({super.key, this.title, this.body}); + final Widget? centerBody; + final Widget? rightBody; + + const WebAppBar({super.key, this.title, this.centerBody, this.rightBody}); @override Widget build(BuildContext context) { + bool isSmallScreen = isSmallScreenSize(context); + return BlocBuilder(builder: (context, state) { return Container( - height: 100, + height: isSmallScreen ? 130 : 100, decoration: const BoxDecoration(color: ColorsManager.secondaryColor), padding: const EdgeInsets.all(10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: title!, - ), - if (body != null) - Expanded( - flex: 2, - child: Wrap( - spacing: 15, // Adjust the spacing as needed - children: body!, - ), - ), - Row( - children: [ - const SizedBox( - width: 10, - ), - const SizedBox.square( - dimension: 40, - child: CircleAvatar( - backgroundColor: Colors.white, - child: SizedBox.square( - dimension: 35, - child: CircleAvatar( - backgroundColor: Colors.grey, - child: FlutterLogo(), + child: isSmallScreen + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (title != null) + Align( + alignment: Alignment.centerLeft, + child: title!, + ), + if (centerBody != null) + Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: centerBody, ), ), + if (rightBody != null || HomeBloc.user != null) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (rightBody != null) rightBody!, + Row( + children: [ + const SizedBox.square( + dimension: 40, + child: CircleAvatar( + backgroundColor: Colors.white, + child: SizedBox.square( + dimension: 35, + child: CircleAvatar( + backgroundColor: Colors.grey, + child: FlutterLogo(), + ), + ), + ), + ), + const SizedBox( + width: 10, + ), + if (HomeBloc.user != null) + Text( + '${HomeBloc.user!.firstName} ${HomeBloc.user!.lastName}', + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ], + ), + ], + ) + : Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Align( + alignment: Alignment.centerLeft, + child: title!, ), - ), - const SizedBox( - width: 10, - ), - if (HomeBloc.user != null) - Text( - '${HomeBloc.user!.firstName.toString()} ${HomeBloc.user!.lastName.toString()} ', - style: Theme.of(context).textTheme.bodyLarge, + if (centerBody != null) + Expanded( + child: Center( + child: centerBody, + ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (rightBody != null) + Align( + alignment: Alignment.centerRight, + child: rightBody, + ), + const SizedBox( + width: 10, + ), + const SizedBox.square( + dimension: 40, + child: CircleAvatar( + backgroundColor: Colors.white, + child: SizedBox.square( + dimension: 35, + child: CircleAvatar( + backgroundColor: Colors.grey, + child: FlutterLogo(), + ), + ), + ), + ), + const SizedBox( + width: 10, + ), + if (HomeBloc.user != null) + Text( + '${HomeBloc.user!.firstName} ${HomeBloc.user!.lastName}', + style: Theme.of(context).textTheme.bodyLarge, + ), + ], ), - ], - ) - ], - ), + ], + ), ); }); } diff --git a/lib/web_layout/web_scaffold.dart b/lib/web_layout/web_scaffold.dart index 72bcb777..b4edd764 100644 --- a/lib/web_layout/web_scaffold.dart +++ b/lib/web_layout/web_scaffold.dart @@ -8,14 +8,17 @@ import 'menu_sidebar.dart'; class WebScaffold extends StatelessWidget with HelperResponsiveLayout { final bool enableMenuSideba; final Widget? appBarTitle; - final List? appBarBody; + final Widget? centerBody; + final Widget? rightBody; final Widget? scaffoldBody; - const WebScaffold( - {super.key, - this.appBarTitle, - this.appBarBody, - this.scaffoldBody, - this.enableMenuSideba = true}); + const WebScaffold({ + super.key, + this.appBarTitle, + this.centerBody, + this.rightBody, + this.scaffoldBody, + this.enableMenuSideba = true, + }); @override Widget build(BuildContext context) { final isSmall = isSmallScreenSize(context); @@ -40,7 +43,8 @@ class WebScaffold extends StatelessWidget with HelperResponsiveLayout { opacity: 0.7, child: WebAppBar( title: appBarTitle, - body: appBarBody, + centerBody: centerBody, + rightBody: rightBody, )), Expanded( child: Row(