refactor: consolidate device loading and error handling in SceneDevicesBody and implement SceneDevicesList widget

This commit is contained in:
Faris Armoush
2025-04-16 15:46:56 +03:00
parent a2e68d6194
commit 9eff9ab371
2 changed files with 93 additions and 49 deletions

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_state.dart'; import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_state.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart'; import 'package:syncrow_app/features/devices/model/subspace_model.dart';
@ -8,20 +7,17 @@ import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart'
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart'; import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart'; import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; import 'package:syncrow_app/features/scene/widgets/scene_devices/scene_devices_list.dart';
import 'package:syncrow_app/features/shared_widgets/app_loading_indicator.dart'; import 'package:syncrow_app/features/shared_widgets/app_loading_indicator.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.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/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/context_extension.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/color_manager.dart';
class SceneDevicesBody extends StatelessWidget { class SceneDevicesBody extends StatelessWidget {
const SceneDevicesBody({ const SceneDevicesBody({
super.key,
required this.tabController, required this.tabController,
required this.rooms, required this.rooms,
super.key,
}); });
final TabController tabController; final TabController tabController;
@ -44,6 +40,8 @@ class SceneDevicesBody extends StatelessWidget {
controller: tabController, controller: tabController,
dividerColor: Colors.transparent, dividerColor: Colors.transparent,
indicatorColor: Colors.transparent, indicatorColor: Colors.transparent,
isScrollable: true,
tabAlignment: TabAlignment.start,
tabs: rooms.map((e) { tabs: rooms.map((e) {
final isStateTabSelected = state is TabSelected; final isStateTabSelected = state is TabSelected;
final isSelected = isStateTabSelected && state.roomId == e.id; final isSelected = isStateTabSelected && state.roomId == e.id;
@ -61,8 +59,6 @@ class SceneDevicesBody extends StatelessWidget {
), ),
); );
}).toList(), }).toList(),
isScrollable: true,
tabAlignment: TabAlignment.start,
), ),
Expanded( Expanded(
child: TabBarView( child: TabBarView(
@ -86,47 +82,29 @@ class SceneDevicesBody extends StatelessWidget {
) { ) {
return BlocBuilder<DeviceManagerBloc, DeviceManagerState>( return BlocBuilder<DeviceManagerBloc, DeviceManagerState>(
builder: (context, state) { builder: (context, state) {
if (state.loading && state.devices == null) { final isLoading = state.loading && state.devices == null;
return const AppLoadingIndicator(); final hasData =
} else if (state.devices != null && state.devices!.isNotEmpty) { state.devices != null && (state.devices?.isNotEmpty ?? false);
return ListView.builder( final hasError = state.error != null;
itemCount: state.devices!.length,
itemBuilder: (context, index) { final widgets = <bool, Widget>{
final device = state.devices?[index]; isLoading: const AppLoadingIndicator(),
return DefaultContainer( hasError: Center(child: Text('${state.error}')),
child: SceneListTile( hasData: SceneDevicesList(
minLeadingWidth: 40, devices: state.devices ?? [],
leadingWidget: SvgPicture.asset(device?.icon ?? ''), isAutomationDeviceStatus: isAutomationDeviceStatus,
titleWidget: BodyMedium( ),
text: device?.name ?? '', };
style: context.titleSmall.copyWith(
color: ColorsManager.secondaryTextColor, final validWidgetEntry = widgets.entries.firstWhere(
fontWeight: FontWeight.w400, (entry) => entry.key == true,
fontSize: 20, orElse: () => MapEntry(
), true,
), Center(child: Text('This subspace has no devices')),
trailingWidget: const Icon( ),
Icons.arrow_forward_ios_rounded, );
color: ColorsManager.greyColor,
size: 16, return validWidgetEntry.value;
),
onPressed: () => Navigator.pushNamed(
context,
Routes.deviceFunctionsRoute,
arguments: {
"device": device,
"isAutomationDeviceStatus": isAutomationDeviceStatus
},
),
),
);
},
);
} else if (state.error != null) {
return const Center(child: Text('Something went wrong'));
} else {
return const SizedBox();
}
}, },
); );
} }

View File

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.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';
class SceneDevicesList extends StatelessWidget {
const SceneDevicesList({
required this.isAutomationDeviceStatus,
required this.devices,
super.key,
});
final List<DeviceModel> devices;
final bool isAutomationDeviceStatus;
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: devices.length,
itemBuilder: (context, index) => _buildDeviceTile(context, devices[index]),
);
}
Widget _buildDeviceTile(
BuildContext context,
DeviceModel device,
) {
return DefaultContainer(
child: SceneListTile(
minLeadingWidth: 40,
leadingWidget: SvgPicture.asset(device.icon ?? ''),
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,
color: ColorsManager.greyColor,
size: 16,
),
onPressed: () => _navigateToDeviceFunctions(context, device),
),
);
}
void _navigateToDeviceFunctions(
BuildContext context,
DeviceModel device,
) {
Navigator.of(context).pushNamed(
Routes.deviceFunctionsRoute,
arguments: {
"device": device,
"isAutomationDeviceStatus": isAutomationDeviceStatus,
},
);
}
}