fix: fix errors

This commit is contained in:
mso7nueh
2024-08-23 15:25:04 +03:00
parent 98e4388922
commit 3d321f6a9d
8 changed files with 372 additions and 123 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -1,59 +1 @@
# eatvio_app
### Системные требования
- Flutter (Channel stable, 3.19.0, on Ubuntu 22.04.3 LTS 6.2.0-39-generic, locale en_US.UTF-8)
- Android Studio (version 2023.1)
- VS Code (version 1.85.0)
- Android SDK (version 34.0.0) - устанавливается из Android Studio (Tools->SDK Manager->Android Command Line Tools)
- Эмулятор (2 available)
### Структура приложения
```
.
├── android - Папка для крмпиляции Android приложения
├── assets - Медиа и шрифты
├── ios - Папка для крмпиляции iOS приложения
├── lib - Основной код
├── main.dart - Файл из которого компилируется приложение
├── presentation - Виджеты экранов (верстка)
├── routes - Роутер (пути до экранов)
└── theme - Темы и оформление
└── widgets - Кастомные виджеты
├── pubspec.yaml - Пакеты
```
### Как запускать?
Для запуска на эмуляторе в программе VS Code скомпилируйте и запустите файл main.dart (предварительно необходимо установить эмулятор в Android Studio или подключите устройство в режиме разработчика через USB)
Для тестирования на устройствах Android в корневой папке проекта выполните команду
`# flutter build apk`
после чего `.apk` файл скомпилируется в директории `build/app/outputs/flutter-apk/app-release.apk`
Данный файл установит приложение на устройство с актуальной версией OS
### Библиотеки и инструменты
- **flutter_bloc** - контроль состояний
- **cached_network_image** - кеширование картинок из веба
- **flutter_svg** - работа с изображениями
- **equatable** - сравнение без оператора "=="
- **validators** - валидация
- **hive** - локальная бд (1)
- **hive_flutter** - локальная бд (2)
- **isar_flutter_libs** - локальная бд (3)
- **path_provider** - динамичкские пути до файлов
- **fl_chart** - построение графиков
- **flutter_spinkit** - прелоадеры
- **flutter_dotenv** - переменные окружения
### Поддержка
Telegram: @maxx_kim (https://t.me/maxx_kim)
# eatvio_app

View File

@@ -19,7 +19,7 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.1" apply false
id "org.jetbrains.kotlin.android" version "1.9.10" apply false
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
}
include ":app"

View File

@@ -78,7 +78,7 @@ class _BlogPostCreateScreenState extends State<BlogPostCreateScreen> {
floatingActionButton: SizedBox(
width: MediaQuery.of(context).size.width - 32,
child: FloatingActionButton.extended(
heroTag: 'nextButtonRegistration',
heroTag: 'createBlogPost',
highlightElevation: 0,
elevation: 0,
focusElevation: 0,

View File

@@ -201,6 +201,7 @@ class DiaryScreen extends StatelessWidget {
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(left: 4, right: 4),
child: FloatingActionButton.extended(
heroTag: 'endDayButton',
highlightElevation: 0,
elevation: 0,
focusElevation: 0,
@@ -264,6 +265,7 @@ class DiaryScreen extends StatelessWidget {
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(left: 4, right: 4),
child: FloatingActionButton.extended(
heroTag: 'addProduct',
highlightElevation: 0,
elevation: 0,
focusElevation: 0,

View File

@@ -126,7 +126,7 @@ class _AuthScreenState extends State<AuthScreen> {
height: 48,
width: MediaQuery.of(context).size.width - 32,
child: FloatingActionButton.extended(
heroTag: 'nextButtonRegistration',
heroTag: 'authButton',
highlightElevation: 0,
elevation: 0,
focusElevation: 0,

View File

@@ -1,24 +1,23 @@
import 'dart:convert';
import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:carousel_slider_plus/carousel_slider_plus.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'bloc/my_profile_bloc.dart';
import 'models/my_profile_model.dart';
class MyProfileScreen extends StatelessWidget {
int valuteToInt(String value) {
// Преобразование строки в целое число
return int.tryParse(value) ?? 0; // Возвращает 0, если преобразование не удалось
}
static Widget builder(BuildContext context) {
return BlocProvider<MyProfileBloc>(
create: (context) => MyProfileBloc(
@@ -193,7 +192,7 @@ class MyProfileScreen extends StatelessWidget {
_buildCarousel(BuildContext contex, MyProfileLoadedState state) {
return CarouselSlider(
options: CarouselOptions(height: 370),
options: CarouselOptions(height: 340),
items: [1, 2, 3].map((chartType) {
return Builder(
builder: (BuildContext context) {
@@ -210,11 +209,11 @@ class MyProfileScreen extends StatelessWidget {
),
),
SizedBox(height: 50.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.chartDates,
// ),
_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.chartDates,
),
],
),
);
@@ -228,14 +227,14 @@ class MyProfileScreen extends StatelessWidget {
),
),
SizedBox(height: 50.v),
// _buildChart(
// context,
// _bodyMassIndex(
// state.chartPoints?.values.map<int>((e) => e['weight']).toList().map((i) => i.toDouble()).toList() ?? [0.0, 0.0, 0.0, 0.0],
// state.userDetails?['height'].toInt() ?? 160,
// ),
// state.chartDates,
// ),
_buildChart(
context,
_bodyMassIndex(
state.chartPoints?.values.map<int>((e) => e['weight']).toList().map((i) => i.toDouble()).toList() ?? [0.0, 0.0, 0.0, 0.0],
state.userDetails?['height'].toInt() ?? 160,
),
state.chartDates,
),
],
);
case 3: //График обхватов
@@ -250,7 +249,7 @@ class MyProfileScreen extends StatelessWidget {
),
),
SizedBox(height: 30.v),
// _buildCircumferenceChart(context, state.circumferences, state.chartDates),
_buildCircumferenceChart(context, state.circumferences, state.chartDates),
_buildChartLegend(),
],
),
@@ -296,34 +295,354 @@ class MyProfileScreen extends StatelessWidget {
);
}
// Widget _buildChart(BuildContext context, List<double> chartPoints, List<String>? dates) {
// // Проверяем, что у нас есть 4 элемента в обоих списках
// if (chartPoints.length != 4 || (dates != null && dates.length != 4)) {
// return Center(child: Text('Должно быть ровно 4 точки и 4 даты.'));
// }
_buildChart(BuildContext context, List<double> chartPoints, List<String>? dates) {
List<FlSpot> spots = [];
for (int i = 0; i < 4; i++) {
spots.add(
FlSpot((i * 10 / 10) + 1, chartPoints[i]),
);
}
// // Преобразуем данные в список точек для графика
// List<ChartData> chartData = List.generate(4, (index) {
// return ChartData(dates![index], chartPoints[index]);
// });
Widget bottomTitleWidgets(double value, TitleMeta meta, double chartWidth, List<String>? dates) {
if (value % 1 != 0) {
return Container();
}
final style = TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
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();
}
}
// return Container(
// height: 300, // Задайте нужную высоту для графика
// child: SfCartesianChart(
// title: ChartTitle(text: 'График значений'),
// primaryXAxis: CategoryAxis(),
// series: <CartesianSeries<ChartData, String>>[
// // Изменяем на CartesianSeries
// LineSeries<ChartData, String>(
// dataSource: chartData,
// xValueMapper: (ChartData data, _) => data.date,
// yValueMapper: (ChartData data, _) => data.value,
// dataLabelSettings: DataLabelSettings(isVisible: true),
// ),
// ],
// ),
// );
// }
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,
right: 20,
),
child: AspectRatio(
aspectRatio: 1,
child: LayoutBuilder(
builder: (context, constraints) {
return LineChart(
LineChartData(
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
maxContentWidth: 100,
// tooltipBgColor: Colors.black,
getTooltipItems: mapSpots(),
),
handleBuiltInTouches: true,
getTouchLineStart: (data, index) => 0,
),
lineBarsData: [
LineChartBarData(
color: Colors.pink,
spots: spots,
isCurved: true,
isStrokeCapRound: true,
barWidth: 3,
belowBarData: BarAreaData(
show: false,
),
dotData: const FlDotData(show: false),
),
],
minY: chartPoints.reduce((value, element) => value < element ? value : element),
maxY: chartPoints.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, dates),
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: valuteToInt(),
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: valuteToInt(),
),
borderData: FlBorderData(show: false),
),
);
},
),
),
);
}
valuteToInt() {
return (value) {
return value.toInt() == 0;
};
}
List<LineTooltipItem?> Function(List<LineBarSpot>) mapSpots() {
return (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();
};
}
_buildCircumferenceChart(BuildContext context, chartPoints, List<String>? dates) {
// Определение осей графика
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, List<String>? dates) {
if (value % 1 != 0) {
return Container();
}
final style = TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
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) {
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,
right: 20,
),
child: AspectRatio(
aspectRatio: 1,
child: LayoutBuilder(
builder: (context, constraints) {
return LineChart(
LineChartData(
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
maxContentWidth: 100,
// tooltipBgColor: Colors.black,
getTooltipItems: mapTouchedSpots(),
),
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, dates),
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: valuteToInt(),
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: valuteToInt(),
),
borderData: FlBorderData(show: false),
),
);
},
),
),
);
}
List<LineTooltipItem?> Function(List<LineBarSpot>) mapTouchedSpots() {
return (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();
};
}
/// Section Widget
_buildPlans(BuildContext context, List<dynamic>? plans, List<dynamic>? imagePaths, List<dynamic>? states) {
@@ -643,16 +962,3 @@ class MyProfileScreen extends StatelessWidget {
};
}
}
class ChartData {
final String date;
final double value;
ChartData(this.date, this.value);
valuteToInt() {
return (value) {
return value.toInt() == 0;
};
}
}

View File

@@ -23,7 +23,6 @@ dependencies:
freezed_annotation: ^2.4.4
json_annotation: ^4.9.0
validators: ^3.0.0
isar_flutter_libs: ^3.1.0
path_provider: ^2.1.4
fl_chart: ^0.68.0
flutter_spinkit: ^5.2.1
@@ -45,7 +44,7 @@ dependencies:
badges: ^3.1.2
google_fonts: ^6.2.1
location: ^6.0.2
# syncfusion_flutter_charts: ^26.2.9
syncfusion_flutter_charts: ^26.2.10
table_calendar: ^3.1.2
flutter_keyboard_visibility: ^6.0.0
share_plus: ^10.0.0