18 changed files with 1218 additions and 79 deletions
			
			
		| @ -0,0 +1,102 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| 
 | ||||
| enum CounterType { | ||||
|   coldWater(_coldWaterId, Colors.blue, Icons.water_drop), | ||||
|   hotWater(_hotWaterId, Colors.red, Icons.water_drop), | ||||
|   electrisity1rates(_electrisity1rateId, Colors.lightBlue, Icons.tungsten), | ||||
|   electrisity2rates(_electrisity2rateId, Colors.lightBlue, Icons.tungsten), | ||||
|   electrisity3rates(_electrisity3rateId, Colors.lightBlue, Icons.tungsten), | ||||
|   gas(_gasId, Colors.blueAccent, Icons.local_fire_department); | ||||
| 
 | ||||
|   const CounterType(this.id, this.color, this.icon); | ||||
| 
 | ||||
|   static const int _coldWaterId = 0; | ||||
|   static const int _hotWaterId = 1; | ||||
|   static const int _gasId = 2; | ||||
|   static const int _electrisity1rateId = 3; | ||||
|   static const int _electrisity2rateId = 4; | ||||
|   static const int _electrisity3rateId = 5; | ||||
|   final int id; | ||||
|   final Color color; | ||||
|   final IconData icon; | ||||
| 
 | ||||
|   String getLabel(BuildContext context) { | ||||
|     switch (id) { | ||||
|       case _coldWaterId: | ||||
|         return AppLocalizations.of(context)!.cold_water_type_str; | ||||
|       case _hotWaterId: | ||||
|         return AppLocalizations.of(context)!.hot_water_type_str; | ||||
|       case _electrisity1rateId: | ||||
|         return AppLocalizations.of(context)!.electrisity1_type_str; | ||||
|       case _electrisity2rateId: | ||||
|         return AppLocalizations.of(context)!.electrisity2_type_str; | ||||
|       case _electrisity3rateId: | ||||
|         return AppLocalizations.of(context)!.electrisity3_type_str; | ||||
|       case _gasId: | ||||
|         return AppLocalizations.of(context)!.gas_type_str; | ||||
|       default: | ||||
|     } | ||||
| 
 | ||||
|     return AppLocalizations.of(context)!.unknown_type_str; | ||||
|   } | ||||
| 
 | ||||
|   CounterUnits getUnits() { | ||||
|     if (id < _electrisity1rateId) { | ||||
|       return CounterUnits.m3; | ||||
|     } | ||||
| 
 | ||||
|     return CounterUnits.kVt; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| enum CounterUnits { | ||||
|   m3(0), | ||||
|   kVt(1); | ||||
| 
 | ||||
|   const CounterUnits(this.id); | ||||
|   final int id; | ||||
| 
 | ||||
|   String getLabel(BuildContext context) { | ||||
|     switch (id) { | ||||
|       case 0: | ||||
|         return AppLocalizations.of(context)!.m3; | ||||
|       case 1: | ||||
|         return AppLocalizations.of(context)!.kVt; | ||||
|     } | ||||
| 
 | ||||
|     return ''; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class Counter { | ||||
|   final int? id; | ||||
|   final int addressId; | ||||
|   final CounterType counterType; | ||||
|   final String name; | ||||
|   final double? value; | ||||
| 
 | ||||
|   Counter( | ||||
|       {this.id, | ||||
|       required this.addressId, | ||||
|       required this.counterType, | ||||
|       required this.name, | ||||
|       this.value}); | ||||
| 
 | ||||
|   factory Counter.fromMap(Map<String, dynamic> json) { | ||||
|     return Counter( | ||||
|         id: json["id"], | ||||
|         addressId: json["address_id"], | ||||
|         counterType: CounterType.values | ||||
|             .firstWhere((element) => element.id == json["counter_type"]), | ||||
|         name: json["name"], | ||||
|         value: json["value"].toDouble()); | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toMap() => { | ||||
|         "id": id, | ||||
|         "address_id": addressId, | ||||
|         "counter_type": counterType.id, | ||||
|         "name": name | ||||
|       }; | ||||
| } | ||||
| @ -0,0 +1,69 @@ | ||||
| import 'package:counters/counters.dart'; | ||||
| import 'package:counters/datbase.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| 
 | ||||
| class NewCounterPage extends StatelessWidget { | ||||
|   final int addressId; | ||||
|   final nameController = TextEditingController(); | ||||
|   CounterType counterType = CounterType.coldWater; | ||||
| 
 | ||||
|   NewCounterPage({super.key, required this.addressId}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|           backgroundColor: Theme.of(context).colorScheme.inversePrimary, | ||||
|           title: Text(AppLocalizations.of(context)!.new_counter_title)), | ||||
|       body: Center( | ||||
|         child: Padding( | ||||
|           padding: const EdgeInsets.all(8.0), | ||||
|           child: Column( | ||||
|               mainAxisAlignment: MainAxisAlignment.center, | ||||
|               crossAxisAlignment: CrossAxisAlignment.center, | ||||
|               children: [ | ||||
|                 DropdownMenu<CounterType>( | ||||
|                   enableFilter: true, | ||||
|                   hintText: | ||||
|                       AppLocalizations.of(context)!.choose_type_of_counter, | ||||
|                   expandedInsets: EdgeInsets.zero, | ||||
|                   dropdownMenuEntries: | ||||
|                       CounterType.values.map<DropdownMenuEntry<CounterType>>( | ||||
|                     (CounterType icon) { | ||||
|                       return DropdownMenuEntry<CounterType>( | ||||
|                           value: icon, | ||||
|                           labelWidget: Text(icon.getLabel(context)), | ||||
|                           label: icon.getLabel(context) | ||||
|                           //leadingIcon: Icon(icon.icon), | ||||
|                           ); | ||||
|                     }, | ||||
|                   ).toList(), | ||||
|                   onSelected: (value) => counterType = value!, | ||||
|                 ), | ||||
|                 const SizedBox(height: 50), | ||||
|                 TextField( | ||||
|                   controller: nameController, | ||||
|                   decoration: InputDecoration( | ||||
|                       hintStyle: const TextStyle(color: Colors.blue), | ||||
|                       hintText: | ||||
|                           AppLocalizations.of(context)!.enter_counter_name), | ||||
|                 ), | ||||
|                 const SizedBox(height: 50), | ||||
|                 TextButton( | ||||
|                     onPressed: () { | ||||
|                       DBProvider.db | ||||
|                           .newCounter(Counter( | ||||
|                               addressId: addressId, | ||||
|                               counterType: counterType, | ||||
|                               name: nameController.text)) | ||||
|                           .then((value) => Navigator.pop(context)); | ||||
|                     }, | ||||
|                     child: Text( | ||||
|                         AppLocalizations.of(context)!.add_new_address_button)) | ||||
|               ]), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,208 @@ | ||||
| import 'package:counters/counters.dart'; | ||||
| import 'package:counters/datbase.dart'; | ||||
| import 'package:counters/value.dart'; | ||||
| import 'package:datetime_picker_formfield/datetime_picker_formfield.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| import 'package:intl/intl.dart'; | ||||
| 
 | ||||
| class NewValuePage extends StatelessWidget { | ||||
|   NewValuePage({super.key, required this.counter}); | ||||
| 
 | ||||
|   factory NewValuePage.forCounter({required Counter counter}) { | ||||
|     switch (counter.counterType) { | ||||
|       case CounterType.coldWater: | ||||
|         return NewValuePageWithOneRate(counter: counter); | ||||
|       case CounterType.hotWater: | ||||
|         return NewValuePageWithOneRate(counter: counter); | ||||
|       case CounterType.gas: | ||||
|         return NewValuePageWithOneRate(counter: counter); | ||||
|       case CounterType.electrisity1rates: | ||||
|         return NewValuePageWithOneRate(counter: counter); | ||||
|       case CounterType.electrisity2rates: | ||||
|         return NewValuePageWithTwoRates(counter: counter); | ||||
|       case CounterType.electrisity3rates: | ||||
|         return NewValuePageWithThreeRates(counter: counter); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   final Counter counter; | ||||
|   final dataFormat = DateFormat("dd-MM-yyyy"); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|           backgroundColor: Theme.of(context).colorScheme.inversePrimary, | ||||
|           title: Text(AppLocalizations.of(context)!.new_value_title)), | ||||
|       body: SizedBox.shrink(), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class NewValuePageWithOneRate extends NewValuePage { | ||||
|   final valueController = TextEditingController(); | ||||
|   final dateController = TextEditingController(); | ||||
|   NewValuePageWithOneRate({super.key, required super.counter}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|           backgroundColor: Theme.of(context).colorScheme.inversePrimary, | ||||
|           title: Text(AppLocalizations.of(context)!.new_value_title)), | ||||
|       body: Padding( | ||||
|         padding: const EdgeInsets.all(8.0), | ||||
|         child: Center( | ||||
|           child: Column( | ||||
|             crossAxisAlignment: CrossAxisAlignment.center, | ||||
|             mainAxisAlignment: MainAxisAlignment.center, | ||||
|             children: [ | ||||
|               DateTimeField( | ||||
|                 format: dataFormat, | ||||
|                 initialValue: DateTime.now(), | ||||
|                 controller: dateController, | ||||
|                 onShowPicker: (context, currentValue) async { | ||||
|                   final date = await showDatePicker( | ||||
|                     context: context, | ||||
|                     initialDate: currentValue ?? DateTime.now(), | ||||
|                     firstDate: DateTime(2000), | ||||
|                     lastDate: DateTime(2100), | ||||
|                   ); | ||||
|                   return date; | ||||
|                 }, | ||||
|               ), | ||||
|               const SizedBox(height: 20), | ||||
|               TextField( | ||||
|                   controller: valueController, | ||||
|                   inputFormatters: [ | ||||
|                     FilteringTextInputFormatter.allow(RegExp(r'(^-?\d*\.?\d*)')) | ||||
|                   ], | ||||
|                   decoration: InputDecoration( | ||||
|                       label: | ||||
|                           Text(AppLocalizations.of(context)!.new_value_title), | ||||
|                       //hintStyle: const TextStyle(color: Colors.blue), | ||||
|                       hintText: '0.0', | ||||
|                       suffixText: | ||||
|                           counter.counterType.getUnits().getLabel(context))), | ||||
|               const SizedBox(height: 50), | ||||
|               TextButton( | ||||
|                   onPressed: () { | ||||
|                     DBProvider.db | ||||
|                         .newValue(Value( | ||||
|                             counterId: counter.id!, | ||||
|                             date: dataFormat.parse(dateController.text).toUtc(), | ||||
|                             rate1Id: 0, | ||||
|                             value1: | ||||
|                                 double.tryParse(valueController.text) ?? 0.0, | ||||
|                             value2: 0, | ||||
|                             value3: 0)) | ||||
|                         .then((value) => Navigator.pop(context)); | ||||
|                   }, | ||||
|                   child: Text( | ||||
|                       AppLocalizations.of(context)!.add_new_address_button)) | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class NewValuePageWithTwoRates extends NewValuePage { | ||||
|   final value1Controller = TextEditingController(); | ||||
|   final value2Controller = TextEditingController(); | ||||
|   final dateController = TextEditingController(); | ||||
|   NewValuePageWithTwoRates({super.key, required super.counter}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|           backgroundColor: Theme.of(context).colorScheme.inversePrimary, | ||||
|           title: Text(AppLocalizations.of(context)!.new_value_title)), | ||||
|       body: Padding( | ||||
|         padding: const EdgeInsets.all(8.0), | ||||
|         child: Center( | ||||
|           child: Column( | ||||
|             crossAxisAlignment: CrossAxisAlignment.center, | ||||
|             mainAxisAlignment: MainAxisAlignment.center, | ||||
|             children: [ | ||||
|               DateTimeField( | ||||
|                 format: DateFormat("dd-MM-yyyy"), | ||||
|                 initialValue: DateTime.now(), | ||||
|                 controller: dateController, | ||||
|                 onShowPicker: (context, currentValue) async { | ||||
|                   final date = await showDatePicker( | ||||
|                     context: context, | ||||
|                     initialDate: currentValue ?? DateTime.now(), | ||||
|                     firstDate: DateTime(2000), | ||||
|                     lastDate: DateTime(2100), | ||||
|                   ); | ||||
|                   return date; | ||||
|                 }, | ||||
|               ), | ||||
|               const SizedBox(height: 20), | ||||
|               TextField( | ||||
|                   controller: value1Controller, | ||||
|                   inputFormatters: [ | ||||
|                     FilteringTextInputFormatter.allow(RegExp(r'(^-?\d*\.?\d*)')) | ||||
|                   ], | ||||
|                   decoration: InputDecoration( | ||||
|                       label: | ||||
|                           Text(AppLocalizations.of(context)!.new_value_title), | ||||
|                       //hintStyle: const TextStyle(color: Colors.blue), | ||||
|                       hintText: '0.0', | ||||
|                       suffixText: | ||||
|                           counter.counterType.getUnits().getLabel(context))), | ||||
|               TextField( | ||||
|                   controller: value2Controller, | ||||
|                   inputFormatters: [ | ||||
|                     FilteringTextInputFormatter.allow(RegExp(r'(^-?\d*\.?\d*)')) | ||||
|                   ], | ||||
|                   decoration: InputDecoration( | ||||
|                       label: | ||||
|                           Text(AppLocalizations.of(context)!.new_value_title), | ||||
|                       //hintStyle: const TextStyle(color: Colors.blue), | ||||
|                       hintText: '0.0', | ||||
|                       suffixText: | ||||
|                           counter.counterType.getUnits().getLabel(context))), | ||||
|               const SizedBox(height: 50), | ||||
|               TextButton( | ||||
|                   onPressed: () { | ||||
|                     DBProvider.db | ||||
|                         .newValue(Value( | ||||
|                             counterId: counter.id!, | ||||
|                             date: dataFormat.parse(dateController.text).toUtc(), | ||||
|                             rate1Id: 0, | ||||
|                             value1: | ||||
|                                 double.tryParse(value1Controller.text) ?? 0.0, | ||||
|                             value2: | ||||
|                                 double.tryParse(value2Controller.text) ?? 0.0, | ||||
|                             value3: 0)) | ||||
|                         .then((value) => Navigator.pop(context)); | ||||
|                   }, | ||||
|                   child: Text( | ||||
|                       AppLocalizations.of(context)!.add_new_address_button)) | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class NewValuePageWithThreeRates extends NewValuePage { | ||||
|   NewValuePageWithThreeRates({super.key, required super.counter}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|           backgroundColor: Theme.of(context).colorScheme.inversePrimary, | ||||
|           title: Text(AppLocalizations.of(context)!.new_value_title)), | ||||
|       body: SizedBox.shrink(), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,43 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| import 'package:flutter_slidable/flutter_slidable.dart'; | ||||
| 
 | ||||
| class RecordActionPane extends StatelessWidget { | ||||
|   const RecordActionPane({ | ||||
|     super.key, | ||||
|     this.onDelete, | ||||
|     this.onEdit, | ||||
|   }); | ||||
| 
 | ||||
|   final VoidCallback? onDelete; | ||||
|   final VoidCallback? onEdit; | ||||
| 
 | ||||
|   @override | ||||
|   ActionPane build(BuildContext context) { | ||||
|     return ActionPane( | ||||
|       motion: const BehindMotion(), | ||||
|       //dismissible: DismissiblePane(onDismissed: () {}), | ||||
| 
 | ||||
|       children: [ | ||||
|         SlidableAction( | ||||
|           onPressed: (context) { | ||||
|             onEdit?.call(); | ||||
|           }, | ||||
|           backgroundColor: Colors.green, | ||||
|           foregroundColor: Colors.white, | ||||
|           icon: Icons.edit, | ||||
|           label: AppLocalizations.of(context)!.edit, | ||||
|         ), | ||||
|         SlidableAction( | ||||
|           onPressed: (context) { | ||||
|             onDelete?.call(); | ||||
|           }, | ||||
|           backgroundColor: Colors.red, | ||||
|           foregroundColor: Colors.white, | ||||
|           icon: Icons.delete, | ||||
|           label: AppLocalizations.of(context)!.delete, | ||||
|         ), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,62 @@ | ||||
| import 'package:counters/address.dart'; | ||||
| import 'package:counters/datbase.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| 
 | ||||
| class UpdateAddressPage extends StatelessWidget { | ||||
|   final streetNameController = TextEditingController(); | ||||
|   final commentsController = TextEditingController(); | ||||
| 
 | ||||
|   UpdateAddressPage({super.key, required this.address}); | ||||
| 
 | ||||
|   final Address address; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     streetNameController.text = address.streetName; | ||||
|     commentsController.text = address.comments; | ||||
| 
 | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         backgroundColor: Theme.of(context).colorScheme.inversePrimary, | ||||
|         title: Text(AppLocalizations.of(context)!.new_address_title), | ||||
|       ), | ||||
|       body: Center( | ||||
|         child: Padding( | ||||
|           padding: const EdgeInsets.all(8.0), | ||||
|           child: Column( | ||||
|               mainAxisAlignment: MainAxisAlignment.center, | ||||
|               crossAxisAlignment: CrossAxisAlignment.center, | ||||
|               children: [ | ||||
|                 TextField( | ||||
|                   controller: streetNameController, | ||||
|                   decoration: InputDecoration( | ||||
|                       hintStyle: const TextStyle(color: Colors.blue), | ||||
|                       hintText: | ||||
|                           AppLocalizations.of(context)!.enter_your_address), | ||||
|                 ), | ||||
|                 const SizedBox(height: 50), | ||||
|                 TextField( | ||||
|                   controller: commentsController, | ||||
|                   decoration: InputDecoration( | ||||
|                       hintStyle: const TextStyle(color: Colors.blue), | ||||
|                       hintText: AppLocalizations.of(context)! | ||||
|                           .enter_your_address_comments), | ||||
|                 ), | ||||
|                 const SizedBox(height: 50), | ||||
|                 TextButton( | ||||
|                     onPressed: () { | ||||
|                       DBProvider.db | ||||
|                           .updateAddress(Address( | ||||
|                               id: address.id, | ||||
|                               streetName: streetNameController.text, | ||||
|                               comments: commentsController.text)) | ||||
|                           .then((value) => Navigator.pop(context)); | ||||
|                     }, | ||||
|                     child: Text(AppLocalizations.of(context)!.edit)) | ||||
|               ]), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,76 @@ | ||||
| import 'package:counters/counters.dart'; | ||||
| import 'package:counters/datbase.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| 
 | ||||
| class UpdateCounterPage extends StatelessWidget { | ||||
|   UpdateCounterPage({super.key, required this.counter}); | ||||
| 
 | ||||
|   final Counter counter; | ||||
|   final nameController = TextEditingController(); | ||||
|   final dropdownController = TextEditingController(); | ||||
|   CounterType counterType = CounterType.coldWater; | ||||
| 
 | ||||
|   @override | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     nameController.text = counter.name; | ||||
|     counterType = counter.counterType; | ||||
|     dropdownController.text = counter.counterType.getLabel(context); | ||||
| 
 | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|           backgroundColor: Theme.of(context).colorScheme.inversePrimary, | ||||
|           title: Text(AppLocalizations.of(context)!.new_counter_title)), | ||||
|       body: Center( | ||||
|         child: Padding( | ||||
|           padding: const EdgeInsets.all(8.0), | ||||
|           child: Column( | ||||
|               mainAxisAlignment: MainAxisAlignment.center, | ||||
|               crossAxisAlignment: CrossAxisAlignment.center, | ||||
|               children: [ | ||||
|                 DropdownMenu<CounterType>( | ||||
|                   controller: dropdownController, | ||||
|                   enableFilter: true, | ||||
|                   hintText: | ||||
|                       AppLocalizations.of(context)!.choose_type_of_counter, | ||||
|                   expandedInsets: EdgeInsets.zero, | ||||
|                   dropdownMenuEntries: | ||||
|                       CounterType.values.map<DropdownMenuEntry<CounterType>>( | ||||
|                     (CounterType icon) { | ||||
|                       return DropdownMenuEntry<CounterType>( | ||||
|                           value: icon, | ||||
|                           labelWidget: Text(icon.getLabel(context)), | ||||
|                           label: icon.getLabel(context) | ||||
|                           //leadingIcon: Icon(icon.icon), | ||||
|                           ); | ||||
|                     }, | ||||
|                   ).toList(), | ||||
|                   onSelected: (value) => counterType = value!, | ||||
|                 ), | ||||
|                 const SizedBox(height: 50), | ||||
|                 TextField( | ||||
|                   controller: nameController, | ||||
|                   decoration: InputDecoration( | ||||
|                       hintStyle: const TextStyle(color: Colors.blue), | ||||
|                       hintText: | ||||
|                           AppLocalizations.of(context)!.enter_counter_name), | ||||
|                 ), | ||||
|                 const SizedBox(height: 50), | ||||
|                 TextButton( | ||||
|                     onPressed: () { | ||||
|                       DBProvider.db | ||||
|                           .updateCounter(Counter( | ||||
|                               id: counter.id, | ||||
|                               addressId: counter.addressId, | ||||
|                               counterType: counterType, | ||||
|                               name: nameController.text)) | ||||
|                           .then((value) => Navigator.pop(context)); | ||||
|                     }, | ||||
|                     child: Text(AppLocalizations.of(context)!.edit)) | ||||
|               ]), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,45 @@ | ||||
| class Value { | ||||
|   final int? id; | ||||
|   final int counterId; | ||||
|   final DateTime date; | ||||
|   final int rate1Id; | ||||
|   final double value1; | ||||
|   final int? rate2Id; | ||||
|   final double? value2; | ||||
|   final int? rate3Id; | ||||
|   final double? value3; | ||||
| 
 | ||||
|   Value( | ||||
|       {this.id, | ||||
|       required this.counterId, | ||||
|       required this.date, | ||||
|       required this.rate1Id, | ||||
|       required this.value1, | ||||
|       this.rate2Id, | ||||
|       this.value2, | ||||
|       this.rate3Id, | ||||
|       this.value3}); | ||||
| 
 | ||||
|   factory Value.fromMap(Map<String, dynamic> json) => Value( | ||||
|       id: json["id"], | ||||
|       counterId: json["counter_id"], | ||||
|       date: DateTime.fromMicrosecondsSinceEpoch(json["date"], isUtc: true), | ||||
|       rate1Id: json["rate1_id"], | ||||
|       value1: json["value1"], | ||||
|       rate2Id: json["rate2_id"], | ||||
|       value2: json["value2"], | ||||
|       rate3Id: json["rate3_id"], | ||||
|       value3: json["value3"]); | ||||
| 
 | ||||
|   Map<String, dynamic> toMap() => { | ||||
|         "id": id, | ||||
|         "counter_id": counterId, | ||||
|         "date": date.microsecondsSinceEpoch, | ||||
|         "rate1_id": rate1Id, | ||||
|         "value1": value1, | ||||
|         "rate2_id": rate2Id, | ||||
|         "value2": value2, | ||||
|         "rate3_id": rate3Id, | ||||
|         "value3": value3 | ||||
|       }; | ||||
| } | ||||
| @ -0,0 +1,119 @@ | ||||
| import 'package:counters/counters.dart'; | ||||
| import 'package:counters/datbase.dart'; | ||||
| import 'package:counters/new_value_page.dart'; | ||||
| import 'package:counters/value.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| import 'package:intl/intl.dart'; | ||||
| 
 | ||||
| class ValuesPage extends StatefulWidget { | ||||
|   const ValuesPage({super.key, required this.counter}); | ||||
| 
 | ||||
|   final Counter counter; | ||||
| 
 | ||||
|   @override | ||||
|   State<ValuesPage> createState() => _ValuesPageState(); | ||||
| } | ||||
| 
 | ||||
| class _ValuesPageState extends State<ValuesPage> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     var values = DBProvider.db.getValuesOfCounter(widget.counter); | ||||
|     return Scaffold( | ||||
|         appBar: AppBar( | ||||
|             backgroundColor: Theme.of(context).colorScheme.inversePrimary, | ||||
|             title: Text(AppLocalizations.of(context)!.app_title)), | ||||
|         body: Center( | ||||
|           child: FutureBuilder( | ||||
|             future: values, | ||||
|             builder: (context, snapshot) { | ||||
|               if (snapshot.hasData && snapshot.data!.isNotEmpty) { | ||||
|                 return ValuesListView( | ||||
|                     counter: widget.counter, | ||||
|                     values: snapshot.data!.reversed.toList()); | ||||
|               } | ||||
| 
 | ||||
|               return Text(AppLocalizations.of(context)!.empty_values_list); | ||||
|             }, | ||||
|           ), | ||||
|         ), | ||||
|         floatingActionButton: FloatingActionButton( | ||||
|           onPressed: () { | ||||
|             Navigator.push( | ||||
|                     context, | ||||
|                     MaterialPageRoute( | ||||
|                         builder: (context) => | ||||
|                             NewValuePage.forCounter(counter: widget.counter))) | ||||
|                 .then((_) => setState(() {})); | ||||
|           }, | ||||
|           tooltip: AppLocalizations.of(context)!.add_new_counter_tooltip, | ||||
|           child: const Icon(Icons.add), | ||||
|         )); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class ValuesListView extends StatelessWidget { | ||||
|   ValuesListView({ | ||||
|     super.key, | ||||
|     required this.values, | ||||
|     required this.counter, | ||||
|   }); | ||||
| 
 | ||||
|   final List<Value> values; | ||||
|   final Counter counter; | ||||
|   final dataFormat = DateFormat("dd-MM-yyyy"); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ListView.builder( | ||||
|       itemCount: values.length, | ||||
|       itemBuilder: (context, index) { | ||||
|         var units = counter.counterType.getUnits().getLabel(context); | ||||
|         var valueWidgets = List<Widget>.empty(growable: true); | ||||
|         valueWidgets.add(Text("T1 - ${values[index].value1} $units")); | ||||
|         var sum = values[index].value1; | ||||
|         var value = values[index]; | ||||
|         if (value.value2 != null && | ||||
|             counter.counterType.id > CounterType.electrisity1rates.id) { | ||||
|           valueWidgets.add(Text("T2 - ${value.value2} $units")); | ||||
|           sum += value.value2!; | ||||
|         } | ||||
|         if (value.value3 != null && | ||||
|             counter.counterType.id > CounterType.electrisity2rates.id) { | ||||
|           valueWidgets.add(Text("T3 - ${value.value3} $units")); | ||||
|           sum += value.value3!; | ||||
|         } | ||||
| 
 | ||||
|         if (valueWidgets.length > 1) { | ||||
|           valueWidgets.add(Text("Итого - $sum $units")); | ||||
|         } | ||||
|         return Padding( | ||||
|           padding: const EdgeInsets.only(left: 8.0, right: 8.0), | ||||
|           child: Card( | ||||
|               child: Align( | ||||
|             alignment: Alignment.centerLeft, | ||||
|             child: Padding( | ||||
|               padding: const EdgeInsets.all(8.0), | ||||
|               child: Column( | ||||
|                 mainAxisAlignment: MainAxisAlignment.start, | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 children: [ | ||||
|                   Row( | ||||
|                     children: [ | ||||
|                       Text(dataFormat.format(value.date.toLocal())), | ||||
|                       Text(' ${AppLocalizations.of(context)!.values}:'), | ||||
|                     ], | ||||
|                   ), | ||||
|                   Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.end, | ||||
|                     children: valueWidgets, | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|           )), | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue