habitrack/app/lib/function_widgets/widget_settings_menu/setting_entry.dart

376 lines
9.5 KiB
Dart
Raw Normal View History

2024-08-26 00:34:20 +02:00
import 'package:duration_picker/duration_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:habitrack_app/main.dart';
import 'package:intl/intl.dart';
abstract class SettingEntry extends StatefulWidget {
const SettingEntry({required this.name, super.key});
final String name;
@override
State<StatefulWidget> createState() => _SettingEntryState();
dynamic getValue() {
throw UnimplementedError();
}
void setValue(dynamic newValue) {
throw UnimplementedError();
}
}
class _SettingEntryState extends State<SettingEntry> {
@override
Widget build(BuildContext context) {
return const Text('Abstract, not implemented');
}
}
// ###############################TEXT#########################################
class SettingEntryText extends SettingEntry {
SettingEntryText({
required super.name,
this.defaultValue = 'Some Text',
super.key,
}) {
setValue(defaultValue);
}
final TextEditingController valueController = TextEditingController();
final String defaultValue;
@override
State<SettingEntryText> createState() => _SettingEntryTextState();
@override
String getValue() {
logger.i('GETTING VALUE ${valueController.text}');
return valueController.text;
}
@override
void setValue(dynamic newValue) {
if (newValue is! String) {
throw Exception('Value of SettingEntryText can only be a String!');
}
valueController.text = newValue;
}
}
class _SettingEntryTextState extends State<SettingEntryText> {
void monitorTextChange() {
final text = widget.valueController.text;
logger.i('TEXT: $text');
}
@override
void initState() {
super.initState();
// Start listening to changes.
widget.valueController.addListener(monitorTextChange);
}
@override
void dispose() {
// Clean up the controller when the widget is removed from the widget tree.
// This also removes the _printLatestValue listener.
widget.valueController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
alignment: AlignmentDirectional.bottomStart,
margin: const EdgeInsets.only(top: 7.5, bottom: 7.5),
// color: Colors.black,
child: Text(
widget.name,
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
TextField(
keyboardType: TextInputType.text,
controller: widget.valueController,
),
],
);
}
}
// ###############################NUMERIC#######################################
class SettingEntryNumeric extends SettingEntry {
SettingEntryNumeric({required super.name, int defaultValue = 0, super.key}) {
setValue(defaultValue);
}
final TextEditingController valueController = TextEditingController();
@override
State<SettingEntryNumeric> createState() => _SettingEntryNumericState();
@override
int getValue() {
return int.parse(valueController.text);
}
@override
void setValue(dynamic newValue) {
if (newValue is! int) {
throw Exception('Value of SettingEntryNumeric can only be an integer!');
}
valueController.text = newValue.toString();
}
}
class _SettingEntryNumericState extends State<SettingEntryNumeric> {
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(widget.name),
TextField(
keyboardType: TextInputType.number,
controller: widget.valueController,
),
],
);
}
}
// ###############################SLIDER########################################
class SettingEntrySlider extends SettingEntry {
SettingEntrySlider({
required this.topValue,
required this.divisions,
required super.name,
double defaultValue = 0.0,
super.key,
}) {
setValue(defaultValue);
}
final double topValue;
final int divisions;
final value = DoubleSaver();
@override
State<SettingEntrySlider> createState() => _SettingEntrySliderState();
@override
double getValue() {
return value.v;
}
@override
void setValue(dynamic newValue) {
if (newValue is! double) {
throw Exception('Value of SettingEntrySlider can only be a Double!');
}
value.v = newValue;
}
}
class DoubleSaver {
double v = 0;
}
class _SettingEntrySliderState extends State<SettingEntrySlider> {
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
//Expanded(flex: 2, child: Text(widget.name)),
Text(widget.name),
Text(' ${widget.getValue()}'),
],
),
Row(
children: [
const Text('0 '),
Expanded(
child: Slider(
value: widget.getValue(),
divisions: widget.divisions,
max: widget.topValue,
onChanged: sliderChange,
),
),
Text(' ${widget.topValue}'),
],
),
],
);
}
void sliderChange(double newValue) {
setState(() {
widget.setValue(newValue);
});
}
}
// #############################DURATION#######################################
class IntSaver {
IntSaver({this.v = 30});
int v;
}
class SettingEntryDuration extends SettingEntry {
SettingEntryDuration({
required super.name,
required this.defaultValue,
super.key,
}) {
val = IntSaver(v: defaultValue);
}
final int defaultValue;
late final IntSaver val;
@override
State<SettingEntryDuration> createState() => _SettingEntryDurationState();
@override
int getValue() {
return val.v;
}
@override
void setValue(dynamic newValue) {
if (newValue is! int) {
throw Exception('Value of SettingEntryDuration can only be a Double!');
}
val.v = newValue;
}
}
class _SettingEntryDurationState extends State<SettingEntryDuration> {
@override
Widget build(BuildContext context) {
final textCurrent =
AppLocalizations.of(context)!.widgetSettings_durationPickerCurrent;
return Column(
children: [
Container(
alignment: AlignmentDirectional.bottomStart,
margin: const EdgeInsets.only(top: 7.5, bottom: 7.5),
child: Text(
'$textCurrent ${widget.getValue()}m',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
),
onPressed: () async {
var resultingDuration = await showDurationPicker(
context: context,
initialTime: Duration(minutes: widget.val.v),
);
resultingDuration ??= Duration(minutes: widget.defaultValue);
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Chose duration: ${resultingDuration.inMinutes}'),
),
);
widget.setValue(resultingDuration.inMinutes);
setState(() {});
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_durationPickerButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
);
}
}
// ###############################DATE#########################################
class DateSaver {
DateTime v = DateTime.now();
}
class SettingEntryDate extends SettingEntry {
SettingEntryDate({required super.name, DateTime? defaultValue, super.key}) {
this.defaultValue = defaultValue ?? DateTime.now();
setValue(defaultValue);
}
final date = DateSaver();
late final DateTime defaultValue;
@override
State<SettingEntryDate> createState() => _SettingEntryDateState();
@override
DateTime getValue() {
return date.v;
}
@override
void setValue(dynamic newValue) {
if (newValue is! DateTime) {
throw Exception('Value of SettingEntryNumeric can only be a DateTime!');
}
date.v = newValue;
}
}
class _SettingEntryDateState extends State<SettingEntryDate> {
@override
Widget build(BuildContext context) {
final dateFormat = DateFormat('dd. MMMM');
return Column(
children: [
Text(
'${widget.name}: ${dateFormat.format(widget.date.v)} ',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.primaryContainer,
),
),
TextButton(
onPressed: datePicker,
child: const Text('Choose a date'),
),
],
);
}
Future<void> datePicker() async {
final pickedDate = await showDatePicker(
context: context,
initialDate: widget.date.v, //get today's date
firstDate: DateTime.now(),
//DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2101),
);
final cleanDate = pickedDate ?? widget.date.v;
widget.setValue(cleanDate);
setState(() {});
}
}