Finished circumference charts

This commit is contained in:
2024-01-16 13:30:58 +03:00
parent f469e6f8e8
commit d81854a12f
4 changed files with 262 additions and 42 deletions

View File

@@ -108,6 +108,9 @@ final Map<String, String> ru = {
// Личный кабинет Screen
"loading": "Загрузка...",
"weight_chart": "График веса",
"circumferences_chart": "График обхватов",
"bodymass_chart": "График ИМТ",
"exit": "Выйти",
"my_blog": "Мой блог", "lbl_01_04_21": "01.04.21", "lbl_01_05_21": "01.05.21",

View File

@@ -53,8 +53,10 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
);
final data = jsonDecode(response.body);
emit(state.copyWith(
user: data['user'],
chartPoints: data['chartPoints'],
userDetails: data['userDetails']));
user: data['user'],
chartPoints: data['chartPoints'],
userDetails: data['userDetails'],
circumferences: data['circumferences'],
));
}
}

View File

@@ -5,24 +5,32 @@ part of 'my_profile_bloc.dart';
/// Represents the state of MyProfile in the application.
class MyProfileState extends Equatable {
MyProfileState(
{this.myProfileModelObj, this.user, this.userDetails, this.chartPoints});
{this.myProfileModelObj,
this.user,
this.userDetails,
this.chartPoints,
this.circumferences});
MyProfileModel? myProfileModelObj;
Map<String, dynamic>? user;
Map<String, dynamic>? userDetails;
Map<String, dynamic>? chartPoints;
Map<String, dynamic>? circumferences;
@override
List<Object?> get props => [myProfileModelObj, user, chartPoints];
List<Object?> get props =>
[myProfileModelObj, user, chartPoints, circumferences];
MyProfileState copyWith(
{MyProfileModel? myProfileModelObj,
Map<String, dynamic>? user,
Map<String, dynamic>? userDetails,
Map<String, dynamic>? chartPoints}) {
Map<String, dynamic>? chartPoints,
Map<String, dynamic>? circumferences}) {
return MyProfileState(
myProfileModelObj: myProfileModelObj ?? this.myProfileModelObj,
user: user ?? this.user,
userDetails: userDetails ?? this.userDetails,
circumferences: circumferences ?? this.circumferences,
chartPoints: chartPoints ?? this.chartPoints);
}
}

View File

@@ -40,17 +40,6 @@ class MyProfileScreen extends StatelessWidget {
SizedBox(height: 15.v),
_buildSettings(context),
SizedBox(height: 30.v),
/*_buildChart(
context,
state.chartPoints?.values
.map<int>((e) => e['weight'])
.toList()
.map((i) => i.toDouble())
.toList() ??
[0.0, 0.0, 0.0, 0.0],
state.chartPoints?.values
.map<String>((e) => e['created_at'])
.toList()),*/
CarouselSlider(
options: CarouselOptions(height: 370.0),
items: [1, 2, 3].map((chartType) {
@@ -58,10 +47,16 @@ class MyProfileScreen extends StatelessWidget {
builder: (BuildContext context) {
switch (chartType) {
case 1: // График веса
return Container(
decoration:
BoxDecoration(color: Colors.white),
child: _buildChart(
return Column(children: [
Text(
"weight_chart".tr,
style: CustomTextStyles.titleMediumBlack
.copyWith(
color: appTheme.black900,
),
),
SizedBox(height: 50.v),
_buildChart(
context,
state.chartPoints?.values
.map<int>((e) => e['weight'])
@@ -72,29 +67,48 @@ class MyProfileScreen extends StatelessWidget {
state.chartPoints?.values
.map<String>((e) => e['created_at'])
.toList()),
);
]);
case 2: // График ИМТ
return Container(
decoration:
BoxDecoration(color: Colors.white),
child: _buildChart(
return Column(children: [
Text(
"bodymass_chart".tr,
style: CustomTextStyles.titleMediumBlack
.copyWith(
color: appTheme.black900,
),
),
SizedBox(height: 50.v),
_buildChart(
context,
_bodyMassIndex(
state.chartPoints!.values
state.chartPoints?.values
.map<int>((e) => e['weight'])
.toList()
.map((i) => i.toDouble())
.toList(),
state.userDetails!['height']
.toInt()) ??
[0.0, 0.0, 0.0, 0.0],
.toList() ??
[10.0, 20.0, 30.0, 40.0],
state.userDetails?['height']
.toInt() ??
160),
state.chartPoints?.values
.map<String>((e) => e['created_at'])
.toList()),
);
case 3:
return Text('text',
style: TextStyle(fontSize: 16.0));
]);
case 3: //График обхватов
return Column(children: [
Text(
"circumferences_chart".tr,
style: CustomTextStyles.titleMediumBlack
.copyWith(
color: appTheme.black900,
),
),
SizedBox(height: 50.v),
_buildCircumferenceChart(
context,
state.circumferences,
)
]);
default:
return Text('text',
style: TextStyle(fontSize: 16.0));
@@ -175,12 +189,6 @@ class MyProfileScreen extends StatelessWidget {
/// Section Widget
Widget _buildChart(BuildContext context, List<double> chartPoints, dates) {
// Определение данных для графика
/* List<FlSpot> chartData = chartPoints
.map((point) =>
FlSpot(point['created_at'] as double, point['weight'] as double))
.toList(); */
// Определение осей графика
List<FlSpot> spots = [];
for (int i = 0; i < 4; i++) {
@@ -188,7 +196,6 @@ class MyProfileScreen extends StatelessWidget {
FlSpot((i * 10 / 10) + 1, chartPoints[i]),
);
}
print(spots);
Widget bottomTitleWidgets(double value, TitleMeta meta, double chartWidth) {
if (value % 1 != 0) {
@@ -332,6 +339,206 @@ class MyProfileScreen extends StatelessWidget {
);
}
Widget _buildCircumferenceChart(BuildContext context, chartPoints) {
// Определение осей графика
final chestCircumferences =
chartPoints.values.map((e) => e['chest_circumference']).toList();
List<FlSpot> spotsChest = [];
for (int i = 0; i < 4; i++) {
chestCircumferences[i] = chestCircumferences[i] * 10 / 10;
spotsChest.add(
FlSpot((i * 10 / 10) + 1, chestCircumferences[i] * 10 / 10),
);
}
final waistCircumferences =
chartPoints.values.map((e) => e['waist_circumference']).toList();
List<FlSpot> spotsWaist = [];
for (int i = 0; i < 4; i++) {
waistCircumferences[i] = waistCircumferences[i] * 10 / 10;
spotsWaist.add(
FlSpot((i * 10 / 10) + 1, waistCircumferences[i] * 10 / 10),
);
}
final hipCircumferences =
chartPoints.values.map((e) => e['hip_circumference']).toList();
List<FlSpot> spotsHip = [];
for (int i = 0; i < 4; i++) {
hipCircumferences[i] = hipCircumferences[i] * 10 / 10;
spotsHip.add(
FlSpot((i * 10 / 10) + 1, hipCircumferences[i]),
);
}
final allCircumferences = [
...chestCircumferences,
...hipCircumferences,
...waistCircumferences
];
Widget bottomTitleWidgets(double value, TitleMeta meta, double chartWidth) {
if (value % 1 != 0) {
return Container();
}
final style = TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: min(18, 18 * chartWidth / 300),
);
return SideTitleWidget(
axisSide: meta.axisSide,
space: 16,
child: Text(meta.formattedValue, style: style),
);
}
Widget leftTitleWidgets(double value, TitleMeta meta, double chartWidth) {
final style = TextStyle(
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),
);
}
return Padding(
padding: const EdgeInsets.only(
left: 12,
bottom: 12,
right: 20,
top: 18,
),
child: AspectRatio(
aspectRatio: 1,
child: LayoutBuilder(
builder: (context, constraints) {
return LineChart(
LineChartData(
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
maxContentWidth: 100,
tooltipBgColor: Colors.black,
getTooltipItems: (touchedSpots) {
return touchedSpots.map((LineBarSpot touchedSpot) {
final textStyle = TextStyle(
color: touchedSpot.bar.gradient?.colors[0] ??
touchedSpot.bar.color,
fontWeight: FontWeight.bold,
fontSize: 14,
);
return LineTooltipItem(
'${touchedSpot.x}, ${touchedSpot.y.toStringAsFixed(2)}',
textStyle,
);
}).toList();
},
),
handleBuiltInTouches: true,
getTouchLineStart: (data, index) => 0,
),
lineBarsData: [
LineChartBarData(
color: Colors.pink,
spots: spotsChest,
isCurved: true,
isStrokeCapRound: true,
barWidth: 3,
belowBarData: BarAreaData(
show: false,
),
dotData: const FlDotData(show: false),
),
LineChartBarData(
color: Colors.green,
spots: spotsWaist,
isCurved: true,
isStrokeCapRound: true,
barWidth: 3,
belowBarData: BarAreaData(
show: false,
),
dotData: const FlDotData(show: false),
),
LineChartBarData(
color: Colors.purple,
spots: spotsHip,
isCurved: true,
isStrokeCapRound: true,
barWidth: 3,
belowBarData: BarAreaData(
show: false,
),
dotData: const FlDotData(show: false),
),
],
minY: allCircumferences.reduce(
(value, element) => value < element ? value : element),
maxY: allCircumferences.reduce(
(value, element) => value > element ? value : element),
minX: 1,
maxX: 4,
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) =>
leftTitleWidgets(value, meta, constraints.maxWidth),
reservedSize: 56,
),
drawBelowEverything: true,
),
rightTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) =>
bottomTitleWidgets(value, meta, constraints.maxWidth),
reservedSize: 36,
interval: 1,
),
drawBelowEverything: true,
),
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
),
gridData: FlGridData(
show: true,
drawHorizontalLine: true,
drawVerticalLine: true,
horizontalInterval: 1.5,
verticalInterval: 5,
checkToShowHorizontalLine: (value) {
return value.toInt() == 0;
},
getDrawingHorizontalLine: (_) => FlLine(
color: Colors.blue.withOpacity(1),
dashArray: [8, 2],
strokeWidth: 0.8,
),
getDrawingVerticalLine: (_) => FlLine(
color: Colors.blue.withOpacity(1),
dashArray: [8, 2],
strokeWidth: 0.8,
),
checkToShowVerticalLine: (value) {
return value.toInt() == 0;
},
),
borderData: FlBorderData(show: false),
),
);
},
),
),
);
}
/// Section Widget
Widget _buildWidget(BuildContext context) {
return Padding(