466 lines
16 KiB
Dart
466 lines
16 KiB
Dart
![]() |
//import 'dart:ffi';
|
||
|
|
||
|
import 'dart:async';
|
||
|
|
||
|
import 'package:flutter/material.dart';
|
||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
import 'package:habitrack_app/function_widgets/widget_settings_menu/setting_entry.dart';
|
||
|
import 'package:habitrack_app/function_widgets/widget_settings_menu/widget_settings.dart';
|
||
|
import 'package:habitrack_app/infrastructure/widget_wall/items_controller.dart';
|
||
|
import 'package:habitrack_app/infrastructure/widget_wall/items_state.dart';
|
||
|
import 'package:habitrack_app/main.dart';
|
||
|
import 'package:habitrack_app/sembast/timer.dart';
|
||
|
import 'package:logger/logger.dart';
|
||
|
|
||
|
// ignore: must_be_immutable
|
||
|
class CompoundWidgetTimer extends ConsumerStatefulWidget {
|
||
|
CompoundWidgetTimer({required this.item, super.key});
|
||
|
|
||
|
TimerItem item;
|
||
|
|
||
|
double getProgress() {
|
||
|
return (item.current / item.goal) / 60;
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
ConsumerState<CompoundWidgetTimer> createState() =>
|
||
|
_CompoundWidgetTimerState();
|
||
|
}
|
||
|
|
||
|
class _CompoundWidgetTimerState extends ConsumerState<CompoundWidgetTimer>
|
||
|
with WidgetsBindingObserver {
|
||
|
_CompoundWidgetTimerState();
|
||
|
|
||
|
String button1text = '';
|
||
|
Timer _timer = Timer(Duration.zero, () => ());
|
||
|
|
||
|
DateTime calcDue() {
|
||
|
final now = DateTime.now();
|
||
|
const secondsToAdd = 10;
|
||
|
const duration = Duration(seconds: secondsToAdd);
|
||
|
final futureTime = now.add(duration);
|
||
|
|
||
|
Logger().i(futureTime);
|
||
|
|
||
|
return DateTime.now();
|
||
|
}
|
||
|
|
||
|
void handleButton1() {
|
||
|
//start timer from 0
|
||
|
const oneSec = Duration(seconds: 1);
|
||
|
|
||
|
if (widget.item.state == 'initial') {
|
||
|
widget.item = widget.item.copyWith(state: 'running');
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
_timer = Timer.periodic(
|
||
|
oneSec,
|
||
|
(Timer funcTimer) {
|
||
|
if (widget.item.current == (widget.item.goal * 60)) {
|
||
|
setState(() {
|
||
|
final now = DateTime.now();
|
||
|
widget.item = widget.item.copyWith(
|
||
|
state: 'completed',
|
||
|
completedOn: now.toString(),
|
||
|
);
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
|
||
|
funcTimer.cancel();
|
||
|
});
|
||
|
} else {
|
||
|
setState(() {
|
||
|
widget.item =
|
||
|
widget.item.copyWith(current: widget.item.current + 1);
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// continue timer
|
||
|
else if (widget.item.state == 'paused') {
|
||
|
widget.item = widget.item.copyWith(state: 'running');
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
_timer = Timer.periodic(
|
||
|
const Duration(seconds: 1),
|
||
|
(Timer funcTimer) {
|
||
|
if (widget.item.current == (widget.item.goal * 60)) {
|
||
|
setState(() {
|
||
|
final now = DateTime.now();
|
||
|
widget.item = widget.item.copyWith(
|
||
|
state: 'completed',
|
||
|
completedOn: now.toString(),
|
||
|
);
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
|
||
|
funcTimer.cancel();
|
||
|
});
|
||
|
} else {
|
||
|
setState(() {
|
||
|
widget.item =
|
||
|
widget.item.copyWith(current: widget.item.current + 1);
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
);
|
||
|
} else if (widget.item.state == 'running') {
|
||
|
widget.item = widget.item.copyWith(state: 'paused');
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
|
||
|
_timer.cancel();
|
||
|
} else if (widget.item.state == 'completed') {}
|
||
|
}
|
||
|
|
||
|
void stopTimer() {
|
||
|
setState(() {
|
||
|
if (_timer.isActive) {
|
||
|
_timer.cancel();
|
||
|
}
|
||
|
widget.item = widget.item.copyWith(
|
||
|
current: 0,
|
||
|
state: 'initial',
|
||
|
);
|
||
|
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
String _formatCurrent() {
|
||
|
final hours = (widget.item.current / 3600).floor();
|
||
|
final minutes = ((widget.item.current - (hours * 3600)) / 60).floor();
|
||
|
return '$hours hours : $minutes minutes';
|
||
|
}
|
||
|
|
||
|
String _formattedTime() {
|
||
|
final minutesTotal = widget.item.goal;
|
||
|
|
||
|
final hours = (minutesTotal / 60).floor();
|
||
|
final minutes = minutesTotal - (hours * 60);
|
||
|
return '$hours hours : $minutes minutes';
|
||
|
}
|
||
|
|
||
|
void _toggleExpansion() {
|
||
|
setState(() {
|
||
|
widget.item = widget.item.copyWith(isExpanded: !widget.item.isExpanded);
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
String _getButton1Text(BuildContext context) {
|
||
|
if (widget.item.state == 'initial') {
|
||
|
return AppLocalizations.of(context)!.timerWidget_buttonStart;
|
||
|
} else if (widget.item.state == 'running') {
|
||
|
return AppLocalizations.of(context)!.timerWidget_buttonPause;
|
||
|
} else if (widget.item.state == 'paused') {
|
||
|
return AppLocalizations.of(context)!.timerWidget_buttonContinue;
|
||
|
}
|
||
|
return 'Done';
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
ref
|
||
|
..watch(homeControllerProvider)
|
||
|
..watch(itemsProvider);
|
||
|
|
||
|
return Container(
|
||
|
margin: const EdgeInsets.only(left: 10, top: 10, right: 10, bottom: 10),
|
||
|
padding: const EdgeInsets.only(left: 10, top: 15, right: 10, bottom: 15),
|
||
|
decoration: BoxDecoration(
|
||
|
color: Theme.of(context).colorScheme.primary,
|
||
|
borderRadius: const BorderRadius.all(Radius.circular(7)),
|
||
|
boxShadow: [
|
||
|
BoxShadow(
|
||
|
color: const Color(0x00000000).withOpacity(0.25),
|
||
|
spreadRadius: 2,
|
||
|
blurRadius: 5,
|
||
|
// changes position of shadow
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
width: double.infinity,
|
||
|
child: Column(
|
||
|
children: [
|
||
|
Row(
|
||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
|
children: [
|
||
|
if (!widget.item.isExpanded) ...[
|
||
|
Icon(
|
||
|
Icons.timer,
|
||
|
size: 20,
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
Expanded(
|
||
|
flex: 2,
|
||
|
child: Container(
|
||
|
margin: const EdgeInsets.only(left: 10, right: 10),
|
||
|
height: 30,
|
||
|
child: ClipRRect(
|
||
|
borderRadius: const BorderRadius.all(
|
||
|
Radius.circular(5),
|
||
|
), // Rounded corners
|
||
|
child: LinearProgressIndicator(
|
||
|
value:
|
||
|
widget.getProgress(), // Progress value (0.0 - 1.0)
|
||
|
backgroundColor:
|
||
|
Colors.grey.withOpacity(0.5), // Background color
|
||
|
valueColor: const AlwaysStoppedAnimation<Color>(
|
||
|
Color(0xffA4E8FD),
|
||
|
), // Progress color
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
Expanded(
|
||
|
child: Text(
|
||
|
widget.item.name,
|
||
|
style: TextStyle(
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
// alignment: TextAlign.left,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
if (widget.item.isExpanded) ...[
|
||
|
Expanded(
|
||
|
child: Text(
|
||
|
widget.item.name,
|
||
|
textScaler: const TextScaler.linear(2),
|
||
|
style: TextStyle(
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
|
||
|
// alignment: TextAlign.left,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
Expanded(
|
||
|
flex: 0,
|
||
|
child: IconButton(
|
||
|
icon: Icon(
|
||
|
Icons.settings,
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
onPressed: () => _showSettingsMenu(ref),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
Expanded(
|
||
|
flex: 0,
|
||
|
child: IconButton(
|
||
|
icon: Icon(
|
||
|
widget.item.isExpanded
|
||
|
? Icons.arrow_drop_up_outlined
|
||
|
: Icons.arrow_drop_down_circle_outlined,
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
onPressed: _toggleExpansion,
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
if (widget.item.isExpanded) ...[
|
||
|
// Additional child elements when expanded
|
||
|
SizedBox(
|
||
|
height: 300,
|
||
|
width: MediaQuery.of(context).size.width,
|
||
|
child: Column(
|
||
|
children: [
|
||
|
Stack(
|
||
|
children: <Widget>[
|
||
|
Container(
|
||
|
alignment: Alignment.center,
|
||
|
// color: Colors.blueAccent,
|
||
|
width: MediaQuery.of(context).size.width,
|
||
|
height: 250,
|
||
|
child: SizedBox(
|
||
|
height: 150,
|
||
|
// color: Colors.greenAccent,
|
||
|
child: Column(
|
||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
children: [
|
||
|
Text(
|
||
|
// ignore: lines_longer_than_80_chars
|
||
|
'${AppLocalizations.of(context)!.timerWidget_current}: ${_formatCurrent()} \n ${AppLocalizations.of(context)!.timerWidget_goal}: ${_formattedTime()}',
|
||
|
style: Theme.of(context)
|
||
|
.textTheme
|
||
|
.bodyMedium!
|
||
|
.copyWith(
|
||
|
color: Theme.of(context)
|
||
|
.colorScheme
|
||
|
.onPrimary,
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
Positioned(
|
||
|
top: 10,
|
||
|
left: MediaQuery.of(context).size.width * 0.15,
|
||
|
width: MediaQuery.of(context).size.width * 0.60,
|
||
|
height: 220,
|
||
|
child: CircularProgressIndicator(
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
strokeWidth: 7,
|
||
|
value: widget.getProgress(),
|
||
|
semanticsLabel: 'Circular progress indicator',
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
Expanded(
|
||
|
child: Column(
|
||
|
children: [
|
||
|
Row(
|
||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
children: [
|
||
|
TextButton(
|
||
|
onPressed: handleButton1,
|
||
|
child: Text(
|
||
|
_getButton1Text(context),
|
||
|
style: Theme.of(context)
|
||
|
.textTheme
|
||
|
.bodyMedium!
|
||
|
.copyWith(
|
||
|
color: Theme.of(context)
|
||
|
.colorScheme
|
||
|
.onPrimary,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
TextButton(
|
||
|
onPressed: stopTimer,
|
||
|
child: Text(
|
||
|
AppLocalizations.of(context)!
|
||
|
.timerWidget_buttonReset,
|
||
|
style: Theme.of(context)
|
||
|
.textTheme
|
||
|
.bodyMedium!
|
||
|
.copyWith(
|
||
|
color: Theme.of(context)
|
||
|
.colorScheme
|
||
|
.onPrimary,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
// Add more widgets here as needed
|
||
|
],
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Future<void> _showSettingsMenu(WidgetRef ref) async {
|
||
|
logger.i('Opening settings');
|
||
|
|
||
|
final settingEntries = WidgetSettingsData(
|
||
|
entries: {
|
||
|
'name': SettingEntryText(
|
||
|
name: AppLocalizations.of(context)!.widgetSettings_name,
|
||
|
defaultValue: widget.item.name,
|
||
|
),
|
||
|
'duration': SettingEntryDuration(
|
||
|
name: AppLocalizations.of(context)!.timerWidgetSettings_duration,
|
||
|
defaultValue: widget.item.goal,
|
||
|
),
|
||
|
},
|
||
|
);
|
||
|
|
||
|
if (widget.item.state == 'running') {
|
||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||
|
SnackBar(
|
||
|
content: Text(
|
||
|
AppLocalizations.of(context)!.timerWidget_pausedForEdit,
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
} else {
|
||
|
return showDialog<void>(
|
||
|
context: context,
|
||
|
barrierDismissible: false,
|
||
|
builder: (BuildContext context) {
|
||
|
return AlertDialog(
|
||
|
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
||
|
content: WidgetSettings(
|
||
|
entries: settingEntries,
|
||
|
),
|
||
|
actions: [
|
||
|
OutlinedButton(
|
||
|
style: OutlinedButton.styleFrom(
|
||
|
backgroundColor: Colors.red,
|
||
|
shape: RoundedRectangleBorder(
|
||
|
borderRadius: BorderRadius.circular(12),
|
||
|
),
|
||
|
),
|
||
|
onPressed: () async {
|
||
|
ref.watch(itemsProvider);
|
||
|
final items = ref.watch(homeControllerProvider);
|
||
|
|
||
|
widget.item = widget.item.copyWith(isVisible: false);
|
||
|
setState(() {
|
||
|
items.edit(widget.item);
|
||
|
});
|
||
|
|
||
|
logger.i('Attempting delete');
|
||
|
// ignore: use_build_context_synchronously
|
||
|
Navigator.of(context).pop();
|
||
|
},
|
||
|
child: Text(
|
||
|
AppLocalizations.of(context)!.widgetSettings_deleteButton,
|
||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
ElevatedButton(
|
||
|
style: ElevatedButton.styleFrom(
|
||
|
shape: RoundedRectangleBorder(
|
||
|
borderRadius: BorderRadius.circular(12),
|
||
|
),
|
||
|
backgroundColor: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
onPressed: () {
|
||
|
_timer.cancel();
|
||
|
ref.watch(homeControllerProvider);
|
||
|
|
||
|
logger.i('Attempting edit of name and/or duration');
|
||
|
|
||
|
final name = settingEntries.getValue('name') as String;
|
||
|
logger.i('New name: $name');
|
||
|
final duration = settingEntries.getValue('duration') as int;
|
||
|
widget.item =
|
||
|
widget.item.copyWith(goal: duration, name: name);
|
||
|
setState(() {
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
});
|
||
|
logger.i('NAME AND DURATION SUCCESSFULLY UPDATED');
|
||
|
|
||
|
Navigator.of(context).pop();
|
||
|
},
|
||
|
child: Text(
|
||
|
AppLocalizations.of(context)!.widgetSettings_saveButton,
|
||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||
|
color: Theme.of(context).colorScheme.primary,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
);
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|