Added pretty chart legend and date display for charts

This commit is contained in:
2024-01-23 11:49:33 +03:00
parent 9241fe2abf
commit edd512fb05
2 changed files with 174 additions and 88 deletions

View File

@@ -111,6 +111,9 @@ final Map<String, String> ru = {
"weight_chart": "График веса",
"circumferences_chart": "График обхватов",
"bodymass_chart": "График ИМТ",
"chest": "Грудь",
"waist": "Талия",
"hips": "Бедра",
"exit": "Выйти",
"active": "Активен",
"archived": "В архиве",

View File

@@ -12,6 +12,7 @@ import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'dart:math';
import 'package:intl/intl.dart';
import 'package:fl_chart/fl_chart.dart';
class MyProfileScreen extends StatelessWidget {
@@ -103,7 +104,94 @@ class MyProfileScreen extends StatelessWidget {
case 3: //График обхватов
return GestureDetector(
onTap: () {
_showAddMeasurementDialog(context);
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Добавить замер"),
content: Column(
children: [
BlocSelector<
MyProfileBloc,
MyProfileState,
TextEditingController?>(
selector: (state) => state
.chestCircumferenceController,
builder: (context,
chestCircumferenceController) {
return TextField(
controller:
chestCircumferenceController,
keyboardType:
TextInputType
.number,
decoration: InputDecoration(
labelText:
"chest_circumference"
.tr),
);
}),
BlocSelector<
MyProfileBloc,
MyProfileState,
TextEditingController?>(
selector: (state) => state
.waistCircumferenceController,
builder: (context,
waistCircumferenceController) {
return TextField(
controller:
waistCircumferenceController,
keyboardType:
TextInputType
.number,
decoration: InputDecoration(
labelText:
"waist_circumference"
.tr),
);
}),
BlocSelector<
MyProfileBloc,
MyProfileState,
TextEditingController?>(
selector: (state) => state
.hipCircumferenceController,
builder: (context,
hipCircumferenceController) {
return TextField(
controller:
hipCircumferenceController,
keyboardType:
TextInputType
.number,
decoration: InputDecoration(
labelText:
"hip_circumference"
.tr),
);
}),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Отмена"),
),
TextButton(
onPressed: () {
// Отправка данных на API
// Закрытие диалогового окна
Navigator.pop(context);
},
child: Text("Изменить"),
),
],
);
},
);
},
child: Column(children: [
Text(
@@ -113,11 +201,15 @@ class MyProfileScreen extends StatelessWidget {
color: appTheme.black900,
),
),
SizedBox(height: 50.v),
SizedBox(height: 30.v),
_buildCircumferenceChart(
context,
state.circumferences,
)
context,
state.circumferences,
state.chartPoints?.values
.map<String>(
(e) => e['created_at'])
.toList()),
_buildChartLegend()
]));
case 4:
return NextStepButton(
@@ -211,8 +303,41 @@ class MyProfileScreen extends StatelessWidget {
return CustomBottomBar(onChanged: (BottomBarEnum type) {});
}
Widget _buildChartLegend() {
final colors = [Colors.pink, Colors.green, Colors.purple];
final titles = ["chest".tr, "waist".tr, "hips".tr];
final style = TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
);
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var i = 0; i < colors.length; i++)
Container(
child: Row(
children: [
Container(
width: 16,
height: 16,
color: colors[i],
),
SizedBox(width: 8),
Text(titles[i], style: style),
],
),
),
],
),
],
);
}
/// Section Widget
Widget _buildChart(BuildContext context, List<double> chartPoints, dates) {
Widget _buildChart(
BuildContext context, List<double> chartPoints, List<String>? dates) {
// Определение осей графика
List<FlSpot> spots = [];
for (int i = 0; i < 4; i++) {
@@ -221,20 +346,29 @@ class MyProfileScreen extends StatelessWidget {
);
}
Widget bottomTitleWidgets(double value, TitleMeta meta, double chartWidth) {
Widget bottomTitleWidgets(
double value, TitleMeta meta, double chartWidth, List<String>? dates) {
if (value % 1 != 0) {
return Container();
}
final style = TextStyle(
color: Colors.white,
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: min(18, 18 * chartWidth / 300),
);
return SideTitleWidget(
axisSide: meta.axisSide,
space: 16,
child: Text(meta.formattedValue, style: style),
fontSize: min(13, 13 * chartWidth / 300),
);
int index = value.toInt();
if (index >= 0 && index < dates!.length) {
// Проверка на допустимый индекс в массиве dates
String formattedDate =
DateFormat('dd.MM').format(DateTime.parse(dates[index]));
return SideTitleWidget(
axisSide: meta.axisSide,
space: 16,
child: Text(formattedDate, style: style),
);
} else {
return Container();
}
}
Widget leftTitleWidgets(double value, TitleMeta meta, double chartWidth) {
@@ -320,8 +454,8 @@ class MyProfileScreen extends StatelessWidget {
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) =>
bottomTitleWidgets(value, meta, constraints.maxWidth),
getTitlesWidget: (value, meta) => bottomTitleWidgets(
value, meta, constraints.maxWidth, dates),
reservedSize: 36,
interval: 1,
),
@@ -363,7 +497,8 @@ class MyProfileScreen extends StatelessWidget {
);
}
Widget _buildCircumferenceChart(BuildContext context, chartPoints) {
Widget _buildCircumferenceChart(
BuildContext context, chartPoints, List<String>? dates) {
// Определение осей графика
final chestCircumferences =
chartPoints.values.map((e) => e['chest_circumference']).toList();
@@ -399,20 +534,29 @@ class MyProfileScreen extends StatelessWidget {
...waistCircumferences
];
Widget bottomTitleWidgets(double value, TitleMeta meta, double chartWidth) {
Widget bottomTitleWidgets(
double value, TitleMeta meta, double chartWidth, List<String>? dates) {
if (value % 1 != 0) {
return Container();
}
final style = TextStyle(
color: Colors.white,
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: min(18, 18 * chartWidth / 300),
);
return SideTitleWidget(
axisSide: meta.axisSide,
space: 16,
child: Text(meta.formattedValue, style: style),
fontSize: min(13, 13 * chartWidth / 300),
);
int index = value.toInt();
if (index >= 0 && index < dates!.length) {
// Проверка на допустимый индекс в массиве dates
String formattedDate =
DateFormat('dd.MM').format(DateTime.parse(dates[index]));
return SideTitleWidget(
axisSide: meta.axisSide,
space: 16,
child: Text(formattedDate, style: style),
);
} else {
return Container();
}
}
Widget leftTitleWidgets(double value, TitleMeta meta, double chartWidth) {
@@ -520,8 +664,8 @@ class MyProfileScreen extends StatelessWidget {
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) =>
bottomTitleWidgets(value, meta, constraints.maxWidth),
getTitlesWidget: (value, meta) => bottomTitleWidgets(
value, meta, constraints.maxWidth, dates),
reservedSize: 36,
interval: 1,
),
@@ -684,66 +828,5 @@ class MyProfileScreen extends StatelessWidget {
}
}
_showAddMeasurementDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Добавить замер"),
content: Column(
children: [
BlocSelector<MyProfileBloc, MyProfileState, TextEditingController?>(
selector: (state) => state.chestCircumferenceController,
builder: (context, chestCircumferenceController) {
return TextField(
controller: chestCircumferenceController,
keyboardType: TextInputType.number,
decoration:
InputDecoration(labelText: "chest_circumference".tr),
);
}),
BlocSelector<MyProfileBloc, MyProfileState, TextEditingController?>(
selector: (state) => state.waistCircumferenceController,
builder: (context, waistCircumferenceController) {
return TextField(
controller: waistCircumferenceController,
keyboardType: TextInputType.number,
decoration:
InputDecoration(labelText: "waist_circumference".tr),
);
}),
BlocSelector<MyProfileBloc, MyProfileState, TextEditingController?>(
selector: (state) => state.hipCircumferenceController,
builder: (context, hipCircumferenceController) {
return TextField(
controller: hipCircumferenceController,
keyboardType: TextInputType.number,
decoration:
InputDecoration(labelText: "hip_circumference".tr),
);
}),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Отмена"),
),
TextButton(
onPressed: () {
// Отправка данных на API
// Закрытие диалогового окна
Navigator.pop(context);
},
child: Text("Изменить"),
),
],
);
},
);
}
_sendDataToApi(String chestCircumference, String waistCircumference,
String hipCircumference) {}