Solved saving on formula edit, but problem remain in initial formula list

This commit is contained in:
Álvaro González 2026-03-08 10:59:20 +01:00
parent 2b3fbffd48
commit 1522a4143b
3 changed files with 107 additions and 79 deletions

View file

@ -172,6 +172,7 @@ class _FormulaEditorState extends State<FormulaEditor> {
); );
return Formula( return Formula(
uuid: widget.formula.uuid,
name: _nameController.text.trim(), name: _nameController.text.trim(),
description: _descriptionController.text.isEmpty ? null : _descriptionController.text, description: _descriptionController.text.isEmpty ? null : _descriptionController.text,
input: input, input: input,

View file

@ -12,10 +12,10 @@ import 'unit_dropdown.dart';
import 'formula_editor.dart'; import 'formula_editor.dart';
class FormulaScreen extends StatefulWidget { class FormulaScreen extends StatefulWidget {
Formula formula; final Formula initialformula;
final Corpus corpus; final Corpus corpus;
FormulaScreen({super.key, required this.formula, required this.corpus}); FormulaScreen({super.key, required formula, required this.corpus}) : initialformula = formula;
@override @override
State<FormulaScreen> createState() => _FormulaScreenState(); State<FormulaScreen> createState() => _FormulaScreenState();
@ -30,12 +30,15 @@ class _FormulaScreenState extends State<FormulaScreen> {
String? _result; String? _result;
String? _selectedOutputUnit; String? _selectedOutputUnit;
bool _isDescriptionExpanded = false; // Track description expansion state bool _isDescriptionExpanded = false; // Track description expansion state
late Formula _formula;
Formula get formula => _formula;
set formula(Formula newFormula) {
_formula = newFormula;
@override
void initState() {
super.initState();
// Initialize controllers and units with listeners // Initialize controllers and units with listeners
for (final input in widget.formula.input) { for (final input in formula.input) {
_selectedUnits[input.name] = input.unit; _selectedUnits[input.name] = input.unit;
if (input.values != null && input.values!.isNotEmpty) { if (input.values != null && input.values!.isNotEmpty) {
// string/categorical variable -> use dropdown // string/categorical variable -> use dropdown
@ -46,7 +49,13 @@ class _FormulaScreenState extends State<FormulaScreen> {
_inputControllers[input.name]!.addListener(_evaluateFormula); _inputControllers[input.name]!.addListener(_evaluateFormula);
} }
} }
_selectedOutputUnit = widget.formula.output.unit; _selectedOutputUnit = formula.output.unit;
}
@override
void initState() {
super.initState();
formula = widget.initialformula;
} }
@override @override
@ -62,7 +71,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
void _evaluateFormula() { void _evaluateFormula() {
try { try {
final inputValues = <String, dynamic>{}; final inputValues = <String, dynamic>{};
for (final input in widget.formula.input) { for (final input in formula.input) {
// string/categorical variable // string/categorical variable
if (input.values != null && input.values!.isNotEmpty) { if (input.values != null && input.values!.isNotEmpty) {
final selected = _selectedValues[input.name]; final selected = _selectedValues[input.name];
@ -105,10 +114,10 @@ class _FormulaScreenState extends State<FormulaScreen> {
} }
final evaluator = FormulaEvaluator(); final evaluator = FormulaEvaluator();
final result = evaluator.evaluate(widget.formula, inputValues); final result = evaluator.evaluate(formula, inputValues);
// Convert output to selected unit if needed // Convert output to selected unit if needed
String? unit = widget.formula.output.unit; String? unit = formula.output.unit;
if (unit != null && result is Number) { if (unit != null && result is Number) {
final converted = widget.corpus.convert(result, unit, _selectedOutputUnit!); final converted = widget.corpus.convert(result, unit, _selectedOutputUnit!);
if (converted is num) { if (converted is num) {
@ -126,7 +135,10 @@ class _FormulaScreenState extends State<FormulaScreen> {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Error: ${e.toString()}'), content: Text('Error: ${e.toString()}'),
backgroundColor: Theme.of(context).colorScheme.error, backgroundColor: Theme
.of(context)
.colorScheme
.error,
), ),
); );
} }
@ -136,7 +148,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(widget.formula.name), title: Text(formula.name),
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.edit), icon: const Icon(Icons.edit),
@ -144,17 +156,18 @@ class _FormulaScreenState extends State<FormulaScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => FormulaEditor( builder: (context) =>
formula: widget.formula, FormulaEditor(
corpus: widget.corpus, formula: formula,
onSave: (updatedFormula) { corpus: widget.corpus,
// Refresh the screen after saving onSave: (updatedFormula) {
setState(() { // Refresh the screen after saving
// The corpus has been updated, refresh the displayed formula setState(() {
widget.formula = updatedFormula; // The corpus has been updated, refresh the displayed formula
}); formula = updatedFormula;
}, });
), },
),
), ),
); );
}, },
@ -180,8 +193,8 @@ class _FormulaScreenState extends State<FormulaScreen> {
} }
Widget _buildDescriptionSection() { Widget _buildDescriptionSection() {
if (widget.formula.description == null || if (formula.description == null ||
widget.formula.description!.isEmpty) { formula.description!.isEmpty) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@ -190,7 +203,11 @@ class _FormulaScreenState extends State<FormulaScreen> {
child: ExpansionTile( child: ExpansionTile(
title: Text( title: Text(
'Description', 'Description',
style: Theme.of(context).textTheme.titleMedium?.copyWith( style: Theme
.of(context)
.textTheme
.titleMedium
?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@ -206,11 +223,14 @@ class _FormulaScreenState extends State<FormulaScreen> {
child: Container( child: Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceVariant, color: Theme
.of(context)
.colorScheme
.surfaceVariant,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Markdown( child: Markdown(
data: widget.formula.description!, data: formula.description!,
shrinkWrap: true, shrinkWrap: true,
builders: { builders: {
'latex': LatexElementBuilder(), 'latex': LatexElementBuilder(),
@ -233,12 +253,16 @@ class _FormulaScreenState extends State<FormulaScreen> {
children: [ children: [
Text( Text(
'Input Variables', 'Input Variables',
style: Theme.of( style: Theme
.of(
context, context,
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), )
.textTheme
.titleMedium
?.copyWith(fontWeight: FontWeight.bold),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
...widget.formula.input.map((variable) => _buildVariableRow(variable)), ...formula.input.map((variable) => _buildVariableRow(variable)),
], ],
); );
} }
@ -249,9 +273,13 @@ class _FormulaScreenState extends State<FormulaScreen> {
children: [ children: [
Text( Text(
'Result', 'Result',
style: Theme.of( style: Theme
.of(
context, context,
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), )
.textTheme
.titleMedium
?.copyWith(fontWeight: FontWeight.bold),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
@ -260,7 +288,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
SizedBox( SizedBox(
width: 150, width: 150,
child: Text( child: Text(
widget.formula.output.name, formula.output.name,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
@ -280,14 +308,13 @@ class _FormulaScreenState extends State<FormulaScreen> {
const SizedBox(width: 8), const SizedBox(width: 8),
UnitDropdown( UnitDropdown(
corpus: widget.corpus, corpus: widget.corpus,
variable: widget.formula.output, variable: formula.output,
selectedUnit: _selectedOutputUnit, selectedUnit: _selectedOutputUnit,
onUnitChanged: (unit) { onUnitChanged: (unit) {
_selectedOutputUnit = unit; _selectedOutputUnit = unit;
_evaluateFormula(); _evaluateFormula();
print( "En output unit changed to $unit: $_result"); print("En output unit changed to $unit: $_result");
setState(() { setState(() {});
});
}, },
), ),
], ],
@ -314,43 +341,42 @@ class _FormulaScreenState extends State<FormulaScreen> {
const SizedBox(width: 8), // Add some spacing const SizedBox(width: 8), // Add some spacing
// Flexible space for input field // Flexible space for input field
Expanded( Expanded(
child: isCategorical child: isCategorical
? DropdownButtonFormField<String>( ? DropdownButtonFormField<String>(
value: _selectedValues[variable.name], value: _selectedValues[variable.name],
items: variable.values! items: variable.values!
.map((v) => DropdownMenuItem<String>(value: v, child: Text(v))) .map((v) => DropdownMenuItem<String>(value: v, child: Text(v)))
.toList(), .toList(),
onChanged: (v) { onChanged: (v) {
_selectedValues[variable.name] = v; _selectedValues[variable.name] = v;
_evaluateFormula(); _evaluateFormula();
setState(() { setState(() {});
}); },
}, decoration: const InputDecoration(
decoration: const InputDecoration( border: UnderlineInputBorder(),
border: UnderlineInputBorder(), ),
), validator: (value) {
validator: (value) { if (value == null || value.isEmpty) return 'Required';
if (value == null || value.isEmpty) return 'Required'; return null;
return null; },
}, )
) : TextFormField(
: TextFormField( controller: _inputControllers[variable.name],
controller: _inputControllers[variable.name], keyboardType: TextInputType.number,
keyboardType: TextInputType.number, inputFormatters: [
inputFormatters: [ //FilteringTextInputFormatter.allow(RegExp(r'[0-9\.\-]')),
//FilteringTextInputFormatter.allow(RegExp(r'[0-9\.\-]')), ],
], decoration: const InputDecoration(
decoration: const InputDecoration( border: UnderlineInputBorder(),
border: UnderlineInputBorder(), ),
), autovalidateMode: AutovalidateMode.always,
autovalidateMode: AutovalidateMode.always, validator: (value) {
validator: (value) { if (value == null || value.isEmpty) {
if (value == null || value.isEmpty) { return 'Required';
return 'Required'; }
} return _inputControllers[variable.name]!.lastError;
return _inputControllers[variable.name]!.lastError; },
}, ),
),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
if (variable.unit != null) if (variable.unit != null)

View file

@ -76,18 +76,19 @@ class Corpus{
/// Updates a formula in the corpus /// Updates a formula in the corpus
void updateFormula(Formula formula) { void updateFormula(Formula formula) {
if (!_allFormulas.containsKey(formula.name)) { if (!_allFormulas.containsKey(formula.uuid)) {
throw ArgumentError("Formula not found: ${formula.name}"); _allFormulas.keys.forEach( (uuid)=> print("Existing formula uuid: $uuid, name: ${_allFormulas[uuid]?.name}") );
throw ArgumentError("Formula not found: ${formula.name} ${formula.uuid}");
} }
// Remove old tags // Remove old tags
final oldFormula = _allFormulas[formula.name]!; final oldFormula = _allFormulas[formula.uuid]!;
for (final tag in oldFormula.tags) { for (final tag in oldFormula.tags) {
_tags[tag]?.removeWhere((f) => f.name == formula.name); _tags[tag]?.removeWhere((f) => f.name == formula.name);
} }
// Update the formula // Update the formula
_allFormulas[formula.name] = formula; _allFormulas[formula.uuid] = formula;
// Add new tags // Add new tags
for (final tag in formula.tags) { for (final tag in formula.tags) {