333 lines
No EOL
9.7 KiB
Dart
333 lines
No EOL
9.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../formula_models.dart';
|
|
|
|
|
|
class FormulaWidget extends StatelessWidget {
|
|
final Formula formula;
|
|
final double fontSize;
|
|
final Color? textColor;
|
|
final Color? backgroundColor;
|
|
final EdgeInsets padding;
|
|
final bool showMagnitudes;
|
|
final bool showCode;
|
|
|
|
const FormulaWidget({
|
|
super.key,
|
|
required this.formula,
|
|
this.fontSize = 16.0,
|
|
this.textColor,
|
|
this.backgroundColor,
|
|
this.padding = const EdgeInsets.all(16.0),
|
|
this.showMagnitudes = true,
|
|
this.showCode = false,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Card(
|
|
color: backgroundColor,
|
|
child: Padding(
|
|
padding: padding,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
// Formula name
|
|
Text(
|
|
formula.name,
|
|
style: TextStyle(
|
|
fontSize: fontSize * 1.2,
|
|
fontWeight: FontWeight.bold,
|
|
color: textColor ?? Theme.of(context).textTheme.headlineSmall?.color,
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Formula equation
|
|
_buildFormulaEquation(context),
|
|
|
|
if (showMagnitudes) ...[
|
|
const SizedBox(height: 16),
|
|
_buildMagnitudesSection(context),
|
|
],
|
|
|
|
if (showCode) ...[
|
|
const SizedBox(height: 16),
|
|
_buildCodeSection(context),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildFormulaEquation(BuildContext context) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.surfaceContainerHighest.withOpacity(0.3),
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(
|
|
color: Theme.of(context).dividerColor,
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
// Output variable
|
|
_buildVariableChip(formula.output, isOutput: true, context: context),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
// Equals sign
|
|
Text(
|
|
'=',
|
|
style: TextStyle(
|
|
fontSize: fontSize * 1.5,
|
|
fontWeight: FontWeight.bold,
|
|
color: textColor ?? Theme.of(context).textTheme.bodyLarge?.color,
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
// Function notation
|
|
Text(
|
|
'${formula.name}(',
|
|
style: TextStyle(
|
|
fontSize: fontSize,
|
|
fontStyle: FontStyle.italic,
|
|
color: textColor ?? Theme.of(context).textTheme.bodyLarge?.color,
|
|
),
|
|
),
|
|
|
|
// Input variables
|
|
Expanded(
|
|
child: Wrap(
|
|
spacing: 8,
|
|
runSpacing: 4,
|
|
children: [
|
|
for (int i = 0; i < formula.input.length; i++) ...[
|
|
_buildVariableChip(formula.input[i], context: context),
|
|
if (i < formula.input.length - 1)
|
|
Text(
|
|
',',
|
|
style: TextStyle(
|
|
fontSize: fontSize,
|
|
color: textColor ?? Theme.of(context).textTheme.bodyLarge?.color,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
|
|
Text(
|
|
')',
|
|
style: TextStyle(
|
|
fontSize: fontSize,
|
|
fontStyle: FontStyle.italic,
|
|
color: textColor ?? Theme.of(context).textTheme.bodyLarge?.color,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildVariableChip(VariableSpec variable, {bool isOutput = false, required BuildContext context}) {
|
|
final bool hasMagnitude = variable.magnitude != VariableSpec.MAGNITUDELESS;
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: isOutput
|
|
? Theme.of(context).colorScheme.primary.withOpacity(0.1)
|
|
: Theme.of(context).colorScheme.secondary.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(6),
|
|
border: Border.all(
|
|
color: isOutput
|
|
? Theme.of(context).colorScheme.primary
|
|
: Theme.of(context).colorScheme.secondary,
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
variable.name,
|
|
style: TextStyle(
|
|
fontSize: fontSize * 0.9,
|
|
fontWeight: FontWeight.w600,
|
|
color: isOutput
|
|
? Theme.of(context).colorScheme.primary
|
|
: Theme.of(context).colorScheme.secondary,
|
|
),
|
|
),
|
|
if (hasMagnitude && showMagnitudes) ...[
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
'[${variable.magnitude}]',
|
|
style: TextStyle(
|
|
fontSize: fontSize * 0.8,
|
|
fontStyle: FontStyle.italic,
|
|
color: (isOutput
|
|
? Theme.of(context).colorScheme.primary
|
|
: Theme.of(context).colorScheme.secondary).withOpacity(0.7),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMagnitudesSection(BuildContext context) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Variables:',
|
|
style: TextStyle(
|
|
fontSize: fontSize * 0.9,
|
|
fontWeight: FontWeight.w600,
|
|
color: textColor ?? Theme.of(context).textTheme.titleSmall?.color,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Wrap(
|
|
spacing: 12,
|
|
runSpacing: 6,
|
|
children: [
|
|
...formula.input.map((variable) => _buildVariableInfo(variable, context)),
|
|
_buildVariableInfo(formula.output, context, isOutput: true),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildVariableInfo(VariableSpec variable, BuildContext context, {bool isOutput = false}) {
|
|
return Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
isOutput ? Icons.arrow_forward : Icons.input,
|
|
size: fontSize * 0.8,
|
|
color: isOutput
|
|
? Theme.of(context).colorScheme.primary
|
|
: Theme.of(context).colorScheme.secondary,
|
|
),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
'${variable.name}: ',
|
|
style: TextStyle(
|
|
fontSize: fontSize * 0.85,
|
|
fontWeight: FontWeight.w500,
|
|
color: textColor ?? Theme.of(context).textTheme.bodyMedium?.color,
|
|
),
|
|
),
|
|
Text(
|
|
variable.magnitude == VariableSpec.MAGNITUDELESS ? 'dimensionless' : variable.magnitude,
|
|
style: TextStyle(
|
|
fontSize: fontSize * 0.85,
|
|
fontStyle: FontStyle.italic,
|
|
color: (textColor ?? Theme.of(context).textTheme.bodyMedium?.color)?.withOpacity(0.8),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildCodeSection(BuildContext context) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Implementation:',
|
|
style: TextStyle(
|
|
fontSize: fontSize * 0.9,
|
|
fontWeight: FontWeight.w600,
|
|
color: textColor ?? Theme.of(context).textTheme.titleSmall?.color,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.surfaceContainerHighest.withOpacity(0.5),
|
|
borderRadius: BorderRadius.circular(6),
|
|
),
|
|
child: Text(
|
|
formula.d4rtCode,
|
|
style: TextStyle(
|
|
fontSize: fontSize * 0.8,
|
|
fontFamily: 'monospace',
|
|
color: textColor ?? Theme.of(context).textTheme.bodySmall?.color,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
// Example usage and demo
|
|
class FormulaDisplayDemo extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// Create sample formulas
|
|
final sampleFormulas = [
|
|
Formula(
|
|
name: 'calculateArea',
|
|
input: [
|
|
VariableSpec(name: 'length', magnitude: 'meters'),
|
|
VariableSpec(name: 'width', magnitude: 'meters'),
|
|
],
|
|
output: VariableSpec(name: 'area', magnitude: 'square_meters'),
|
|
d4rtCode: 'return length * width;',
|
|
),
|
|
Formula(
|
|
name: 'pythagoras',
|
|
input: [
|
|
VariableSpec(name: 'a', magnitude: 'meters'),
|
|
VariableSpec(name: 'b', magnitude: 'meters'),
|
|
],
|
|
output: VariableSpec(name: 'c', magnitude: 'meters'),
|
|
d4rtCode: 'return sqrt(a * a + b * b);',
|
|
),
|
|
Formula(
|
|
name: 'normalize',
|
|
input: [
|
|
VariableSpec(name: 'value', magnitude: VariableSpec.MAGNITUDELESS),
|
|
VariableSpec(name: 'max', magnitude: VariableSpec.MAGNITUDELESS),
|
|
],
|
|
output: VariableSpec(name: 'normalized', magnitude: VariableSpec.MAGNITUDELESS),
|
|
d4rtCode: 'return value / max;',
|
|
),
|
|
];
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Formula Display Demo'),
|
|
),
|
|
body: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
children: [
|
|
for (final formula in sampleFormulas) ...[
|
|
FormulaWidget(
|
|
formula: formula,
|
|
showCode: true,
|
|
),
|
|
const SizedBox(height: 16),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |