diff --git a/assets/icons/AC.svg b/assets/icons/AC.svg new file mode 100644 index 0000000..92f6fc5 --- /dev/null +++ b/assets/icons/AC.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/Curtain.svg b/assets/icons/Curtain.svg new file mode 100644 index 0000000..a2e4f23 --- /dev/null +++ b/assets/icons/Curtain.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/icons/Door Lock.svg b/assets/icons/Door Lock.svg new file mode 100644 index 0000000..6f27673 --- /dev/null +++ b/assets/icons/Door Lock.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/Gateway.svg b/assets/icons/Gateway.svg new file mode 100644 index 0000000..e293999 --- /dev/null +++ b/assets/icons/Gateway.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/Light.svg b/assets/icons/Light.svg new file mode 100644 index 0000000..c8cfff5 --- /dev/null +++ b/assets/icons/Light.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/icons/Screen.svg b/assets/icons/Screen.svg new file mode 100644 index 0000000..cc0cd8f --- /dev/null +++ b/assets/icons/Screen.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 8062933..abc23cb 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -1,25 +1,99 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/utils/resource_manager/assets_manager.dart'; -import '../model/device_model.dart'; +import '../model/device_category_model.dart'; part 'devices_state.dart'; class DevicesCubit extends Cubit { DevicesCubit() : super(DevicesInitial()) { - getDevices(); + getCategories(); + } + + bool ACSwitchValue = false; + bool lightsSwitchValue = false; + bool doorSwitchValue = false; + bool curtainSwitchValue = false; + bool screensSwitchValue = false; + bool gatewaySwitchValue = false; + + void changeSwitchValue(DeviceType device) { + switch (device) { + case DeviceType.AC: + ACSwitchValue = !ACSwitchValue; + break; + case DeviceType.Lights: + lightsSwitchValue = !lightsSwitchValue; + break; + case DeviceType.Door: + doorSwitchValue = !doorSwitchValue; + break; + case DeviceType.Curtain: + curtainSwitchValue = !curtainSwitchValue; + break; + case DeviceType.Screens: + screensSwitchValue = !screensSwitchValue; + break; + case DeviceType.Gateway: + gatewaySwitchValue = !gatewaySwitchValue; + break; + } + emit(DevicesSuccess()); } - //TODO separate the navigation logic to another cubit static DevicesCubit get(context) => BlocProvider.of(context); - var devices = []; + var categories = []; - Future> getDevices() async { + Future> getCategories() async { emit(DevicesLoading()); await Future.delayed(const Duration(seconds: 2)); emit(DevicesSuccess()); - return devices = []; + return categories = [ + DevicesCategoryModel( + devices: [], + icon: IconsManager.ac, + name: 'ACs', + switchValue: false, + type: DeviceType.AC, + ), + DevicesCategoryModel( + devices: [], + icon: IconsManager.light, + name: 'Lights', + switchValue: false, + type: DeviceType.Lights, + ), + DevicesCategoryModel( + devices: [], + icon: IconsManager.doorLock, + name: 'Doors', + switchValue: false, + type: DeviceType.Door, + ), + DevicesCategoryModel( + devices: [], + icon: IconsManager.curtain, + name: 'Curtains', + switchValue: false, + type: DeviceType.Curtain, + ), + DevicesCategoryModel( + devices: [], + icon: IconsManager.screen, + name: 'Screens', + switchValue: false, + type: DeviceType.Screens, + ), + DevicesCategoryModel( + devices: [], + icon: IconsManager.gateway, + name: 'Gateway', + switchValue: false, + type: DeviceType.Gateway, + ), + ]; } } diff --git a/lib/features/devices/model/device_category_model.dart b/lib/features/devices/model/device_category_model.dart new file mode 100644 index 0000000..6197f26 --- /dev/null +++ b/lib/features/devices/model/device_category_model.dart @@ -0,0 +1,27 @@ +import 'device_model.dart'; + +class DevicesCategoryModel { + final String name; + final String icon; + + final bool switchValue; + final List devices; + + final DeviceType type; + + DevicesCategoryModel( + {required this.type, + required this.name, + required this.icon, + required this.switchValue, + required this.devices}); +} + +enum DeviceType { + AC, + Lights, + Door, + Curtain, + Screens, + Gateway, +} diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 75dba0d..37ee98e 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -1 +1,14 @@ -class DeviceModel {} +class DeviceModel { + final String name; + final String imageUrl; + final String description; + final String category; + final String id; + + DeviceModel( + {required this.name, + required this.imageUrl, + required this.description, + required this.category, + required this.id}); +} diff --git a/lib/features/devices/view/devices_view.dart b/lib/features/devices/view/devices_view.dart index e46862d..00f315d 100644 --- a/lib/features/devices/view/devices_view.dart +++ b/lib/features/devices/view/devices_view.dart @@ -3,8 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart'; -class DevicesView extends StatelessWidget { - const DevicesView({super.key}); +class CategoriesView extends StatelessWidget { + const CategoriesView({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/features/devices/view/widgets/categories_view.dart b/lib/features/devices/view/widgets/categories_view.dart new file mode 100644 index 0000000..4a21ee9 --- /dev/null +++ b/lib/features/devices/view/widgets/categories_view.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/devices/view/widgets/devices_mode_tab.dart'; +import 'package:syncrow_app/features/devices/view/widgets/switches.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; + +class CategoriesView extends StatelessWidget { + const CategoriesView({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return const Padding( + padding: EdgeInsets.only(top: 60, right: 15, left: 15, bottom: 100), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TitleMedium( + text: StringsManager.devices, + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + ), + ), + DevicesModeTab(), + ], + ), + ), + Expanded( + flex: 3, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TitleMedium( + text: StringsManager.wizard, + style: TextStyle( + fontSize: 28, + ), + ), + Switches(), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/devices_mode_tab.dart b/lib/features/devices/view/widgets/devices_mode_tab.dart new file mode 100644 index 0000000..77c1145 --- /dev/null +++ b/lib/features/devices/view/widgets/devices_mode_tab.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:gap/gap.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/resource_manager/assets_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; + +class DevicesModeTab extends StatelessWidget { + const DevicesModeTab({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.all(10), + margin: const EdgeInsets.only(right: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 30, + width: 25, + child: SvgPicture.asset( + IconsManager.winter, + fit: BoxFit.contain, + ), + ), + const Gap(5), + const BodySmall( + text: StringsManager.winter, + fontWeight: FontWeight.bold, + fontColor: Colors.grey, + ), + ], + ), + ), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.all(10), + margin: const EdgeInsets.only(right: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 30, + width: 25, + child: SvgPicture.asset( + IconsManager.summer, + fit: BoxFit.contain, + ), + ), + const Gap(5), + const BodySmall( + text: StringsManager.summer, + fontWeight: FontWeight.bold, + fontColor: Colors.grey, + ), + ], + ), + ), + const Expanded(child: SizedBox.shrink()) + ], + ); + } +} diff --git a/lib/features/devices/view/widgets/devices_view_body.dart b/lib/features/devices/view/widgets/devices_view_body.dart index 06fb86e..7f41859 100644 --- a/lib/features/devices/view/widgets/devices_view_body.dart +++ b/lib/features/devices/view/widgets/devices_view_body.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/view/widgets/categories_view.dart'; import '../../bloc/devices_cubit.dart'; import 'no_devices_view.dart'; @@ -15,9 +16,13 @@ class DevicesViewBody extends StatelessWidget { create: (context) => DevicesCubit(), child: BlocBuilder( builder: (context, state) { - return DevicesCubit.get(context).devices.isEmpty - ? const NoDevicesView() - : const SizedBox(); + return state is DevicesLoading + ? const Center(child: CircularProgressIndicator()) + : state is DevicesSuccess + ? DevicesCubit.get(context).categories.isEmpty + ? const NoDevicesView() + : const CategoriesView() + : const Center(child: Text('Error')); }, )); } diff --git a/lib/features/devices/view/widgets/switches.dart b/lib/features/devices/view/widgets/switches.dart new file mode 100644 index 0000000..08bb3df --- /dev/null +++ b/lib/features/devices/view/widgets/switches.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/shared_widgets/custom_switch.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; + +import '../../bloc/devices_cubit.dart'; + +class Switches extends StatelessWidget { + const Switches({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + childAspectRatio: 1.5, + ), + padding: const EdgeInsets.only(top: 10), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: DevicesCubit.get(context).categories.length, + itemBuilder: (context, index) { + return DefaultContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SvgPicture.asset( + DevicesCubit.get(context).categories[index].icon, + fit: BoxFit.contain, + ), + SizedBox( + width: 30, + height: 20, + child: CustomSwitch( + value: DevicesCubit.get(context) + .categories[index] + .switchValue, + onChanged: (value) => DevicesCubit.get(context) + .changeSwitchValue( + DevicesCubit.get(context).categories[index].type), + ), + ), + ], + ), + BodyLarge( + text: DevicesCubit.get(context).categories[index].name, + fontWeight: FontWeight.bold, + ) + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/shared_widgets/custom_switch.dart b/lib/features/shared_widgets/custom_switch.dart new file mode 100644 index 0000000..d7bba55 --- /dev/null +++ b/lib/features/shared_widgets/custom_switch.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class CustomSwitch extends StatefulWidget { + final bool value; + final ValueChanged onChanged; + + const CustomSwitch({super.key, required this.value, required this.onChanged}); + + @override + _CustomSwitchState createState() => _CustomSwitchState(); +} + +class _CustomSwitchState extends State + with SingleTickerProviderStateMixin { + Animation? _circleAnimation; + AnimationController? _animationController; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + vsync: this, duration: const Duration(milliseconds: 100)); + _circleAnimation = AlignmentTween( + begin: widget.value ? Alignment.centerRight : Alignment.centerLeft, + end: widget.value ? Alignment.centerLeft : Alignment.centerRight) + .animate(CurvedAnimation( + parent: _animationController!, curve: Curves.linear)); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _animationController!, + builder: (context, child) { + return GestureDetector( + onTap: () { + _animationController!.isCompleted + ? _animationController!.reverse() + : _animationController!.forward(); + widget.value == false + ? widget.onChanged(true) + : widget.onChanged(false); + }, + child: Container( + width: 45.0, + height: 28.0, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24.0), + color: _circleAnimation!.value == Alignment.centerLeft + ? Colors.grey + : ColorsManager.primaryColor, + ), + child: Padding( + padding: const EdgeInsets.all(2.0), + child: Container( + alignment: widget.value + ? ((Directionality.of(context) == TextDirection.rtl) + ? Alignment.centerRight + : Alignment.centerLeft) + : ((Directionality.of(context) == TextDirection.rtl) + ? Alignment.centerLeft + : Alignment.centerRight), + child: Container( + width: 20.0, + height: 20.0, + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Colors.white), + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/features/shared_widgets/default_container.dart b/lib/features/shared_widgets/default_container.dart new file mode 100644 index 0000000..53b3ee0 --- /dev/null +++ b/lib/features/shared_widgets/default_container.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class DefaultContainer extends StatelessWidget { + const DefaultContainer({ + super.key, + required this.child, + }); + + final Widget child; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.all(10), + child: child, + ); + } +} diff --git a/lib/features/shared_widgets/text_widgets/body_large.dart b/lib/features/shared_widgets/text_widgets/body_large.dart index 471dbbb..18a1447 100644 --- a/lib/features/shared_widgets/text_widgets/body_large.dart +++ b/lib/features/shared_widgets/text_widgets/body_large.dart @@ -10,6 +10,7 @@ class BodyLarge extends StatelessWidget { this.textAlign, this.style, this.height, + this.fontWeight, }); final String text; @@ -19,10 +20,13 @@ class BodyLarge extends StatelessWidget { final double? height; + final FontWeight? fontWeight; + @override Widget build(BuildContext context) => CustomText( text, style: style ?? context.bodyLarge.copyWith(height: height ?? 1.5), textAlign: textAlign, + fontWeight: fontWeight, ); } diff --git a/lib/features/shared_widgets/text_widgets/body_small.dart b/lib/features/shared_widgets/text_widgets/body_small.dart index 2cf842d..f784dc7 100644 --- a/lib/features/shared_widgets/text_widgets/body_small.dart +++ b/lib/features/shared_widgets/text_widgets/body_small.dart @@ -9,6 +9,7 @@ class BodySmall extends StatelessWidget { this.style, this.fontColor, this.fontSize, + this.fontWeight, }); final String text; @@ -17,6 +18,7 @@ class BodySmall extends StatelessWidget { final Color? fontColor; final double? fontSize; + final FontWeight? fontWeight; @override Widget build(BuildContext context) => CustomText( @@ -24,5 +26,6 @@ class BodySmall extends StatelessWidget { style: style ?? context.bodySmall, fontColor: fontColor, fontSize: fontSize, + fontWeight: fontWeight, ); } diff --git a/lib/features/shared_widgets/text_widgets/custom_text_widget.dart b/lib/features/shared_widgets/text_widgets/custom_text_widget.dart index 2d886f0..c0b7c5b 100644 --- a/lib/features/shared_widgets/text_widgets/custom_text_widget.dart +++ b/lib/features/shared_widgets/text_widgets/custom_text_widget.dart @@ -11,7 +11,8 @@ class CustomText extends StatelessWidget { this.maxLines, this.textDirection, this.fontSize, - this.fontColor}); + this.fontColor, + this.fontWeight}); final String text; final TextStyle? style; @@ -24,14 +25,16 @@ class CustomText extends StatelessWidget { final double? fontSize; final Color? fontColor; + final FontWeight? fontWeight; + @override Widget build(BuildContext context) { return SelectableText( text, style: style!.copyWith( - fontSize: fontSize, - color: fontColor ?? ColorsManager.textPrimaryColor, - ), + fontSize: fontSize, + color: fontColor ?? ColorsManager.textPrimaryColor, + fontWeight: fontWeight), textAlign: textAlign, onTap: onTap, minLines: minLines, diff --git a/lib/navigation/router.dart b/lib/navigation/router.dart index 5a13864..0836b18 100644 --- a/lib/navigation/router.dart +++ b/lib/navigation/router.dart @@ -19,7 +19,7 @@ class Router { case Routes.devicesRoute: return MaterialPageRoute( - builder: (_) => const DevicesView(), settings: settings); + builder: (_) => const CategoriesView(), settings: settings); case Routes.profileRoute: return MaterialPageRoute( diff --git a/lib/utils/resource_manager/assets_manager.dart b/lib/utils/resource_manager/assets_manager.dart index 0b469b4..217d4ed 100644 --- a/lib/utils/resource_manager/assets_manager.dart +++ b/lib/utils/resource_manager/assets_manager.dart @@ -30,12 +30,18 @@ abstract class IconsManager { static const String layout = '$base/Layout.svg'; static const String layoutFill = '$base/Layout-fill.svg'; static const String frequency = '$base/frequency.svg'; - static const String winter = '$base/winter.svg'; + static const String winter = '$base/Winter.svg'; static const String active = '$base/active.svg'; static const String voltMeter = '$base/volt-meter.svg'; static const String summer = '$base/Summer.svg'; static const String CO2 = '$base/CO2.svg'; static const String sustainability = '$base/sustainability.svg'; + static const String ac = '$base/AC.svg'; + static const String curtain = '$base/Curtain.svg'; + static const String doorLock = '$base/Door Lock.svg'; + static const String gateway = '$base/Gateway.svg'; + static const String light = '$base/Light.svg'; + static const String screen = '$base/Screen.svg'; } abstract class VideosManager { diff --git a/lib/utils/resource_manager/strings_manager.dart b/lib/utils/resource_manager/strings_manager.dart index 31dd807..539a924 100644 --- a/lib/utils/resource_manager/strings_manager.dart +++ b/lib/utils/resource_manager/strings_manager.dart @@ -5,6 +5,8 @@ class StringsManager { static const noInternetConnection = 'No internet connection'; static const dashboard = 'Dashboard'; + static const devices = 'Devices'; + static const wizard = 'Wizard'; static const active = 'Active'; static const current = 'Current'; static const frequency = 'Frequency'; @@ -14,4 +16,6 @@ class StringsManager { static const units = 'Units'; static const emissions = 'Emissions'; static const reductions = 'Reductions'; + static const winter = 'Winter'; + static const summer = 'Summer'; }