forked from eatvio/eatvio_app
refactor: remove unused libs
This commit is contained in:
@@ -4,7 +4,6 @@ import 'package:intl/intl.dart';
|
||||
const String dateTimeFormatPattern = 'dd/MM/yyyy';
|
||||
|
||||
extension DateTimeExtension on DateTime {
|
||||
/// Return a string representing [date] formatted according to our locale
|
||||
String format([
|
||||
String pattern = dateTimeFormatPattern,
|
||||
String? locale,
|
||||
|
||||
@@ -30,12 +30,33 @@ class MyApp extends StatelessWidget {
|
||||
],
|
||||
child: KeyboardDismisser(
|
||||
child: MaterialApp.router(
|
||||
theme: ThemeData.light().copyWith(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: appTheme.blueA400),
|
||||
),
|
||||
darkTheme: ThemeData.dark().copyWith(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: appTheme.blueA400),
|
||||
),
|
||||
theme: ThemeHelper().themeData(),
|
||||
// theme: ThemeData.light().copyWith(
|
||||
// colorScheme: ColorScheme.fromSeed(
|
||||
// seedColor: appTheme.blueA400,
|
||||
// brightness: Brightness.light,
|
||||
// ),
|
||||
// appBarTheme: AppBarTheme(
|
||||
// systemOverlayStyle: SystemUiOverlayStyle.light.copyWith(
|
||||
// statusBarIconBrightness: Brightness.dark,
|
||||
// systemNavigationBarIconBrightness: Brightness.dark,
|
||||
// systemNavigationBarColor: ThemeData.light().scaffoldBackgroundColor,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// darkTheme: ThemeData.dark().copyWith(
|
||||
// colorScheme: ColorScheme.fromSeed(
|
||||
// seedColor: appTheme.blueA400,
|
||||
// brightness: Brightness.dark,
|
||||
// ),
|
||||
// appBarTheme: AppBarTheme(
|
||||
// systemOverlayStyle: SystemUiOverlayStyle.dark.copyWith(
|
||||
// statusBarIconBrightness: Brightness.light,
|
||||
// systemNavigationBarIconBrightness: Brightness.light,
|
||||
// systemNavigationBarColor: ThemeData.dark().scaffoldBackgroundColor,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
themeMode: ThemeMode.system,
|
||||
routerConfig: AppRouter.appRouter,
|
||||
debugShowCheckedModeBanner: false,
|
||||
|
||||
@@ -1,972 +0,0 @@
|
||||
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/const.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:eatvio_app/theme/app_decoration.dart';
|
||||
import 'package:eatvio_app/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/theme/theme_helper.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:flutter_bloc/flutter_bloc.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 {
|
||||
static Widget builder(BuildContext context) {
|
||||
return BlocProvider<MyProfileBloc>(
|
||||
create: (context) => MyProfileBloc(
|
||||
MyProfileState(
|
||||
myProfileModelObj: MyProfileModel(),
|
||||
),
|
||||
)..add(MyProfileInitialEvent()),
|
||||
child: MyProfileScreen(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<MyProfileBloc, MyProfileState>(
|
||||
builder: (context, state) {
|
||||
if (state is MyProfileInitialState) {
|
||||
// Это начальное состояние, данные ещё не загружены.
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
} else if (state is MyProfileLoadedState) {
|
||||
// Данные загружены, можно строить экран.
|
||||
return _buildMyProfileScreen(context, state);
|
||||
} else if (state is MyProfileErrorState) {
|
||||
// Произошла ошибка при загрузке данных.
|
||||
return Center(child: Text(state.errorMessage));
|
||||
} else {
|
||||
// Другие состояния, если они есть.
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_buildMyProfileScreen(BuildContext context, MyProfileLoadedState state) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
toolbarHeight: 0,
|
||||
automaticallyImplyLeading: false,
|
||||
),
|
||||
backgroundColor: Colors.grey[200],
|
||||
resizeToAvoidBottomInset: true,
|
||||
body: SingleChildScrollView(
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
decoration: BoxDecoration(color: Colors.grey[200]),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 30),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _buildInfo(context, state),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _buildCarousel(context, state),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
if (!state.plans!.isEmpty)
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _buildPlans(
|
||||
context,
|
||||
state.plans?.map((plan) => plan['name']).toList(),
|
||||
state.plans?.map((plan) => plan['image']).toList(),
|
||||
state.plans?.map((plan) => plan['state']).toList(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
_buildInfo(BuildContext context, MyProfileLoadedState state) {
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => addImage(context),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: baseUrl + '/storage/' + (state.user?['profile_photo_path'] ?? ''),
|
||||
imageBuilder: (context, imageProvider) => CircleAvatar(
|
||||
backgroundImage: imageProvider,
|
||||
radius: 60,
|
||||
),
|
||||
placeholder: (context, url) => CircularProgressIndicator(),
|
||||
errorWidget: (context, url, error) => CircleAvatar(
|
||||
child: Icon(Icons.person),
|
||||
backgroundColor: Colors.white,
|
||||
radius: 60,
|
||||
),
|
||||
),
|
||||
),
|
||||
AppbarSubtitle(text: state.user?['name'] ?? ''),
|
||||
SizedBox(height: 20),
|
||||
Text(state.userDetails?['additional_info'] ?? ''),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
addImage(BuildContext context) async {
|
||||
final picker = ImagePicker();
|
||||
XFile? image = await picker.pickImage(source: ImageSource.gallery);
|
||||
if (image != null) {
|
||||
context.read<MyProfileBloc>().add(UploadAvatar(image));
|
||||
}
|
||||
}
|
||||
|
||||
onTapSettings(BuildContext context) {
|
||||
return () {
|
||||
GoRouter.of(context).goNamed('mySettingsScreen');
|
||||
};
|
||||
}
|
||||
|
||||
_buildCarousel(BuildContext context, MyProfileLoadedState state) {
|
||||
final s = S.of(context);
|
||||
return CarouselSlider(
|
||||
options: CarouselOptions(height: 340),
|
||||
items: [1, 2, 3].map((chartType) {
|
||||
return Builder(
|
||||
builder: (BuildContext context) {
|
||||
switch (chartType) {
|
||||
case 1: // График веса
|
||||
return GestureDetector(
|
||||
onTap: () => _editWeight(context, state),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
s.weight_chart,
|
||||
style: CustomTextStyles.titleMediumBlack.copyWith(
|
||||
color: appTheme.black900,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 50),
|
||||
_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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
case 2: // График ИМТ
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
s.bodymass_chart,
|
||||
style: CustomTextStyles.titleMediumBlack.copyWith(
|
||||
color: appTheme.black900,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 50),
|
||||
_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: //График обхватов
|
||||
return GestureDetector(
|
||||
onTap: () => _editCircumference(context, state),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
s.circumferences_chart,
|
||||
style: CustomTextStyles.titleMediumBlack.copyWith(
|
||||
color: appTheme.black900,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
_buildCircumferenceChart(context, state.circumferences, state.chartDates),
|
||||
_buildChartLegend(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
default:
|
||||
return Text('text', style: TextStyle(fontSize: 16.0));
|
||||
}
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
_buildChartLegend(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
final colors = [Colors.pink, Colors.green, Colors.purple];
|
||||
final titles = [s.chest, s.waist, s.hips];
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_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]),
|
||||
);
|
||||
}
|
||||
|
||||
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: 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) {
|
||||
final s = S.of(context);
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(left: 7),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 15),
|
||||
child: Text(s.plans_complete, style: CustomTextStyles.titleMediumBlack.copyWith(color: appTheme.black900)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Container(
|
||||
decoration: AppDecoration.outlineBlueGray.copyWith(borderRadius: BorderRadiusStyle.roundedBorder5),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 105,
|
||||
width: 343,
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ListView.builder(
|
||||
itemCount: plans?.length ?? 0,
|
||||
itemBuilder: (context, i) {
|
||||
String state = states?[i];
|
||||
var boxColor = appTheme.greenA700;
|
||||
switch (states?[i]) {
|
||||
case 'active':
|
||||
boxColor = appTheme.greenA700;
|
||||
break;
|
||||
case 'archived': // График ИМТ
|
||||
boxColor = appTheme.gray400;
|
||||
break;
|
||||
case 'draft':
|
||||
boxColor = appTheme.deepOrange900;
|
||||
break;
|
||||
case 'in_progress': //График обхватов
|
||||
boxColor = appTheme.lightGreen300;
|
||||
break;
|
||||
}
|
||||
return SizedBox(
|
||||
height: 40,
|
||||
width: 343,
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomRight,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(bottom: 11),
|
||||
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
||||
decoration: AppDecoration.fillGray.copyWith(borderRadius: BorderRadiusStyle.roundedBorder5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 1),
|
||||
child: Text(plans![i], style: CustomTextStyles.titleSmallBluegray800Bold),
|
||||
),
|
||||
Container(
|
||||
height: 19,
|
||||
width: 100,
|
||||
decoration: BoxDecoration(color: boxColor, borderRadius: BorderRadius.circular(5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(bottom: 12),
|
||||
child: Image.network(
|
||||
baseUrl + '/storage/' + imagePaths?[i],
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Container(
|
||||
width: 70,
|
||||
margin: EdgeInsets.only(bottom: 14, right: 22),
|
||||
child: Text("$state", maxLines: 3, overflow: TextOverflow.ellipsis, style: theme.textTheme.titleSmall),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<double> _bodyMassIndex(List<double> weightPoints, int height) {
|
||||
List<double> bmiList = [];
|
||||
for (double weight in weightPoints) {
|
||||
double bmi = weight / ((height / 100) * (height / 100));
|
||||
bmiList.add(bmi);
|
||||
}
|
||||
return bmiList;
|
||||
}
|
||||
|
||||
_editCircumference(BuildContext context, MyProfileLoadedState state) {
|
||||
final s = S.of(context);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
//final digitsOnly = FilteringTextInputFormatter.digitsOnly;
|
||||
return AlertDialog(
|
||||
title: Center(child: Text("Добавить замер")),
|
||||
|
||||
content: SizedBox(
|
||||
width: 200, // Ширина диалогового окна
|
||||
height: 300, // Высота диалогового окна
|
||||
child: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 5),
|
||||
child: Text(s.chest_circumference, style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFormField(
|
||||
controller: state.chestCircumferenceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.digitsOnly, // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 15),
|
||||
child: Text(s.waist_circumference, style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFormField(
|
||||
controller: state.waistCircumferenceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.digitsOnly, // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 15),
|
||||
child: Text(s.hip_circumference, style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFormField(
|
||||
controller: state.hipCircumferenceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.digitsOnly, // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: onTapOk(context),
|
||||
child: Text("Отмена", style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: sendEditData(state, context),
|
||||
child: Text(
|
||||
"Изменить",
|
||||
style: CustomTextStyles.titleMediumBlack.copyWith(
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
backgroundColor: Colors.white,
|
||||
titlePadding: EdgeInsets.all(20.0), // Можете настроить отступы по вашему желанию
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
sendEditData(MyProfileLoadedState state, BuildContext context) {
|
||||
return () async {
|
||||
Map<String, dynamic> data = {
|
||||
"chest_circumference": state.chestCircumferenceController?.text,
|
||||
"waist_circumference": state.waistCircumferenceController?.text,
|
||||
"hip_circumference": state.hipCircumferenceController?.text,
|
||||
};
|
||||
|
||||
// Преобразование Map в JSON
|
||||
String jsonData = json.encode(data);
|
||||
|
||||
// Отправка HTTP-запроса
|
||||
try {
|
||||
final token = state.token;
|
||||
final response = await http.post(
|
||||
Uri.parse(baseUrl + '/api/profile/addCircumference'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: jsonData,
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
GoRouter.of(context).go('/my-profile-screen');
|
||||
} else {
|
||||
// Ошибка от сервера
|
||||
print("Ошибка при отправке данных. Код ответа: ${response.statusCode}");
|
||||
}
|
||||
} catch (e) {
|
||||
// Обработка ошибок при отправке запроса
|
||||
print("Ошибка при отправке запроса: $e");
|
||||
}
|
||||
Navigator.pop(context);
|
||||
};
|
||||
}
|
||||
|
||||
_editWeight(BuildContext context, MyProfileLoadedState state) {
|
||||
final s = S.of(context);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
content: SizedBox(
|
||||
width: 200, // Ширина диалогового окна
|
||||
height: 100, // Высота диалогового окна
|
||||
child: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 5),
|
||||
child: Text(s.weight, style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFormField(
|
||||
controller: state.weightController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text("Отмена", style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => saveWeight(state, context),
|
||||
child: Text(
|
||||
"Изменить",
|
||||
style: CustomTextStyles.titleMediumBlack.copyWith(
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
backgroundColor: Colors.white,
|
||||
titlePadding: EdgeInsets.all(20.0),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void saveWeight(MyProfileLoadedState state, BuildContext context) {
|
||||
addWeight(state, context);
|
||||
Navigator.of(context).pop(); // Закрыть диалоговое окно после изменения веса
|
||||
}
|
||||
|
||||
onTapOk(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pop(context);
|
||||
};
|
||||
}
|
||||
|
||||
addWeight(MyProfileLoadedState state, BuildContext context) {
|
||||
return () async {
|
||||
Map<String, dynamic> data = {
|
||||
"weight": state.weightController!.text,
|
||||
};
|
||||
// Преобразование Map в JSON
|
||||
String jsonData = json.encode(data);
|
||||
|
||||
// Отправка HTTP-запроса
|
||||
try {
|
||||
final token = state.token;
|
||||
final response = await http.post(
|
||||
Uri.parse(baseUrl + '/api/profile/addWeight'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: jsonData,
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
GoRouter.of(context).go('/my-profile-screen');
|
||||
} else {
|
||||
// Ошибка от сервера
|
||||
print("Ошибка при отправке данных. Код ответа: ${response.statusCode}");
|
||||
}
|
||||
} catch (e) {
|
||||
// Обработка ошибок при отправке запроса
|
||||
print("Ошибка при отправке запроса: $e");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -137,20 +137,8 @@ class BorderRadiusStyle {
|
||||
);
|
||||
}
|
||||
|
||||
// Comment/Uncomment the below code based on your Flutter SDK version.
|
||||
|
||||
// For Flutter SDK Version 3.7.2 or greater.
|
||||
|
||||
double get strokeAlignInside => BorderSide.strokeAlignInside;
|
||||
|
||||
double get strokeAlignCenter => BorderSide.strokeAlignCenter;
|
||||
|
||||
double get strokeAlignOutside => BorderSide.strokeAlignOutside;
|
||||
|
||||
// For Flutter SDK Version 3.7.1 or less.
|
||||
|
||||
// StrokeAlign get strokeAlignInside => StrokeAlign.inside;
|
||||
//
|
||||
// StrokeAlign get strokeAlignCenter => StrokeAlign.center;
|
||||
//
|
||||
// StrokeAlign get strokeAlignOutside => StrokeAlign.outside;
|
||||
|
||||
@@ -17,20 +17,12 @@ dependencies:
|
||||
flutter_bloc: ^8.1.6
|
||||
cached_network_image: ^3.4.1
|
||||
flutter_svg: ^2.0.10+1
|
||||
# remove
|
||||
equatable: ^2.0.5
|
||||
freezed_annotation: ^2.4.4
|
||||
json_annotation: ^4.9.0
|
||||
# remove?
|
||||
validators: ^3.0.0
|
||||
path_provider: ^2.1.4
|
||||
# remove?
|
||||
fl_chart: ^0.69.0
|
||||
# remove?
|
||||
flutter_spinkit: ^5.2.1
|
||||
# remove?
|
||||
carousel_slider_plus: ^7.0.1
|
||||
# remove?
|
||||
flutter_form_builder: ^9.4.1
|
||||
image_picker: ^1.1.2
|
||||
flutter_typeahead: ^5.2.0
|
||||
|
||||
Reference in New Issue
Block a user