You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cloudsolutions-atoms/lib/views/widgets/timer/app_timer.dart

461 lines
18 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/timer_model.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
import '../../../new_views/app_style/app_color.dart';
class AppTimer extends StatefulWidget {
final TimerModel? timer;
final Future<bool> Function(TimerModel)? onChange;
final TimerModel? pickerTimer;
final DateTime? pickerFromDate;
final Function(TimerModel?)? onPick;
final TextStyle? style;
final BoxDecoration? decoration;
final bool enabled;
final String? label;
final double? width;
final Function(bool)? timerProgress;
const AppTimer({
Key? key,
this.label,
this.timer,
this.onChange,
this.style,
this.decoration,
this.width,
this.pickerTimer,
this.pickerFromDate,
this.onPick,
this.timerProgress,
this.enabled = true,
}) : super(key: key);
@override
State<AppTimer> createState() => _AppTimerState();
}
class _AppTimerState extends State<AppTimer> {
Timer? _timer;
DateTime? _startAt;
DateTime? _endAt;
int _delay = 0;
bool _running = false;
bool _loading = false;
bool canPickTime = false;
final ValueNotifier<String> _period = ValueNotifier("0:00:00");
DateTime? _pickerStartAt;
DateTime? _pickerEndAt;
TimerModel? _tempPickerTimer;
void setPickerTime() async {
int difference = _pickerStartAt == null ? 0 : (_pickerEndAt ?? DateTime.now()).difference(_pickerStartAt!).inSeconds;
_tempPickerTimer = TimerModel(startAt: _pickerStartAt, endAt: _pickerEndAt, durationInSecond: difference);
_pickerStartAt = null;
_pickerEndAt = null;
setState(() {});
widget.onPick!(_tempPickerTimer);
}
void deletePickerTime() {
_tempPickerTimer = null;
_pickerStartAt = null;
_pickerEndAt = null;
widget.onPick!(_tempPickerTimer);
setState(() {});
}
Future<void> _startTimer() async {
if (!_running && widget.onChange != null) {
final time = DateTime.now();
bool result = await widget.onChange!(TimerModel(startAt: time, endAt: null, durationInSecond: _delay));
if (!result) return;
_running = true;
if (_endAt != null) {
_delay += _endAt!.difference(_startAt!).inSeconds;
}
_startAt = time.subtract(Duration(seconds: _delay));
_endAt = null;
}
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_loading) return;
_period.value = (_endAt ?? DateTime.now()).difference(_startAt!).toString().split(".").first;
});
}
Future<void> _stopTimer() async {
if (widget.onChange != null) {
final time = DateTime.now();
final tempStartAt = _startAt!.add(Duration(seconds: _delay));
bool result = await widget.onChange!(TimerModel(startAt: tempStartAt, endAt: time, durationInSecond: _delay));
if (!result) return;
_running = false;
_endAt = time;
_startAt = tempStartAt;
_timer?.cancel();
}
}
Future<void> _onPressed() async {
_loading = true;
setState(() {});
if (!_running) {
await _startTimer();
} else {
await _stopTimer();
}
_loading = false;
setState(() {});
//widget.timerProgress?(_running); // Use null-aware operator to call timerProgress
}
@override
void initState() {
canPickTime = ApiManager.instance.assetGroup?.enabledEngineerTimer ?? false;
_startAt = widget.timer?.startAt;
_endAt = widget.timer?.endAt;
_running = _startAt != null && _endAt == null;
_delay = widget.timer?.durationInSecond ?? 0;
int difference = _startAt == null ? 0 : (_endAt ?? DateTime.now()).difference(_startAt!).inSeconds;
if (difference != 0 && _delay != 0) {
difference = difference + _delay;
}
_period.value = Duration(seconds: _running ? difference : (difference != 0 ? difference : _delay)).toString().split(".").first;
if (widget.pickerTimer != null) {
_tempPickerTimer = widget.pickerTimer;
}
super.initState();
if (_running) {
_startTimer();
}
}
@override
void dispose() {
_timer?.cancel(); // Cancel the timer when the widget is disposed
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
if (canPickTime) ...[
Row(
mainAxisSize: MainAxisSize.min,
children: [
ADatePicker(
label: context.translation.startTime,
hideShadow: true,
backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
date: _pickerStartAt,
from: widget.pickerFromDate,
enable: widget.enabled ? _tempPickerTimer == null : false,
formatDateWithTime: true,
onDatePicker: (selectedDate) {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
builder: (BuildContext context, Widget? child) {
final ThemeData currentTheme = Theme.of(context);
return Theme(
data: currentTheme.copyWith(
timePickerTheme: TimePickerThemeData(
dialHandColor: AppColor.primary10,
dialBackgroundColor: Colors.grey.withOpacity(0.1),
hourMinuteColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1)),
dayPeriodColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent),
dayPeriodTextColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10),
dayPeriodBorderSide: BorderSide(color: Colors.grey.withOpacity(0.2)),
entryModeIconColor: AppColor.primary10,
),
textButtonTheme: TextButtonThemeData(style: TextButton.styleFrom(foregroundColor: AppColor.primary10)),
),
child: child!,
);
},
).then((selectedTime) {
if (selectedTime != null) {
_pickerStartAt = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
if (widget.pickerFromDate != null && _pickerStartAt!.isBefore(widget.pickerFromDate!)) {
"Start time is before the request time.".showToast;
_pickerStartAt = null;
selectedTime = null;
return;
}
if (_pickerStartAt!.isAfter(DateTime.now())) {
"Start time is after than current time".showToast;
_pickerStartAt = null;
selectedTime = null;
return;
}
setState(() {});
}
});
},
).expanded,
8.width,
ADatePicker(
label: context.translation.endTime,
hideShadow: true,
backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
enable: widget.enabled ? _pickerStartAt != null : false,
from: _pickerStartAt,
date: _pickerEndAt,
to: DateTime.now(),
formatDateWithTime: true,
onDatePicker: (selectedDate) {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
builder: (BuildContext context, Widget? child) {
final ThemeData currentTheme = Theme.of(context);
return Theme(
data: currentTheme.copyWith(
timePickerTheme: TimePickerThemeData(
dialHandColor: AppColor.primary10,
dialBackgroundColor: Colors.grey.withOpacity(0.1),
hourMinuteColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1)),
dayPeriodColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent),
dayPeriodTextColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10),
dayPeriodBorderSide: BorderSide(color: Colors.grey.withOpacity(0.2)),
entryModeIconColor: AppColor.primary10,
),
textButtonTheme: TextButtonThemeData(style: TextButton.styleFrom(foregroundColor: AppColor.primary10)),
),
child: child!,
);
},
).then((selectedTime) {
if (selectedTime != null) {
selectedDate = selectedDate.add(Duration(hours: selectedTime.hour, minutes: selectedTime.minute));
bool isBeforeCurrentTime = selectedDate.isBefore(DateTime.now());
bool isAfterStartTime = selectedDate.isAfter(_pickerStartAt!);
if (!isBeforeCurrentTime) {
"Please select a time before the current time.".showToast;
return;
}
if (!isAfterStartTime) {
"End Date time must be greater then start date".showToast;
return;
}
_pickerEndAt = selectedDate;
setPickerTime();
}
});
},
).expanded,
],
),
if (_tempPickerTimer != null) ...[
8.height,
Row(
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ServiceRequestUtils.formatTimerDurationCM(_tempPickerTimer!.durationInSecond!.round()).bodyText(context).custom(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
"Start at: ${_tempPickerTimer!.startAt!.toIso8601String().toFirstActionFormat}\nEnd at: ${_tempPickerTimer!.endAt!.toIso8601String().toFirstActionFormat}".tinyFont(context),
],
).expanded,
8.width,
const Icon(Icons.delete_rounded, size: 20, color: Color(0xffF63939)).onPress(() {
deletePickerTime();
})
],
).toShadowContainer(context, padding: 8)
],
8.height,
],
Container(
// width: widget.width ?? 100 * AppStyle.getScaleFactor(context),
height: 56.toScreenHeight,
padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth),
decoration: widget.decoration ??
BoxDecoration(
color: context.isDark && !widget.enabled
? AppColor.neutral60
: !widget.enabled
// backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
? AppColor.neutral40
: AppColor.fieldBgColor(context),
borderRadius: BorderRadius.circular(10),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)],
),
child: _loading
? const SizedBox.square(dimension: 18, child: CircularProgressIndicator(color: Colors.white))
: Row(
children: [
Expanded(
child: ValueListenableBuilder<String>(
valueListenable: _period,
builder: (context, value, _) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
(widget.label ?? "Timer").tinyFont(context),
value.bodyText(context).custom(
color: widget.enabled
? context.isDark
? AppColor.neutral30
: AppColor.neutral50
: AppColor.neutral20,
),
],
);
},
),
),
if (widget.enabled) Icon(_running ? Icons.pause : Icons.play_arrow),
],
),
).onPress(_loading || !widget.enabled ? null : _onPressed),
],
);
}
}
// class AppTimerPicker extends StatefulWidget {
// final TimerModel? timer;
// final Function(TimerModel?) onPick;
//
// AppTimerPicker({Key? key, this.timer, required this.onPick}) : super(key: key);
//
// @override
// _AppTimerPickerState createState() {
// return _AppTimerPickerState();
// }
// }
//
// class _AppTimerPickerState extends State<AppTimerPicker> {
// DateTime? _pickerStartAt;
// DateTime? _pickerEndAt;
//
// TimerModel? _tempPickerTimer;
//
// @override
// void initState() {
// if (widget.timer != null) {
// _tempPickerTimer = widget.timer;
// }
// super.initState();
// }
//
// void setTime() async {
// int difference = _pickerStartAt == null ? 0 : (_pickerEndAt ?? DateTime.now()).difference(_pickerStartAt!).inSeconds;
// _tempPickerTimer = TimerModel(startAt: _pickerStartAt, endAt: _pickerEndAt, durationInSecond: difference);
// // timerList.add(_tempModel);
// // await widget.onChange!(_tempModel);
// _pickerStartAt = null;
// _pickerEndAt = null;
// setState(() {});
// widget.onPick(_tempPickerTimer);
// }
//
// void deleteTime() {
// _tempPickerTimer = null;
// _pickerStartAt = null;
// _pickerEndAt = null;
// widget.onPick(_tempPickerTimer);
// setState(() {});
// }
//
// @override
// void dispose() {
// super.dispose();
// }
//
// @override
// Widget build(BuildContext context) {
// return Column(
// children: [
// Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// ADatePicker(
// label: context.translation.startTime,
// hideShadow: true,
// backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
// date: _pickerStartAt,
// enable: _tempPickerTimer == null,
// formatDateWithTime: true,
// onDatePicker: (selectedDate) {
// showTimePicker(
// context: context,
// initialTime: TimeOfDay.now(),
// ).then((selectedTime) {
// if (selectedTime != null) {
// _pickerStartAt = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
// setState(() {});
// }
// });
// },
// ).expanded,
// 8.width,
// ADatePicker(
// label: context.translation.endTime,
// hideShadow: true,
// backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
// enable: _pickerStartAt != null,
// date: _pickerEndAt,
// formatDateWithTime: true,
// onDatePicker: (selectedDate) {
// showTimePicker(
// context: context,
// initialTime: TimeOfDay.now(),
// ).then((selectedTime) {
// if (selectedTime != null) {
// DateTime selectedDateTime = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
// if (_pickerStartAt != null && selectedDateTime.isBefore(_pickerStartAt!)) {
// "End Date time must be greater then start date".showToast;
// return;
// }
// _pickerEndAt = selectedDateTime;
// setTime();
// }
// });
// },
// ).expanded,
// ],
// ),
// if (_tempPickerTimer != null) ...[
// 8.height,
// Row(
// children: [
// Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// ServiceRequestUtils.formatTimerDurationCM(_tempPickerTimer!.durationInSecond!.round()).bodyText(context).custom(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
// "Start at: ${_tempPickerTimer!.startAt!.toIso8601String().toFirstActionFormat}\nEnd at: ${_tempPickerTimer!.endAt!.toIso8601String().toFirstActionFormat}".tinyFont(context),
// ],
// ).expanded,
// 8.width,
// const Icon(Icons.delete_rounded, size: 20, color: Color(0xffF63939)).onPress(() {
// deleteTime();
// })
// ],
// ).toShadowContainer(context, padding: 8)
// ]
// ],
// );
// }
// }