Commit 27bed8c
Changed files (6)
lib
components
dialoges
l10n
model
export_import
screens
subsettings
lib/components/dialoges/add_export_column_dialoge.dart
@@ -0,0 +1,147 @@
+import 'package:blood_pressure_app/components/measurement_list/measurement_list_entry.dart';
+import 'package:blood_pressure_app/model/blood_pressure.dart';
+import 'package:blood_pressure_app/model/export_import/column.dart';
+import 'package:blood_pressure_app/screens/subsettings/export_import/export_field_format_documentation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+
+class AddExportColumnDialoge extends StatefulWidget {
+
+ const AddExportColumnDialoge({super.key, this.initialColumn});
+
+ final ExportColumn? initialColumn;
+
+ @override
+ State<AddExportColumnDialoge> createState() => _AddExportColumnDialogeState();
+}
+
+class _AddExportColumnDialogeState extends State<AddExportColumnDialoge> {
+ final formKey = GlobalKey<FormState>();
+
+ /// Csv column title used to compute remaining titles.
+ late String csvTitle;
+
+ /// Pattern to
+ late String formatPattern;
+
+
+ @override
+ void initState() {
+ super.initState();
+ csvTitle = widget.initialColumn?.csvTitle ?? '';
+ formatPattern = widget.initialColumn?.formatPattern ?? '';
+ }
+
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final localizations = AppLocalizations.of(context)!;
+ return Scaffold(
+ appBar: AppBar(
+ forceMaterialTransparency: true,
+ leading: IconButton(
+ onPressed: () => Navigator.of(context).pop(null),
+ icon: const Icon(Icons.close)
+ ),
+ actions: [
+ TextButton(
+ onPressed: () {
+ if (formKey.currentState?.validate() ?? false) {
+ formKey.currentState!.save();
+ final column = UserColumn(formatPattern, csvTitle, formatPattern);
+ // TODO: validate internalIdentifier doesn't exist and find append index ?
+ Navigator.pop(context, column);
+ }
+ },
+ child: Text(localizations.btnSave)
+ )
+ ],
+ ),
+ body: Form(
+ key: formKey,
+ child: ListView(
+ children: [
+ TextFormField(
+ initialValue: csvTitle,
+ decoration: getInputDecoration(localizations.csvTitle),
+ validator: (value) => (value != null && value.isNotEmpty) ? null : localizations.errNoValue,
+ onSaved: (value) => setState(() {csvTitle = value!;}),
+ ),
+ const SizedBox(height: 8,),
+ TextFormField(
+ initialValue: formatPattern,
+ onChanged: (value) => setState(() {
+ formatPattern = value;
+ }),
+ decoration: getInputDecoration(localizations.fieldFormat).copyWith(
+ suffixIcon: IconButton(
+ onPressed: () {
+ Navigator.of(context).push(MaterialPageRoute(
+ builder: (context) => InformationScreen(text: localizations.exportFieldFormatDocumentation)));
+ },
+ icon: const Icon(Icons.info_outline)
+ ),
+ ),
+ validator: (value) => (value != null && value.isNotEmpty) ? null : localizations.errNoValue,
+ onSaved: (value) => setState(() {formatPattern = value!;}),
+ ),
+ const SizedBox(height: 8,),
+ Container(
+ padding: const EdgeInsets.all(24),
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surface,
+ borderRadius: BorderRadius.circular(20)
+ ),
+ child: (){
+ final record = BloodPressureRecord(DateTime.now(), 123, 78, 65, 'test note');
+ final column = UserColumn('', '', formatPattern);
+ String? text = column.encode(record);
+ final decoded = column.decode(text);
+ if (text.isEmpty) text = null;
+ return Column(
+ children: [
+ MeasurementListRow(record: record,),
+ const SizedBox(height: 8,),
+ const Icon(Icons.arrow_downward),
+ const SizedBox(height: 8,),
+ (text != null) ? Text(text) :
+ Text(localizations.errNoValue, style: const TextStyle(fontStyle: FontStyle.italic),),
+ const SizedBox(height: 8,),
+ const Icon(Icons.arrow_downward),
+ const SizedBox(height: 8,),
+ Text(decoded.toString())
+ ],
+ );
+ }()
+ ),
+ ],
+ ),
+
+ ),
+ );
+ }
+
+ InputDecoration getInputDecoration(String? labelText) {
+ final border = OutlineInputBorder(
+ borderSide: BorderSide(
+ width: 3,
+ color: Theme.of(context).primaryColor,
+ ),
+ borderRadius: BorderRadius.circular(20)
+ );
+ return InputDecoration(
+ hintText: labelText,
+ labelText: labelText,
+ errorMaxLines: 5,
+ border: border,
+ enabledBorder: border,
+ );
+ }
+}
+
+// TODO: showdialoge function and use in manager
\ No newline at end of file
lib/l10n/app_en.arb
@@ -478,5 +478,7 @@
"manageExportColumns": "Manage export columns",
"@manageExportColumns": {},
"buildIn": "Build-in",
- "@buildIn": {}
+ "@buildIn": {},
+ "csvTitle": "CSV-title",
+ "@csvTitle": {}
}
lib/model/export_import/column.dart
@@ -217,17 +217,21 @@ class BuildInColumn extends ExportColumn {
// TODO: add class for formattedTimestamp
-/// Class for storing export behavior of columns.
-///
-/// In most cases using the sealed
+/// Class for storing data of user added columns.
class UserColumn extends ExportColumn {
/// Create a object that handles export behavior for data in a column.
///
- /// [formatter] will be created according to [formatString].
- UserColumn(this.internalIdentifier, this.csvTitle, String formatString):
- formatter = ScriptedFormatter(formatString);
+ /// [formatter] will be created according to [formatPattern].
+ ///
+ /// [internalIdentifier] is automatically prefixed with 'userColumn.' during object creation.
+ UserColumn(String internalIdentifier, this.csvTitle, String formatPattern):
+ formatter = ScriptedFormatter(formatPattern),
+ internalIdentifier = 'userColumn.$internalIdentifier';
@override
+ /// Unique identifier of userColumn.
+ ///
+ /// Is automatically be prefixed with `userColumn.` to avoid name collisions with build-ins.
final String internalIdentifier;
@override
lib/screens/subsettings/export_import/export_column_data.dart
@@ -1,197 +0,0 @@
-/* TODO: rewrite
-
-import 'package:blood_pressure_app/components/consistent_future_builder.dart';
-import 'package:blood_pressure_app/model/blood_pressure.dart';
-import 'package:blood_pressure_app/model/export_import/import_field_type.dart';
-import 'package:blood_pressure_app/model/export_options.dart';
-import 'package:blood_pressure_app/screens/subsettings/export_import/export_field_format_documentation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-
-class EditExportColumnPage extends StatefulWidget {
- final String? initialInternalName;
- final String? initialDisplayName;
- final String? initialFormatPattern;
- final bool editable;
-
- const EditExportColumnPage({super.key, this.initialDisplayName, this.initialInternalName,
- this.initialFormatPattern, this.editable = true});
-
- @override
- State<EditExportColumnPage> createState() => _EditExportColumnPageState();
-}
-
-class _EditExportColumnPageState extends State<EditExportColumnPage> {
- final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
- String? _internalName;
- String? _displayName;
- String? _formatPattern;
- bool _editedInternalName = false;
- var _internalNameKeyNr = 0;
-
-
- @override
- void initState() {
- super.initState();
- _internalName = widget.initialInternalName;
- _displayName = widget.initialDisplayName;
- _formatPattern= widget.initialFormatPattern;
- }
-
- _EditExportColumnPageState();
-
- @override
- Widget build(BuildContext context) {
- final localizations = AppLocalizations.of(context)!;
- return ConsistentFutureBuilder(
- future: ExportConfigurationModel.get(localizations),
- lastChildWhileWaiting: true,
- onData: (BuildContext context, ExportConfigurationModel exportConfigurationModel) => Scaffold(
- body: Center(
- child: Padding(
- padding: const EdgeInsets.all(60.0),
- child: Form(
- key: _formKey,
- child: SingleChildScrollView(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- if (!widget.editable)
- Text(localizations.errCantEditThis),
- Opacity(
- opacity: widget.editable ? 1 : 0.7,
- child: IgnorePointer(
- ignoring: !widget.editable,
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- TextFormField(
- key: const Key('displayName'),
- initialValue: _displayName,
- decoration: InputDecoration(label: Text(localizations.displayTitle)),
- onChanged: (String? value) {
- if (value != null && value.isNotEmpty) {
- setState(() {
- _displayName = value;
- });
- if (_editedInternalName || (widget.initialInternalName != null)) return;
- final asciiName = value.replaceAll(RegExp(r'[^A-Za-z0-9 ]'), '');
- final internalName = asciiName.replaceAllMapped(RegExp(r' (.)'), (match) {
- return match.group(1)!.toUpperCase();
- }).replaceAll(' ', '');
- setState(() {
- _internalNameKeyNr++;
- _internalName = internalName;
- });
- }
- },
- ),
- TextFormField(
- key: Key('internalName$_internalNameKeyNr'), // it should update when display name is changed without unfocussing on edit
- initialValue: _internalName,
- decoration: InputDecoration(label: Text(localizations.internalName)),
- enabled: (widget.initialInternalName == null),
- validator: (String? value) {
- if (value == null || value.isEmpty || RegExp(r'[^A-Za-z0-9]').hasMatch(value)) {
- return localizations.errOnlyLatinCharactersAndArabicNumbers;
- }
- if (exportConfigurationModel.availableFormatsMap.keys.contains(value)) {
- return localizations.errExportColumnWithThisNameAlreadyExists;
- }
- return null;
- },
- onChanged: (String? value) {
- if (value != null && value.isNotEmpty && !RegExp(r'[^A-Za-z0-9]').hasMatch(value)) {
- setState(() {
- _internalName = value;
- _editedInternalName = true;
- });
- }
- },
- ),
- TextFormField(
- key: const Key('formatPattern'),
- initialValue: _formatPattern,
- decoration: InputDecoration(
- label: Text(localizations.fieldFormat),
- suffixIcon: IconButton(
- onPressed: () {
- Navigator.of(context).push(MaterialPageRoute(
- builder: (context) => InformationScreen(text: localizations.exportFieldFormatDocumentation)));
- },
- icon: const Icon(Icons.info_outline)
- ),
- ),
- maxLines: 6,
- minLines: 1,
- validator: (String? value) {
- if (value == null || value.isEmpty) {
- return localizations.errNoValue;
- } else if (_internalName != null && _displayName != null) {
- try {
- final column = LegacyExportColumn(internalName: _internalName!, columnTitle: _displayName!, formatPattern: value);
- column.formatRecord(BloodPressureRecord(DateTime.now(), 100, 80, 60, ''));
- _formatPattern = value;
- } catch (e) {
- _formatPattern = null;
- return e.toString();
- }
- }
- return null;
- },
- onChanged: (value) => setState(() {_formatPattern = value;}),
- ),
- const SizedBox(height: 12,),
- Text(localizations.result),
- Text(((){try {
- final column = LegacyExportColumn(internalName: _internalName!, columnTitle: _displayName!, formatPattern: _formatPattern!);
- return column.formatRecord(BloodPressureRecord(DateTime.now(), 100, 80, 60, 'test'));
- } catch (e) {
- return '-';
- }})()),
- const SizedBox(height: 24,),
- ],
- ),
- ),
- ),
- Row(
- children: [
- TextButton(
- key: const Key('btnCancel'),
- onPressed: () {
- Navigator.of(context).pop();
- },
-
- child: Text(localizations.btnCancel)
- ),
- const Spacer(),
- FilledButton.icon(
- key: const Key('btnSave'),
- icon: const Icon(Icons.save),
- label: Text(localizations.btnSave),
- onPressed: (widget.editable) ? (() async {
- if (_formKey.currentState?.validate() ?? false) {
- final navigator = Navigator.of(context);
- exportConfigurationModel.addOrUpdate(LegacyExportColumn(
- internalName: _internalName!,
- columnTitle: _displayName!,
- formatPattern: _formatPattern!
- ));
- navigator.pop();
- }
- }) : null,
- )
- ],
- )
- ],
- ),
- ),
- ),
- ),
- )
- ),
- );
- }
-}
-
- */
\ No newline at end of file
lib/screens/subsettings/export_import/export_column_management_screen.dart
@@ -1,3 +1,4 @@
+import 'package:blood_pressure_app/components/dialoges/add_export_column_dialoge.dart';
import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -46,6 +47,7 @@ class ExportColumnsManagementScreen extends StatelessWidget {
leading: const Icon(Icons.add),
title: Text(localizations.addExportformat),
onTap: () {
+ Navigator.push(context, MaterialPageRoute(builder: (context) => AddExportColumnDialoge()));
// TODO: reimplement tile adding
},
)
lib/screens/subsettings/export_import/export_field_format_documentation.dart
@@ -14,7 +14,7 @@ class InformationScreen extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
- backgroundColor: Theme.of(context).primaryColor,
+ forceMaterialTransparency: true,
),
body: Container(
padding: const EdgeInsets.all(10),