492 lines
18 KiB
Dart
492 lines
18 KiB
Dart
![]() |
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/hydration.dart';
|
||
|
import 'package:liquid_progress_indicator_v2/liquid_progress_indicator.dart';
|
||
|
|
||
|
// ignore: must_be_immutable
|
||
|
class CompoundWidgetWater extends ConsumerStatefulWidget {
|
||
|
CompoundWidgetWater({required this.item, super.key});
|
||
|
|
||
|
late Hydration item;
|
||
|
|
||
|
double getProgress() {
|
||
|
return item.current / item.goal;
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
ConsumerState<CompoundWidgetWater> createState() =>
|
||
|
_CompoundWidgetWaterState();
|
||
|
}
|
||
|
|
||
|
class _CompoundWidgetWaterState extends ConsumerState<CompoundWidgetWater> {
|
||
|
void _toggleExpansion() {
|
||
|
setState(() {
|
||
|
widget.item = widget.item.copyWith(isExpanded: !widget.item.isExpanded);
|
||
|
ref.watch(homeControllerProvider).edit(widget.item);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void _addQuantity(int toAdd) {
|
||
|
setState(() {
|
||
|
final controller = ref.watch(homeControllerProvider);
|
||
|
final oldval = widget.item.current;
|
||
|
|
||
|
logger.i('Old item: ${widget.item}');
|
||
|
|
||
|
widget.item = widget.item.copyWith(current: oldval + toAdd);
|
||
|
logger.i('New item: ${widget.item}');
|
||
|
|
||
|
controller.edit(widget.item);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
ref.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),
|
||
|
//height: 100,
|
||
|
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.local_drink,
|
||
|
size: 20,
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
Expanded(
|
||
|
flex: 2,
|
||
|
child: Container(
|
||
|
margin: const EdgeInsets.only(left: 10, right: 10),
|
||
|
// Width of the progress bar
|
||
|
height: 30, // Height of the progress bar
|
||
|
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: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
if (widget.item.isExpanded) ...[
|
||
|
Expanded(
|
||
|
child: Text(
|
||
|
widget.item.name,
|
||
|
textScaler: const TextScaler.linear(2),
|
||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
Expanded(
|
||
|
flex: 0,
|
||
|
child: IconButton(
|
||
|
icon: Icon(
|
||
|
Icons.settings,
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
onPressed: () => _showSettingsMenu(ref),
|
||
|
),
|
||
|
//child: Icon(Icons.arrow_drop_down_circle_outlined),
|
||
|
),
|
||
|
],
|
||
|
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,
|
||
|
),
|
||
|
//child: Icon(Icons.arrow_drop_down_circle_outlined),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
if (widget.item.isExpanded) ...[
|
||
|
// Additional child elements when expanded
|
||
|
SizedBox(
|
||
|
height: 300,
|
||
|
width: MediaQuery.of(context).size.width,
|
||
|
child: Column(
|
||
|
children: [
|
||
|
//INSERT WIDGET SPECIFIC STUFF HERE
|
||
|
SizedBox(
|
||
|
height: 300,
|
||
|
width: MediaQuery.of(context).size.width,
|
||
|
child: Row(
|
||
|
children: [
|
||
|
Expanded(
|
||
|
child: OvershootLiquidLinearProgressIndicator(
|
||
|
current: widget.item.current,
|
||
|
goal: widget.item.goal,
|
||
|
),
|
||
|
),
|
||
|
Expanded(
|
||
|
child: Column(
|
||
|
children: [
|
||
|
Text(
|
||
|
'${widget.item.current / 1000} / ${widget.item.goal / 1000} L',
|
||
|
style: Theme.of(context)
|
||
|
.textTheme
|
||
|
.bodyMedium!
|
||
|
.copyWith(
|
||
|
color: Theme.of(context)
|
||
|
.colorScheme
|
||
|
.onPrimary,
|
||
|
),
|
||
|
),
|
||
|
Expanded(
|
||
|
child: OutlinedButton(
|
||
|
style: ButtonStyle(
|
||
|
backgroundColor: WidgetStateProperty.all(
|
||
|
Theme.of(context).colorScheme.secondary,
|
||
|
),
|
||
|
),
|
||
|
onPressed: () => {
|
||
|
_addQuantity(
|
||
|
widget.item.button1Amount,
|
||
|
),
|
||
|
},
|
||
|
child: Text(
|
||
|
'${widget.item.button1Amount} mL',
|
||
|
style: Theme.of(context)
|
||
|
.textTheme
|
||
|
.bodyMedium!
|
||
|
.copyWith(
|
||
|
color: Theme.of(context)
|
||
|
.colorScheme
|
||
|
.onSecondary,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
Expanded(
|
||
|
child: OutlinedButton(
|
||
|
style: ButtonStyle(
|
||
|
backgroundColor: WidgetStateProperty.all(
|
||
|
Theme.of(context).colorScheme.secondary,
|
||
|
),
|
||
|
),
|
||
|
onPressed: () => {
|
||
|
_addQuantity(
|
||
|
widget.item.button2Amount,
|
||
|
),
|
||
|
},
|
||
|
child: Text(
|
||
|
'${widget.item.button2Amount} mL',
|
||
|
style: Theme.of(context)
|
||
|
.textTheme
|
||
|
.bodyMedium!
|
||
|
.copyWith(
|
||
|
color: Theme.of(context)
|
||
|
.colorScheme
|
||
|
.onPrimary,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
Expanded(
|
||
|
child: OutlinedButton(
|
||
|
style: ButtonStyle(
|
||
|
backgroundColor: WidgetStateProperty.all(
|
||
|
Theme.of(context).colorScheme.secondary,
|
||
|
),
|
||
|
),
|
||
|
onPressed: _showPopupCustomAmount,
|
||
|
child: Text(
|
||
|
AppLocalizations.of(context)!
|
||
|
.waterWidget_customAmountButton,
|
||
|
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 {
|
||
|
final settingEntries = WidgetSettingsData(
|
||
|
entries: {
|
||
|
'name': SettingEntryText(
|
||
|
name: AppLocalizations.of(context)!.widgetSettings_name,
|
||
|
defaultValue: widget.item.name,
|
||
|
),
|
||
|
'button1Amount': SettingEntryNumeric(
|
||
|
name: AppLocalizations.of(context)!.waterWidgetSettings_button1,
|
||
|
defaultValue: widget.item.button1Amount,
|
||
|
),
|
||
|
'button2Amount': SettingEntryNumeric(
|
||
|
name: AppLocalizations.of(context)!.waterWidgetSettings_button2,
|
||
|
defaultValue: widget.item.button2Amount,
|
||
|
),
|
||
|
'targetGoal': SettingEntrySlider(
|
||
|
name: AppLocalizations.of(context)!.waterWidgetSettings_goal,
|
||
|
defaultValue: double.parse(widget.item.goal.toString()),
|
||
|
divisions: 80,
|
||
|
topValue: 4000,
|
||
|
),
|
||
|
'currentAmount': SettingEntryNumeric(
|
||
|
name: AppLocalizations.of(context)!.waterWidgetSettings_current,
|
||
|
defaultValue: widget.item.current,
|
||
|
),
|
||
|
},
|
||
|
);
|
||
|
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: () {
|
||
|
//widget.parent.delete(ref);
|
||
|
ref.watch(itemsProvider);
|
||
|
final controller = ref.watch(homeControllerProvider);
|
||
|
|
||
|
widget.item = widget.item.copyWith(isVisible: false);
|
||
|
controller.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(
|
||
|
backgroundColor: Theme.of(context).colorScheme.onPrimary,
|
||
|
shape: RoundedRectangleBorder(
|
||
|
borderRadius: BorderRadius.circular(12),
|
||
|
),
|
||
|
),
|
||
|
onPressed: () {
|
||
|
final controller = ref.watch(homeControllerProvider);
|
||
|
|
||
|
logger.i('Attempting edit of water widget stuff');
|
||
|
// widget.settingEntries.notify();
|
||
|
final name = settingEntries.getValue('name') as String;
|
||
|
final currentAmount =
|
||
|
settingEntries.getValue('currentAmount') as int;
|
||
|
final targetGoal =
|
||
|
(settingEntries.getValue('targetGoal') as double).round();
|
||
|
final button1Amount =
|
||
|
settingEntries.getValue('button1Amount') as int;
|
||
|
final button2Amount =
|
||
|
settingEntries.getValue('button2Amount') as int;
|
||
|
|
||
|
widget.item = widget.item.copyWith(
|
||
|
name: name,
|
||
|
current: currentAmount,
|
||
|
goal: targetGoal,
|
||
|
button1Amount: button1Amount,
|
||
|
button2Amount: button2Amount,
|
||
|
);
|
||
|
controller.edit(widget.item);
|
||
|
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,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
);
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Future<void> _showPopupCustomAmount() async {
|
||
|
final customFieldController = TextEditingController();
|
||
|
|
||
|
return showDialog<void>(
|
||
|
context: context,
|
||
|
barrierDismissible: false,
|
||
|
builder: (BuildContext context) {
|
||
|
return AlertDialog(
|
||
|
scrollable: true,
|
||
|
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
||
|
title: Text(
|
||
|
AppLocalizations.of(context)!.waterWidget_customAmountMessage,
|
||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||
|
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||
|
),
|
||
|
),
|
||
|
content: Column(
|
||
|
children: <Widget>[
|
||
|
TextField(
|
||
|
controller: customFieldController,
|
||
|
keyboardType: TextInputType.number,
|
||
|
autofocus: true,
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
actions: <Widget>[
|
||
|
ElevatedButton(
|
||
|
style: ElevatedButton.styleFrom(
|
||
|
shape: RoundedRectangleBorder(
|
||
|
borderRadius: BorderRadius.circular(12),
|
||
|
),
|
||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||
|
),
|
||
|
onPressed: () {
|
||
|
Navigator.of(context).pop();
|
||
|
},
|
||
|
child: Text(
|
||
|
AppLocalizations.of(context)!.widgetSettings_cancelButton,
|
||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
ElevatedButton(
|
||
|
style: ElevatedButton.styleFrom(
|
||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||
|
shape: RoundedRectangleBorder(
|
||
|
borderRadius: BorderRadius.circular(12),
|
||
|
),
|
||
|
),
|
||
|
onPressed: () {
|
||
|
Navigator.of(context).pop();
|
||
|
final result = int.tryParse(customFieldController.text) ?? 0;
|
||
|
_addQuantity(result);
|
||
|
},
|
||
|
child: Text(
|
||
|
AppLocalizations.of(context)!.widgetSettings_saveButton,
|
||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
);
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class OvershootLiquidLinearProgressIndicator extends StatelessWidget {
|
||
|
const OvershootLiquidLinearProgressIndicator({
|
||
|
required this.current,
|
||
|
required this.goal,
|
||
|
super.key,
|
||
|
});
|
||
|
|
||
|
final int current;
|
||
|
final int goal;
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
var mainColor = Colors.blue;
|
||
|
var backColor = Colors.white;
|
||
|
var value = 0.0;
|
||
|
if (current < goal) {
|
||
|
mainColor = Colors.blue;
|
||
|
backColor = Colors.white;
|
||
|
value = (current - goal * 0) / goal;
|
||
|
} else if (current < goal * 2) {
|
||
|
mainColor = Colors.orange;
|
||
|
backColor = Colors.blue;
|
||
|
value = (current - goal * 1) / goal;
|
||
|
} else {
|
||
|
mainColor = Colors.red;
|
||
|
backColor = Colors.orange;
|
||
|
value = (current - goal * 2) / goal;
|
||
|
}
|
||
|
return LiquidLinearProgressIndicator(
|
||
|
value: value,
|
||
|
valueColor: AlwaysStoppedAnimation(
|
||
|
mainColor,
|
||
|
),
|
||
|
backgroundColor: backColor,
|
||
|
borderColor: Colors.black,
|
||
|
borderWidth: 3,
|
||
|
borderRadius: 12,
|
||
|
direction: Axis.vertical,
|
||
|
);
|
||
|
}
|
||
|
}
|