From a8132c37dbf29bc0661b06fb2bec4f31babf6acf Mon Sep 17 00:00:00 2001 From: ashrafzarkanisala Date: Thu, 13 Jun 2024 02:13:44 +0300 Subject: [PATCH] push scene devices body with blocs. need to drop the future builder - enhancment --- lib/features/devices/bloc/devices_cubit.dart | 17 +- lib/features/devices/bloc/devices_state.dart | 5 +- .../bloc/tab_change/tab_change_bloc.dart | 11 ++ .../bloc/tab_change/tab_change_event.dart | 8 + .../bloc/tab_change/tab_change_state.dart | 12 ++ .../scene/view/create_scene_view.dart | 1 + .../scene/view/scene_control_devices.dart | 180 ++++++++++++++++++ .../view/widgets/bottom_sheet_widget.dart | 5 +- .../scene/view/widgets/scene_list_tile.dart | 13 +- lib/navigation/router.dart | 11 ++ lib/navigation/routing_constants.dart | 1 + lib/utils/resource_manager/color_manager.dart | 2 +- 12 files changed, 253 insertions(+), 13 deletions(-) create mode 100644 lib/features/scene/bloc/tab_change/tab_change_bloc.dart create mode 100644 lib/features/scene/bloc/tab_change/tab_change_event.dart create mode 100644 lib/features/scene/bloc/tab_change/tab_change_state.dart create mode 100644 lib/features/scene/view/scene_control_devices.dart diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 48341df..426c363 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -291,8 +291,10 @@ class DevicesCubit extends Cubit { if (roomId == null) return; emitSafe(GetDevicesLoading()); - int roomIndex = - HomeCubit.getInstance().selectedSpace!.rooms!.indexWhere((element) => element.id == roomId); + int roomIndex = HomeCubit.getInstance() + .selectedSpace! + .rooms! + .indexWhere((element) => element.id == roomId); try { HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices = await DevicesAPI.getDevicesByRoomId(roomId); @@ -300,7 +302,9 @@ class DevicesCubit extends Cubit { emitSafe(GetDevicesError(e.toString())); return; } - emitSafe(GetDevicesSuccess()); + final devices = + HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices; + emitSafe(GetDevicesSuccess(devices)); //get status for each device //TODO get devices status per page via page controller instead of getting all devices status at once @@ -329,8 +333,11 @@ class DevicesCubit extends Cubit { emitSafe(GetDeviceStatusError(e.toString())); return; } - HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices![deviceIndex].status = - statuses; + HomeCubit.getInstance() + .selectedSpace! + .rooms![roomIndex] + .devices![deviceIndex] + .status = statuses; emitSafe(GetDeviceStatusSuccess(code: code)); } diff --git a/lib/features/devices/bloc/devices_state.dart b/lib/features/devices/bloc/devices_state.dart index e1a45d4..cb23e24 100644 --- a/lib/features/devices/bloc/devices_state.dart +++ b/lib/features/devices/bloc/devices_state.dart @@ -35,7 +35,10 @@ class GetDeviceStatusError extends DevicesState { class GetDevicesLoading extends DevicesState {} -class GetDevicesSuccess extends DevicesState {} +class GetDevicesSuccess extends DevicesState { + GetDevicesSuccess(this.devices); + final List? devices; +} class GetDevicesError extends DevicesState { final String errorMsg; diff --git a/lib/features/scene/bloc/tab_change/tab_change_bloc.dart b/lib/features/scene/bloc/tab_change/tab_change_bloc.dart new file mode 100644 index 0000000..18eb51b --- /dev/null +++ b/lib/features/scene/bloc/tab_change/tab_change_bloc.dart @@ -0,0 +1,11 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart'; +import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart'; + +class TabBarBloc extends Bloc { + TabBarBloc() : super(const Initial()) { + on((event, emit) { + emit(TabSelected(event.tabIndex)); + }); + } +} diff --git a/lib/features/scene/bloc/tab_change/tab_change_event.dart b/lib/features/scene/bloc/tab_change/tab_change_event.dart new file mode 100644 index 0000000..bd2af23 --- /dev/null +++ b/lib/features/scene/bloc/tab_change/tab_change_event.dart @@ -0,0 +1,8 @@ +abstract class TabBarEvent { + const TabBarEvent(); +} + +class TabChanged extends TabBarEvent { + final int tabIndex; + const TabChanged(this.tabIndex); +} diff --git a/lib/features/scene/bloc/tab_change/tab_change_state.dart b/lib/features/scene/bloc/tab_change/tab_change_state.dart new file mode 100644 index 0000000..46e1522 --- /dev/null +++ b/lib/features/scene/bloc/tab_change/tab_change_state.dart @@ -0,0 +1,12 @@ +abstract class TabBarState { + const TabBarState(); +} + +class Initial extends TabBarState { + const Initial(); +} + +class TabSelected extends TabBarState { + final int selectedTabIndex; + const TabSelected(this.selectedTabIndex); +} diff --git a/lib/features/scene/view/create_scene_view.dart b/lib/features/scene/view/create_scene_view.dart index 92e07b4..f349b1f 100644 --- a/lib/features/scene/view/create_scene_view.dart +++ b/lib/features/scene/view/create_scene_view.dart @@ -12,6 +12,7 @@ import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; class CreateSceneView extends StatelessWidget { const CreateSceneView({super.key}); + @override Widget build(BuildContext context) { return DefaultScaffold( diff --git a/lib/features/scene/view/scene_control_devices.dart b/lib/features/scene/view/scene_control_devices.dart new file mode 100644 index 0000000..f4556e1 --- /dev/null +++ b/lib/features/scene/view/scene_control_devices.dart @@ -0,0 +1,180 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/room_model.dart'; +import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart'; +import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart'; +import 'package:syncrow_app/features/scene/view/widgets/scene_list_tile.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; + +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; + +class SceneControlDevicesView extends StatefulWidget { + const SceneControlDevicesView({super.key}); + + @override + State createState() => + _SceneControlDevicesViewState(); +} + +class _SceneControlDevicesViewState extends State + with SingleTickerProviderStateMixin { + late final TabController _tabController; + List? rooms = []; + + @override + void initState() { + rooms = HomeCubit.getInstance().selectedSpace!.rooms!; + + _tabController = + TabController(length: rooms!.length, vsync: this, initialIndex: 0); + _tabController.addListener(_handleTabSelection); + super.initState(); + } + + void _handleTabSelection() { + if (_tabController.indexIsChanging) { + context.read().add(TabChanged(_tabController.index)); + } + } + + @override + void dispose() { + super.dispose(); + _tabController.dispose(); + _tabController.removeListener(() {}); + } + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: StringsManager.createScene, + padding: EdgeInsets.zero, + child: BlocProvider( + create: (context) => DevicesCubit.getInstance(), + child: FutureBuilder( + future: DevicesCubit.getInstance() + .fetchDevicesByRoomId(rooms![0].id!), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(), + ); + } else if (snapshot.hasError) { + return Text(snapshot.error.toString()); + } else { + return SceneDevicesBody( + tabController: _tabController, rooms: rooms); + } + })), + ); + } +} + +class SceneDevicesBody extends StatelessWidget { + const SceneDevicesBody({ + super.key, + required TabController tabController, + required this.rooms, + }) : _tabController = tabController; + + final TabController _tabController; + final List? rooms; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + TabBar( + controller: _tabController, + dividerColor: Colors.transparent, + indicatorColor: Colors.transparent, + tabs: List.generate(rooms!.length, (index) { + return Tab( + child: BodyLarge( + text: rooms![index].name ?? '', + style: context.bodyLarge.copyWith( + color: (state is TabSelected) && + state.selectedTabIndex == index + ? ColorsManager.textPrimaryColor + : ColorsManager.textPrimaryColor.withOpacity(0.2), + ), + ), + ); + }), + isScrollable: true, + onTap: (value) { + DevicesCubit.getInstance() + .fetchDevicesByRoomId(rooms![value].id!); + }, + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: rooms! + .map((e) => BlocBuilder( + builder: (context, state) { + if (state is GetDevicesLoading) { + return const Center( + child: CircularProgressIndicator()); + } else if (state is GetDevicesSuccess) { + return ListView.builder( + itemCount: state.devices!.length, + itemBuilder: (context, index) { + final device = state.devices![index]; + return DefaultContainer( + child: SceneListTile( + minLeadingWidth: 40, + leadingWidget: Image.network( + device.icon ?? '', + errorBuilder: + (context, error, stackTrace) => + Image.asset( + Assets.assetsIconsLogo, + width: 20, + ), + ), + titleWidget: BodyMedium( + text: device.name ?? '', + style: context.titleSmall.copyWith( + color: + ColorsManager.secondaryTextColor, + fontWeight: FontWeight.w400, + fontSize: 20, + ), + ), + trailingWidget: const Icon( + Icons.arrow_forward_ios_rounded, + size: 16, + weight: 0.2, + ), + ), + ); + }, + ); + } else if (state is GetDevicesError) { + return Center(child: Text(state.errorMsg)); + } + return const SizedBox(); + }, + )) + .toList(), + ), + ), + ], + ); + }, + ); + } +} diff --git a/lib/features/scene/view/widgets/bottom_sheet_widget.dart b/lib/features/scene/view/widgets/bottom_sheet_widget.dart index a7ac4ef..66c539e 100644 --- a/lib/features/scene/view/widgets/bottom_sheet_widget.dart +++ b/lib/features/scene/view/widgets/bottom_sheet_widget.dart @@ -5,6 +5,7 @@ import 'package:syncrow_app/features/shared_widgets/light_divider.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; @@ -46,7 +47,9 @@ class CustomBottomSheetWidget extends StatelessWidget { size: 16, color: ColorsManager.greyColor, ), - onPressed: () {}, + onPressed: () { + Navigator.pushNamed(context, Routes.sceneControlDevicesRoute); + }, ), SceneListTile( assetPath: Assets.player, diff --git a/lib/features/scene/view/widgets/scene_list_tile.dart b/lib/features/scene/view/widgets/scene_list_tile.dart index 78e4c4d..c667cf0 100644 --- a/lib/features/scene/view/widgets/scene_list_tile.dart +++ b/lib/features/scene/view/widgets/scene_list_tile.dart @@ -19,6 +19,7 @@ class SceneListTile extends StatelessWidget { this.onPressed, this.assetHeight, this.minLeadingWidth, + this.titleWidget, }); final String? assetPath; final String? titleString; @@ -30,6 +31,7 @@ class SceneListTile extends StatelessWidget { final void Function()? onPressed; final double? assetHeight; final double? minLeadingWidth; + final Widget? titleWidget; @override Widget build(BuildContext context) { @@ -46,11 +48,12 @@ class SceneListTile extends StatelessWidget { : null), trailing: trailingWidget, contentPadding: padding, - title: BodyMedium( - text: titleString ?? '', - textAlign: textAlign, - style: context.bodyMedium.copyWith(fontSize: 15), - ), + title: titleWidget ?? + BodyMedium( + text: titleString ?? '', + textAlign: textAlign, + style: context.bodyMedium.copyWith(fontSize: 15), + ), subtitle: subtitle == null ? null : BodySmall( diff --git a/lib/navigation/router.dart b/lib/navigation/router.dart index 2cbb1b3..898b316 100644 --- a/lib/navigation/router.dart +++ b/lib/navigation/router.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/app_layout/view/app_layout.dart'; import 'package:syncrow_app/features/auth/view/otp_view.dart'; import 'package:syncrow_app/features/auth/view/login_view.dart'; @@ -8,7 +9,10 @@ import 'package:syncrow_app/features/layout/view/layout_view.dart'; import 'package:syncrow_app/features/menu/view/menu_view.dart'; import 'package:syncrow_app/features/menu/view/widgets/create_home/create_home_view.dart'; import 'package:syncrow_app/features/menu/view/widgets/profile/profile_view.dart'; +import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart'; import 'package:syncrow_app/features/scene/view/scene_add_tasks.dart'; +import 'package:syncrow_app/features/scene/view/scene_control_devices.dart'; import 'package:syncrow_app/features/scene/view/scene_view.dart'; import 'package:syncrow_app/features/splash/view/splash_view.dart'; import 'routing_constants.dart'; @@ -66,6 +70,13 @@ class Router { case Routes.sceneTasksRoute: return MaterialPageRoute( builder: (_) => const SceneAddTasksView(), settings: settings); + case Routes.sceneControlDevicesRoute: + return MaterialPageRoute( + builder: (_) => BlocProvider( + create: (context) => TabBarBloc()..add(const TabChanged(0)), + child: const SceneControlDevicesView(), + ), + settings: settings); default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/navigation/routing_constants.dart b/lib/navigation/routing_constants.dart index f4ec47e..3726c16 100644 --- a/lib/navigation/routing_constants.dart +++ b/lib/navigation/routing_constants.dart @@ -18,4 +18,5 @@ class Routes { static const String otpRoute = '/otp'; static const String createUnit = '/create-unit'; static const String sceneTasksRoute = '/scene-tasks'; + static const String sceneControlDevicesRoute = '/scene-control-devices'; } diff --git a/lib/utils/resource_manager/color_manager.dart b/lib/utils/resource_manager/color_manager.dart index 4833ab4..2abd9fc 100644 --- a/lib/utils/resource_manager/color_manager.dart +++ b/lib/utils/resource_manager/color_manager.dart @@ -6,7 +6,7 @@ abstract class ColorsManager { static const Color primaryColor = Color(0xFF0030CB); static Color primaryColorWithOpacity = const Color(0xFF023DFE).withOpacity(0.6); - + static const Color secondaryTextColor = Color(0xFF848484); static const Color onPrimaryColor = Colors.white; static const Color secondaryColor = Color(0xFF023DFE); static const Color onSecondaryColor = Color(0xFF023DFE);