forked from eatvio/eatvio_app
251 lines
10 KiB
Dart
251 lines
10 KiB
Dart
import 'package:eatvio_app/features/profile/models/weight_model.dart';
|
||
import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart';
|
||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:intl/intl.dart';
|
||
import 'package:syncfusion_flutter_charts/charts.dart';
|
||
|
||
class WeightChartWidget extends StatefulWidget {
|
||
final List<WeightModel> weightPoints;
|
||
final ProfileCubit? profileCubit;
|
||
|
||
const WeightChartWidget({
|
||
super.key,
|
||
required this.weightPoints,
|
||
this.profileCubit,
|
||
});
|
||
|
||
@override
|
||
State<WeightChartWidget> createState() => _WeightChartWidgetState();
|
||
}
|
||
|
||
class _WeightChartWidgetState extends State<WeightChartWidget> {
|
||
late FixedExtentScrollController weightKiloController;
|
||
late FixedExtentScrollController weightGramsController;
|
||
int weightKilo = 20;
|
||
int weightGrams = 0;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
if (widget.weightPoints.isNotEmpty)
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||
child: Row(
|
||
children: [
|
||
Text(
|
||
'${widget.weightPoints.last.weight.toStringAsFixed(1)} кг',
|
||
style: const TextStyle(
|
||
color: Colors.black,
|
||
fontSize: 40,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
if (widget.weightPoints.length == 1)
|
||
Container(
|
||
margin: const EdgeInsets.symmetric(horizontal: 12),
|
||
width: 8,
|
||
height: 8,
|
||
decoration: const BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
color: Colors.black12,
|
||
),
|
||
),
|
||
if (widget.weightPoints.length > 1)
|
||
widget.weightPoints.last.weight > widget.weightPoints[widget.weightPoints.length - 2].weight
|
||
? Icon(
|
||
Icons.arrow_drop_up,
|
||
size: 40,
|
||
color: Colors.grey.withValues(alpha: 0.4),
|
||
)
|
||
: Icon(
|
||
Icons.arrow_drop_down,
|
||
size: 40,
|
||
color: Colors.grey.withValues(alpha: 0.4),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Stack(
|
||
alignment: Alignment.center,
|
||
children: [
|
||
SfCartesianChart(
|
||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||
enableAxisAnimation: true,
|
||
plotAreaBorderWidth: 0,
|
||
trackballBehavior: TrackballBehavior(
|
||
enable: true,
|
||
activationMode: ActivationMode.singleTap,
|
||
tooltipDisplayMode: TrackballDisplayMode.groupAllPoints,
|
||
tooltipSettings: const InteractiveTooltip(),
|
||
lineType: TrackballLineType.vertical,
|
||
),
|
||
primaryXAxis: DateTimeCategoryAxis(
|
||
tickPosition: TickPosition.inside,
|
||
majorTickLines: const MajorTickLines(
|
||
size: 0,
|
||
),
|
||
edgeLabelPlacement: EdgeLabelPlacement.shift,
|
||
dateFormat: DateFormat.MMMd('ru'),
|
||
labelStyle: const TextStyle(
|
||
color: Colors.grey,
|
||
),
|
||
majorGridLines: MajorGridLines(dashArray: const [1], color: Colors.grey.withValues(alpha: 0.3)),
|
||
axisLine: AxisLine(
|
||
color: Colors.grey.withValues(alpha: 0.3),
|
||
width: 0.4,
|
||
),
|
||
),
|
||
primaryYAxis: const NumericAxis(
|
||
isVisible: false,
|
||
labelFormat: '{value} кг',
|
||
majorGridLines: MajorGridLines(dashArray: [1], color: Colors.grey),
|
||
axisLine: AxisLine(
|
||
width: 1,
|
||
),
|
||
majorTickLines: MajorTickLines(size: 0),
|
||
),
|
||
series: _getSplineSeries(),
|
||
tooltipBehavior: TooltipBehavior(enable: true),
|
||
),
|
||
if (widget.profileCubit != null)
|
||
Positioned(
|
||
right: 16,
|
||
bottom: 16,
|
||
child: FloatingActionButton.small(
|
||
heroTag: 'addWeight',
|
||
onPressed: () {
|
||
if (widget.weightPoints.isNotEmpty) {
|
||
try {
|
||
weightKilo = int.parse(widget.weightPoints.last.weight.toString().split('.')[0]);
|
||
weightGrams = int.parse(widget.weightPoints.last.weight.toString().split('.')[1][0]);
|
||
} catch (e) {
|
||
weightGrams = 0;
|
||
}
|
||
weightKiloController = FixedExtentScrollController(initialItem: weightKilo - 20);
|
||
weightGramsController = FixedExtentScrollController(initialItem: weightGrams);
|
||
} else {
|
||
weightKiloController = FixedExtentScrollController();
|
||
weightGramsController = FixedExtentScrollController();
|
||
}
|
||
showModalBottomSheet(
|
||
useRootNavigator: true,
|
||
context: context,
|
||
builder: (context) {
|
||
return SafeArea(
|
||
child: Column(
|
||
children: [
|
||
Expanded(
|
||
child: Row(
|
||
children: [
|
||
Expanded(
|
||
child: CupertinoPicker(
|
||
magnification: 1.22,
|
||
squeeze: 1.2,
|
||
useMagnifier: true,
|
||
scrollController: weightKiloController,
|
||
itemExtent: 24,
|
||
onSelectedItemChanged: (int value) {
|
||
weightKilo = value + 20;
|
||
},
|
||
children: [
|
||
for (int i = 20; i < 300; i++) Text('$i'),
|
||
],
|
||
),
|
||
),
|
||
Expanded(
|
||
child: CupertinoPicker(
|
||
magnification: 1.22,
|
||
squeeze: 1.2,
|
||
useMagnifier: true,
|
||
scrollController: weightGramsController,
|
||
itemExtent: 24,
|
||
onSelectedItemChanged: (int value) {
|
||
weightGrams = value;
|
||
},
|
||
children: [
|
||
for (int i = 0; i < 10; i++) Text('$i'),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
InkWell(
|
||
onTap: () {
|
||
widget.profileCubit?.addWeight(weight: double.parse('$weightKilo.$weightGrams'));
|
||
Navigator.of(context).pop();
|
||
},
|
||
borderRadius: BorderRadius.circular(16),
|
||
child: Ink(
|
||
width: MediaQuery.of(context).size.width - 32,
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: appTheme.blueA400,
|
||
borderRadius: BorderRadius.circular(16),
|
||
),
|
||
child: const Center(
|
||
child: Text(
|
||
'Сохранить',
|
||
style: TextStyle(
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
);
|
||
},
|
||
backgroundColor: appTheme.blueA400,
|
||
shape: const CircleBorder(),
|
||
child: const Icon(
|
||
Icons.add,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
if (widget.weightPoints.isEmpty) const Text('Добавьте Ваш вес'),
|
||
],
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
List<CartesianSeries<WeightModel, DateTime>> _getSplineSeries() {
|
||
return <CartesianSeries<WeightModel, DateTime>>[
|
||
SplineAreaSeries<WeightModel, DateTime>(
|
||
markerSettings: MarkerSettings(
|
||
isVisible: true,
|
||
color: appTheme.blueA400,
|
||
height: 8,
|
||
width: 8,
|
||
),
|
||
dataLabelSettings: const DataLabelSettings(
|
||
labelAlignment: ChartDataLabelAlignment.top,
|
||
isVisible: true,
|
||
),
|
||
dataSource: widget.weightPoints,
|
||
borderColor: appTheme.blueA400,
|
||
gradient: LinearGradient(
|
||
colors: [
|
||
appTheme.blueA400.withValues(alpha: 0.14),
|
||
Colors.transparent,
|
||
],
|
||
begin: Alignment.topCenter,
|
||
end: const Alignment(0, 0.6),
|
||
),
|
||
name: 'Вес',
|
||
xValueMapper: (WeightModel value, int index) => value.createdAt,
|
||
yValueMapper: (WeightModel value, int index) => value.weight,
|
||
),
|
||
];
|
||
}
|
||
}
|