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.
		
		
		
		
		
			
		
			
				
	
	
		
			414 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Dart
		
	
			
		
		
	
	
			414 lines
		
	
	
		
			15 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/helper/utils.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: AppColor.neutral100,
 | 
						|
                date: _pickerStartAt,
 | 
						|
                from: widget.pickerFromDate,
 | 
						|
                enable: widget.enabled ?  _tempPickerTimer == null:false,
 | 
						|
                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: AppColor.neutral100,
 | 
						|
                enable: widget.enabled?  _pickerStartAt != null:false,
 | 
						|
                from:_pickerStartAt,
 | 
						|
                date: _pickerEndAt,
 | 
						|
                to: DateTime.now(),
 | 
						|
                formatDateWithTime: true,
 | 
						|
                onDatePicker: (selectedDate) {
 | 
						|
                  showTimePicker(
 | 
						|
                    context: context,
 | 
						|
                    initialTime: TimeOfDay.now(),
 | 
						|
                  ).then((selectedTime) {
 | 
						|
 | 
						|
                    if (selectedTime != null) {
 | 
						|
                      final TimeOfDay now = TimeOfDay.now();
 | 
						|
                      final bool isBeforeNow = Utils.isBeforeOrEqualCurrentTime(selectedTime, now);
 | 
						|
                      if (!isBeforeNow) {
 | 
						|
                        "Please select a time before the current time.".showToast;
 | 
						|
                        return;
 | 
						|
                      }
 | 
						|
                      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;
 | 
						|
                      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
 | 
						|
                        ? AppColor.neutral40
 | 
						|
                        : AppColor.background(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: AppColor.neutral100,
 | 
						|
//               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: AppColor.neutral100,
 | 
						|
//               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)
 | 
						|
//         ]
 | 
						|
//       ],
 | 
						|
//     );
 | 
						|
//   }
 | 
						|
// }
 |