mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
Fixed design issues
This commit is contained in:
@ -53,11 +53,26 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
@override
|
||||
void didUpdateWidget(DynamicTable oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.data.length != widget.data.length) {
|
||||
if (!_compareListOfLists(oldWidget.data, widget.data)) {
|
||||
_initializeSelection();
|
||||
}
|
||||
}
|
||||
|
||||
bool _compareListOfLists(List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
||||
// Check if the old and new lists are the same
|
||||
if (oldList.length != newList.length) return false;
|
||||
|
||||
for (int i = 0; i < oldList.length; i++) {
|
||||
if (oldList[i].length != newList[i].length) return false;
|
||||
|
||||
for (int j = 0; j < oldList[i].length; j++) {
|
||||
if (oldList[i][j] != newList[i][j]) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _initializeSelection() {
|
||||
_selectedRows = List<bool>.filled(widget.data.length, false);
|
||||
_selectAll = false;
|
||||
@ -90,13 +105,11 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: widget.headerDecoration ??
|
||||
BoxDecoration(color: Colors.grey[200]),
|
||||
decoration: widget.headerDecoration ?? BoxDecoration(color: Colors.grey[200]),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
||||
...widget.headers
|
||||
.map((header) => _buildTableHeaderCell(header)),
|
||||
...widget.headers.map((header) => _buildTableHeaderCell(header)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -123,8 +136,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color: ColorsManager.grayColor),
|
||||
.copyWith(color: ColorsManager.grayColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -144,11 +156,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
return Row(
|
||||
children: [
|
||||
if (widget.withCheckBox)
|
||||
_buildRowCheckbox(
|
||||
index, widget.size.height * 0.10),
|
||||
...row.map((cell) => _buildTableCell(
|
||||
cell.toString(),
|
||||
widget.size.height * 0.10)),
|
||||
_buildRowCheckbox(index, widget.size.height * 0.10),
|
||||
...row.map((cell) =>
|
||||
_buildTableCell(cell.toString(), widget.size.height * 0.10)),
|
||||
],
|
||||
);
|
||||
},
|
||||
@ -173,9 +183,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
),
|
||||
child: Checkbox(
|
||||
value: _selectAll,
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
||||
? _toggleSelectAll
|
||||
: null,
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty ? _toggleSelectAll : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -2,20 +2,21 @@ import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class StatefulTextField extends StatefulWidget {
|
||||
const StatefulTextField({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.hintText = 'Please enter',
|
||||
required this.width,
|
||||
this.elevation = 0,
|
||||
required this.controller,
|
||||
});
|
||||
const StatefulTextField(
|
||||
{super.key,
|
||||
required this.title,
|
||||
this.hintText = 'Please enter',
|
||||
required this.width,
|
||||
this.elevation = 0,
|
||||
required this.controller,
|
||||
this.onSubmitted});
|
||||
|
||||
final String title;
|
||||
final String hintText;
|
||||
final double width;
|
||||
final double elevation;
|
||||
final TextEditingController controller;
|
||||
final Function? onSubmitted;
|
||||
|
||||
@override
|
||||
State<StatefulTextField> createState() => _StatefulTextFieldState();
|
||||
@ -25,30 +26,31 @@ class _StatefulTextFieldState extends State<StatefulTextField> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomTextField(
|
||||
title: widget.title,
|
||||
controller: widget.controller,
|
||||
hintText: widget.hintText,
|
||||
width: widget.width,
|
||||
elevation: widget.elevation,
|
||||
);
|
||||
title: widget.title,
|
||||
controller: widget.controller,
|
||||
hintText: widget.hintText,
|
||||
width: widget.width,
|
||||
elevation: widget.elevation,
|
||||
onSubmittedFun: widget.onSubmitted);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomTextField extends StatelessWidget {
|
||||
const CustomTextField({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.controller,
|
||||
this.hintText = 'Please enter',
|
||||
required this.width,
|
||||
this.elevation = 0,
|
||||
});
|
||||
const CustomTextField(
|
||||
{super.key,
|
||||
required this.title,
|
||||
required this.controller,
|
||||
this.hintText = 'Please enter',
|
||||
required this.width,
|
||||
this.elevation = 0,
|
||||
this.onSubmittedFun});
|
||||
|
||||
final String title;
|
||||
final TextEditingController controller;
|
||||
final String hintText;
|
||||
final double width;
|
||||
final double elevation;
|
||||
final Function? onSubmittedFun;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -81,10 +83,12 @@ class CustomTextField extends StatelessWidget {
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
hintStyle: const TextStyle(fontSize: 12),
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
onFieldSubmitted: (_) {
|
||||
onSubmittedFun!();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -37,8 +37,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
offlineCount = state.offlineCount;
|
||||
lowBatteryCount = state.lowBatteryCount;
|
||||
isControlButtonEnabled = state.isControlButtonEnabled;
|
||||
selectedDevices = state.selectedDevice ??
|
||||
context.read<DeviceManagementBloc>().selectedDevices;
|
||||
selectedDevices =
|
||||
state.selectedDevice ?? context.read<DeviceManagementBloc>().selectedDevices;
|
||||
} else if (state is DeviceManagementFiltered) {
|
||||
devicesToShow = state.filteredDevices;
|
||||
selectedIndex = state.selectedIndex;
|
||||
@ -46,8 +46,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
offlineCount = state.offlineCount;
|
||||
lowBatteryCount = state.lowBatteryCount;
|
||||
isControlButtonEnabled = state.isControlButtonEnabled;
|
||||
selectedDevices = state.selectedDevice ??
|
||||
context.read<DeviceManagementBloc>().selectedDevices;
|
||||
selectedDevices =
|
||||
state.selectedDevice ?? context.read<DeviceManagementBloc>().selectedDevices;
|
||||
} else if (state is DeviceManagementInitial) {
|
||||
devicesToShow = [];
|
||||
selectedIndex = 0;
|
||||
@ -61,15 +61,13 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
'Low Battery ($lowBatteryCount)',
|
||||
];
|
||||
|
||||
final buttonLabel =
|
||||
(selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||
final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: isLargeScreenSize(context)
|
||||
? const EdgeInsets.all(30)
|
||||
: const EdgeInsets.all(15),
|
||||
padding:
|
||||
isLargeScreenSize(context) ? const EdgeInsets.all(30) : const EdgeInsets.all(15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -78,9 +76,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
tabs: tabs,
|
||||
selectedIndex: selectedIndex,
|
||||
onTabChanged: (index) {
|
||||
context
|
||||
.read<DeviceManagementBloc>()
|
||||
.add(SelectedFilterChanged(index));
|
||||
context.read<DeviceManagementBloc>().add(SelectedFilterChanged(index));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
@ -102,14 +98,12 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
),
|
||||
);
|
||||
} else if (selectedDevices.length > 1) {
|
||||
final productTypes = selectedDevices
|
||||
.map((device) => device.productType)
|
||||
.toSet();
|
||||
final productTypes =
|
||||
selectedDevices.map((device) => device.productType).toSet();
|
||||
if (productTypes.length == 1) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
DeviceBatchControlDialog(
|
||||
builder: (context) => DeviceBatchControlDialog(
|
||||
devices: selectedDevices,
|
||||
),
|
||||
);
|
||||
@ -123,9 +117,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isControlButtonEnabled
|
||||
? Colors.white
|
||||
: Colors.grey,
|
||||
color: isControlButtonEnabled ? Colors.white : Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -144,9 +136,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
cellDecoration: containerDecoration,
|
||||
onRowSelected: (index, isSelected, row) {
|
||||
final selectedDevice = devicesToShow[index];
|
||||
context
|
||||
.read<DeviceManagementBloc>()
|
||||
.add(SelectDevice(selectedDevice));
|
||||
context.read<DeviceManagementBloc>().add(SelectDevice(selectedDevice));
|
||||
},
|
||||
withCheckBox: true,
|
||||
size: context.screenSize,
|
||||
@ -169,20 +159,16 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
device.uuid ?? '',
|
||||
device.unit?.name ?? '',
|
||||
device.room?.name ?? '',
|
||||
device.batteryLevel != null
|
||||
? '${device.batteryLevel}%'
|
||||
: '-',
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.createTime ?? 0) * 1000)),
|
||||
device.batteryLevel != null ? '${device.batteryLevel}%' : '-',
|
||||
formatDateTime(
|
||||
DateTime.fromMillisecondsSinceEpoch((device.createTime ?? 0) * 1000)),
|
||||
device.online == true ? 'Online' : 'Offline',
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.updateTime ?? 0) * 1000)),
|
||||
formatDateTime(
|
||||
DateTime.fromMillisecondsSinceEpoch((device.updateTime ?? 0) * 1000)),
|
||||
];
|
||||
}).toList(),
|
||||
onSelectionChanged: (selectedRows) {
|
||||
context
|
||||
.read<DeviceManagementBloc>()
|
||||
.add(UpdateSelection(selectedRows));
|
||||
context.read<DeviceManagementBloc>().add(UpdateSelection(selectedRows));
|
||||
},
|
||||
initialSelectedIds: context
|
||||
.read<DeviceManagementBloc>()
|
||||
|
@ -12,8 +12,7 @@ class DeviceSearchFilters extends StatefulWidget {
|
||||
State<DeviceSearchFilters> createState() => _DeviceSearchFiltersState();
|
||||
}
|
||||
|
||||
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
||||
with HelperResponsiveLayout {
|
||||
class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperResponsiveLayout {
|
||||
final TextEditingController communityController = TextEditingController();
|
||||
final TextEditingController unitNameController = TextEditingController();
|
||||
final TextEditingController productNameController = TextEditingController();
|
||||
@ -35,8 +34,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
||||
const SizedBox(width: 20),
|
||||
_buildSearchField("Unit Name", unitNameController, 200),
|
||||
const SizedBox(width: 20),
|
||||
_buildSearchField(
|
||||
"Device Name / Product Name", productNameController, 300),
|
||||
_buildSearchField("Device Name / Product Name", productNameController, 300),
|
||||
const SizedBox(width: 20),
|
||||
_buildSearchResetButtons(),
|
||||
],
|
||||
@ -45,22 +43,34 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
||||
spacing: 20,
|
||||
runSpacing: 10,
|
||||
children: [
|
||||
_buildSearchField("Community", communityController, 200),
|
||||
_buildSearchField(
|
||||
"Community",
|
||||
communityController,
|
||||
200,
|
||||
),
|
||||
_buildSearchField("Unit Name", unitNameController, 200),
|
||||
_buildSearchField(
|
||||
"Device Name / Product Name", productNameController, 300),
|
||||
"Device Name / Product Name",
|
||||
productNameController,
|
||||
300,
|
||||
),
|
||||
_buildSearchResetButtons(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSearchField(
|
||||
String title, TextEditingController controller, double width) {
|
||||
Widget _buildSearchField(String title, TextEditingController controller, double width) {
|
||||
return StatefulTextField(
|
||||
title: title,
|
||||
width: width,
|
||||
elevation: 2,
|
||||
controller: controller,
|
||||
onSubmitted: () {
|
||||
context.read<DeviceManagementBloc>().add(SearchDevices(
|
||||
productName: productNameController.text,
|
||||
unitName: unitNameController.text,
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class FirmwareUpdateWidget extends StatefulWidget {
|
||||
const FirmwareUpdateWidget(
|
||||
{super.key, required this.deviceId, required this.version});
|
||||
const FirmwareUpdateWidget({super.key, required this.deviceId, required this.version});
|
||||
|
||||
final String deviceId;
|
||||
final int version;
|
||||
@ -34,20 +33,23 @@ class _FirmwareUpdateWidgetState extends State<FirmwareUpdateWidget> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Firmware Update',
|
||||
style: context.textTheme.titleMedium!.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'Firmware Update',
|
||||
style: context.textTheme.titleMedium!.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Are you sure?',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'Are you sure?',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
|
@ -11,7 +11,14 @@ class DeviceControlsContainer extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: ColorsManager.greyColor.withOpacity(0.2),
|
||||
border: Border.all(color: ColorsManager.boxDivider),
|
||||
// border: Border.all(color: ColorsManager.boxDivider),
|
||||
// boxShadow: <BoxShadow>[
|
||||
// BoxShadow(
|
||||
// color: ColorsManager.blackColor.withOpacity(0.05),
|
||||
// blurRadius: 6.0,
|
||||
// offset: const Offset(0, 5),
|
||||
// spreadRadius: 0)
|
||||
// ],
|
||||
),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: child,
|
||||
|
@ -5,8 +5,7 @@ abstract class ColorsManager {
|
||||
static const Color switchOffColor = Color(0x7F8D99AE);
|
||||
static const Color primaryColor = Color(0xFF0030CB); //023DFE
|
||||
static const Color secondaryTextColor = Color(0xFF848484);
|
||||
static Color primaryColorWithOpacity =
|
||||
const Color(0xFF023DFE).withOpacity(0.6);
|
||||
static Color primaryColorWithOpacity = const Color(0xFF023DFE).withOpacity(0.6);
|
||||
static const Color whiteColors = Colors.white;
|
||||
static const Color secondaryColor = Color(0xFF023DFE);
|
||||
static const Color onSecondaryColor = Color(0xFF023DFE);
|
||||
|
Reference in New Issue
Block a user