refactor(legacy): remove remaining setState usages + tap-to-center on device banner
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
@@ -18,28 +18,38 @@ class EditPeriodSheet extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _EditPeriodSheetState extends ConsumerState<EditPeriodSheet> {
|
||||
late TimeOfDay _start;
|
||||
late TimeOfDay _end;
|
||||
late List<bool> _weekDays;
|
||||
late final ValueNotifier<TimeOfDay> _start;
|
||||
late final ValueNotifier<TimeOfDay> _end;
|
||||
late final ValueNotifier<List<bool>> _weekDays;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.initial != null) {
|
||||
_start = _parseTime(widget.initial!.start);
|
||||
_end = _parseTime(widget.initial!.end);
|
||||
_weekDays = widget.initial!.week
|
||||
.padRight(7, '0')
|
||||
.split('')
|
||||
.map((c) => c == '1')
|
||||
.toList();
|
||||
_start = ValueNotifier(_parseTime(widget.initial!.start));
|
||||
_end = ValueNotifier(_parseTime(widget.initial!.end));
|
||||
_weekDays = ValueNotifier(
|
||||
widget.initial!.week
|
||||
.padRight(7, '0')
|
||||
.split('')
|
||||
.map((c) => c == '1')
|
||||
.toList(),
|
||||
);
|
||||
} else {
|
||||
_start = const TimeOfDay(hour: 22, minute: 0);
|
||||
_end = const TimeOfDay(hour: 7, minute: 0);
|
||||
_weekDays = [true, true, true, true, true, true, true];
|
||||
_start = ValueNotifier(const TimeOfDay(hour: 22, minute: 0));
|
||||
_end = ValueNotifier(const TimeOfDay(hour: 7, minute: 0));
|
||||
_weekDays = ValueNotifier([true, true, true, true, true, true, true]);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_start.dispose();
|
||||
_end.dispose();
|
||||
_weekDays.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
TimeOfDay _parseTime(String time) {
|
||||
final parts = time.split(':');
|
||||
return TimeOfDay(
|
||||
@@ -54,13 +64,13 @@ class _EditPeriodSheetState extends ConsumerState<EditPeriodSheet> {
|
||||
String _formatForDisplay(TimeOfDay t) =>
|
||||
'${t.hour.toString().padLeft(2, '0')}:${t.minute.toString().padLeft(2, '0')}';
|
||||
|
||||
String _weekToString() => _weekDays.map((d) => d ? '1' : '0').join();
|
||||
String _weekToString(List<bool> days) =>
|
||||
days.map((d) => d ? '1' : '0').join();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final primaryColor = context.sfColors.legacyPrimary;
|
||||
final isEditing = widget.initial != null;
|
||||
final hasSelectedDays = _weekDays.any((d) => d);
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
@@ -89,32 +99,38 @@ class _EditPeriodSheetState extends ConsumerState<EditPeriodSheet> {
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _TimePickerTile(
|
||||
label: context.translate(I18n.doNotDisturbStart),
|
||||
value: _formatForDisplay(_start),
|
||||
color: primaryColor,
|
||||
onTap: () async {
|
||||
final picked = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: _start,
|
||||
);
|
||||
if (picked != null) setState(() => _start = picked);
|
||||
},
|
||||
child: ValueListenableBuilder<TimeOfDay>(
|
||||
valueListenable: _start,
|
||||
builder: (context, start, _) => _TimePickerTile(
|
||||
label: context.translate(I18n.doNotDisturbStart),
|
||||
value: _formatForDisplay(start),
|
||||
color: primaryColor,
|
||||
onTap: () async {
|
||||
final picked = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: start,
|
||||
);
|
||||
if (picked != null) _start.value = picked;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
|
||||
Expanded(
|
||||
child: _TimePickerTile(
|
||||
label: context.translate(I18n.doNotDisturbEnd),
|
||||
value: _formatForDisplay(_end),
|
||||
color: primaryColor,
|
||||
onTap: () async {
|
||||
final picked = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: _end,
|
||||
);
|
||||
if (picked != null) setState(() => _end = picked);
|
||||
},
|
||||
child: ValueListenableBuilder<TimeOfDay>(
|
||||
valueListenable: _end,
|
||||
builder: (context, end, _) => _TimePickerTile(
|
||||
label: context.translate(I18n.doNotDisturbEnd),
|
||||
value: _formatForDisplay(end),
|
||||
color: primaryColor,
|
||||
onTap: () async {
|
||||
final picked = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: end,
|
||||
);
|
||||
if (picked != null) _end.value = picked;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -129,29 +145,41 @@ class _EditPeriodSheetState extends ConsumerState<EditPeriodSheet> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 10, big: 8)),
|
||||
WeekDayRow(
|
||||
week: _weekToString(),
|
||||
activeColor: primaryColor,
|
||||
readOnly: false,
|
||||
onToggle: (index) =>
|
||||
setState(() => _weekDays[index] = !_weekDays[index]),
|
||||
ValueListenableBuilder<List<bool>>(
|
||||
valueListenable: _weekDays,
|
||||
builder: (context, days, _) => WeekDayRow(
|
||||
week: _weekToString(days),
|
||||
activeColor: primaryColor,
|
||||
readOnly: false,
|
||||
onToggle: (index) {
|
||||
final updated = List<bool>.from(days);
|
||||
updated[index] = !updated[index];
|
||||
_weekDays.value = updated;
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 20)),
|
||||
PrimaryButton(
|
||||
onPressed: hasSelectedDays
|
||||
? () {
|
||||
Navigator.pop(
|
||||
context,
|
||||
DoNotDisturbPeriod(
|
||||
start: _formatForApi(_start),
|
||||
end: _formatForApi(_end),
|
||||
week: _weekToString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
text: context.translate(I18n.doNotDisturbConfirm),
|
||||
color: primaryColor,
|
||||
ValueListenableBuilder<List<bool>>(
|
||||
valueListenable: _weekDays,
|
||||
builder: (context, days, _) {
|
||||
final hasSelectedDays = days.any((d) => d);
|
||||
return PrimaryButton(
|
||||
onPressed: hasSelectedDays
|
||||
? () {
|
||||
Navigator.pop(
|
||||
context,
|
||||
DoNotDisturbPeriod(
|
||||
start: _formatForApi(_start.value),
|
||||
end: _formatForApi(_end.value),
|
||||
week: _weekToString(days),
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
text: context.translate(I18n.doNotDisturbConfirm),
|
||||
color: primaryColor,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
|
||||
],
|
||||
|
||||
@@ -44,20 +44,14 @@ class _ActivityFormSheetState extends ConsumerState<ActivityFormSheet> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_nameController = TextEditingController(text: widget.activity?.name ?? '');
|
||||
_nameController.addListener(_onNameChanged);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.removeListener(_onNameChanged);
|
||||
_nameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onNameChanged() {
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _submit({
|
||||
required ActivityFormState formState,
|
||||
required List<ScheduledActivityEntity> currentActivities,
|
||||
@@ -110,9 +104,6 @@ class _ActivityFormSheetState extends ConsumerState<ActivityFormSheet> {
|
||||
: (ref.watch(scheduledActivitiesProvider(device.id)).value ??
|
||||
const []);
|
||||
|
||||
final canSubmit = _nameController.text.trim().isNotEmpty &&
|
||||
formState.isTimeValid;
|
||||
|
||||
final bottomInset = MediaQuery.of(context).viewInsets.bottom;
|
||||
|
||||
return Padding(
|
||||
@@ -209,30 +200,39 @@ class _ActivityFormSheetState extends ConsumerState<ActivityFormSheet> {
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
|
||||
SizedBox(
|
||||
height: SizeUtils.getByScreen(small: 48, big: 46),
|
||||
child: ElevatedButton(
|
||||
onPressed: canSubmit
|
||||
? () => _submit(
|
||||
formState: formState,
|
||||
currentActivities: currentActivities,
|
||||
)
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: primaryColor,
|
||||
disabledBackgroundColor: Colors.grey[300],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
SizeUtils.getByScreen(small: 12, big: 10),
|
||||
child: AnimatedBuilder(
|
||||
animation: _nameController,
|
||||
builder: (context, _) {
|
||||
final canSubmit =
|
||||
_nameController.text.trim().isNotEmpty &&
|
||||
formState.isTimeValid;
|
||||
return ElevatedButton(
|
||||
onPressed: canSubmit
|
||||
? () => _submit(
|
||||
formState: formState,
|
||||
currentActivities: currentActivities,
|
||||
)
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: primaryColor,
|
||||
disabledBackgroundColor: Colors.grey[300],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
SizeUtils.getByScreen(small: 12, big: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
context.translate(I18n.save),
|
||||
style: TextStyle(
|
||||
color: canSubmit ? Colors.white : Colors.grey,
|
||||
fontSize: SizeUtils.getByScreen(small: 16, big: 15),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
context.translate(I18n.save),
|
||||
style: TextStyle(
|
||||
color: canSubmit ? Colors.white : Colors.grey,
|
||||
fontSize:
|
||||
SizeUtils.getByScreen(small: 16, big: 15),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -85,7 +85,6 @@ class _LegacyOtpCodeFieldsState extends State<LegacyOtpCodeFields> {
|
||||
|
||||
_focusNodes[nextIndex].requestFocus();
|
||||
_emit();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _onChanged(int index, String value) {
|
||||
@@ -101,7 +100,6 @@ class _LegacyOtpCodeFieldsState extends State<LegacyOtpCodeFields> {
|
||||
}
|
||||
|
||||
_emit();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
KeyEventResult _onKey(int index, KeyEvent event) {
|
||||
@@ -114,7 +112,6 @@ class _LegacyOtpCodeFieldsState extends State<LegacyOtpCodeFields> {
|
||||
_controllers[index - 1].text = '';
|
||||
_focusNodes[index - 1].requestFocus();
|
||||
_emit();
|
||||
setState(() {});
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ class DeviceBanner extends ConsumerStatefulWidget {
|
||||
final List<DeviceEntity> devices;
|
||||
final List<PositionEntity> positions;
|
||||
final ValueChanged<DeviceEntity> onDeviceChanged;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const DeviceBanner({
|
||||
super.key,
|
||||
@@ -18,6 +19,7 @@ class DeviceBanner extends ConsumerStatefulWidget {
|
||||
required this.devices,
|
||||
required this.positions,
|
||||
required this.onDeviceChanged,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -94,10 +96,14 @@ class _DeviceBannerState extends ConsumerState<DeviceBanner> {
|
||||
final pos = widget.positions
|
||||
.where((p) => p.deviceIdentificator == dev.identificator)
|
||||
.firstOrNull;
|
||||
return _DeviceCard(
|
||||
device: dev,
|
||||
position: pos,
|
||||
primaryColor: primaryColor,
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: widget.onTap,
|
||||
child: _DeviceCard(
|
||||
device: dev,
|
||||
position: pos,
|
||||
primaryColor: primaryColor,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -496,6 +496,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
devices: widget.devices,
|
||||
positions: widget.positions,
|
||||
onDeviceChanged: widget.onDeviceChanged,
|
||||
onTap: _centerOnDevice,
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user