fix-errors

This commit is contained in:
mso7nueh
2026-06-01 12:42:22 +03:00
parent af3b40d5f2
commit 85d32dd3a1
8 changed files with 82 additions and 18 deletions

View File

@@ -1,3 +1,7 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
# This builtInKotlin flag was added automatically by Flutter migrator
android.builtInKotlin=false
# This newDsl flag was added automatically by Flutter migrator
android.newDsl=false

View File

@@ -0,0 +1,12 @@
FLUTTER_ROOT=C:\Users\Alexander\fvm\versions\3.44.0
FLUTTER_APPLICATION_PATH=C:\Users\Alexander\eatvio_app
FLUTTER_FRAMEWORK_SWIFT_PACKAGE_PATH=C:\Users\Alexander\eatvio_app\ios\Flutter\ephemeral\Packages\.packages\FlutterFramework
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_TARGET=lib\main.dart
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=1.3.23
FLUTTER_BUILD_NUMBER=105
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.dart_tool/package_config.json

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:developer';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/env_config.dart';
import 'package:eatvio_app/core/internet_connection_cubit/internet_connection_state.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
@@ -9,7 +10,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:eatvio_app/core/error_reporting/report_from.dart';
import 'package:http/http.dart' as http;
class InternetConnectionCubit extends Cubit<InternetConnectionState> {
InternetConnectionCubit() : super(InternetConnectionState()) {
@@ -29,6 +29,16 @@ class InternetConnectionCubit extends Cubit<InternetConnectionState> {
late StreamSubscription<List<ConnectivityResult>> _connectivitySubscription;
late final AppLifecycleListener _listener;
/// Отдельный легковесный Dio без авторизационных интерцепторов —
/// используется только для проверки доступности сети.
final Dio _checkDio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 5),
followRedirects: true,
// Любой HTTP-ответ = интернет доступен
validateStatus: (status) => true,
));
Future<void> checkConnection() async {
late List<ConnectivityResult> result;
@@ -50,40 +60,61 @@ class InternetConnectionCubit extends Cubit<InternetConnectionState> {
// Показываем toast только при переходе online → offline,
// а не при каждом срабатывании проверки
if (wasOnline && !hasInternet) showInternetConnectionToast();
emit(state.copyWith(isInternetAvailable: hasInternet));
if (!isClosed) {
emit(state.copyWith(isInternetAvailable: hasInternet));
}
} catch (e, st) {
await reportFrom(e, st, this);
}
}
Future<bool> _checkInternetAccess(List<ConnectivityResult> result) async {
// connectivity_plus может ошибочно возвращать 'none' на эмуляторах
// и некоторых устройствах, поэтому ВСЕГДА проверяем через HTTP.
// Если connectivity говорит 'none' — используем короткий таймаут.
final isLikelyOffline = result.contains(ConnectivityResult.none);
final timeout = Duration(seconds: isLikelyOffline ? 2 : 3);
log('[InternetCheck] connectivity result: $result');
final uris = [
Uri.parse(EnvConfig.googleCheckUrl),
Uri.parse(EnvConfig.yandexCheckUrl),
Uri.parse(EnvConfig.baseUrl),
final rawUrls = [
EnvConfig.googleCheckUrl,
EnvConfig.yandexCheckUrl,
EnvConfig.baseUrl,
];
final futures = uris.map((uri) =>
http.get(uri)
.timeout(timeout)
.then((r) => r.statusCode >= 100 && r.statusCode < 600)
.catchError((_) => false)
final urls = rawUrls
.map((u) => u.trim())
.where((u) => u.isNotEmpty && Uri.tryParse(u)?.hasScheme == true)
.toList();
if (urls.isEmpty) {
log('[InternetCheck] No valid check URLs configured, assuming online');
return true;
}
final futures = urls.map((url) =>
_checkDio.get<void>(url)
.then((_) {
log('[InternetCheck] $url → OK');
return true;
})
.catchError((Object e) {
// DioException с ответом от сервера = интернет есть
if (e is DioException && e.response != null) {
log('[InternetCheck] $url → ${e.response!.statusCode} (has response, internet OK)');
return true;
}
log('[InternetCheck] $url → FAILED: $e');
return false;
})
);
final results = await Future.wait(futures);
return results.any((ok) => ok);
final hasInternet = results.any((ok) => ok);
log('[InternetCheck] final result: hasInternet=$hasInternet');
return hasInternet;
}
@override
Future<void> close() {
_connectivitySubscription.cancel();
_listener.dispose();
_checkDio.close();
return super.close();
}
}

View File

@@ -47,6 +47,7 @@ class _IngredientsWidgetState extends State<IngredientsWidget> {
ReorderableListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
// ignore: deprecated_member_use
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {

View File

@@ -116,6 +116,9 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
builder: (context, state) {
if (state is DiaryStateLoaded) {
int mealIndex = state.selectedMeal - 1;
if (mealIndex < 0 || mealIndex >= state.diary.meals.length) {
return const SizedBox.shrink();
}
final photos = state.diary.meals[mealIndex].photos;
// BUG-5 fix: сбросить индекс, если он вышел за пределы
// (например, после переключения на meal с меньшим числом фото)

View File

@@ -20,6 +20,9 @@ class _ProductsListWidgetState extends State<ProductsListWidget> {
builder: (BuildContext context, DiaryState state) {
if (state is DiaryStateLoaded) {
int mealIndex = state.selectedMeal - 1;
if (mealIndex < 0 || mealIndex >= state.diary.meals.length) {
return const SizedBox.shrink();
}
final portions = state.diary.meals[mealIndex].portions;
return Column(
mainAxisSize: MainAxisSize.min,
@@ -35,6 +38,9 @@ class _ProductsListWidgetState extends State<ProductsListWidget> {
}
if (state is DiaryStateLoadedTomorrowWithDiet) {
int mealIndex = state.selectedMeal - 1;
if (mealIndex < 0 || mealIndex >= state.diary.meals.length) {
return const SizedBox.shrink();
}
final portions = state.diary.meals[mealIndex].portions;
return Column(
mainAxisSize: MainAxisSize.min,

View File

@@ -87,6 +87,11 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
return BlocBuilder<DiaryCubit, DiaryState>(
builder: (context, state) {
if (state is DiaryStateLoaded) {
final meals = state.diary.meals;
final mealIndex = state.selectedMeal - 1;
if (mealIndex < 0 || mealIndex >= meals.length) {
return const SizedBox.shrink();
}
return Column(
children: [
const CustomDivider(),

View File

@@ -12,7 +12,9 @@ class FeedProfileCubit extends Cubit<ProfileState> {
Future<void> getProfile() async {
try {
final profile = await profileRepository.getMyProfile();
emit(state.copyWith(profile: profile));
if (!isClosed) {
emit(state.copyWith(profile: profile));
}
} catch (e, stackTrace) {
await reportFrom(e, stackTrace, this);
log(e.toString(), stackTrace: stackTrace);