Сергей Марков
8 months ago
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