fix: ui overflow nav-bar, name files in chat, qr-code scanner in dev-tools, message field size, keyboard type

This commit is contained in:
mso7nueh
2025-12-22 16:48:13 +03:00
parent f56092d88f
commit 42b443c3ba
61 changed files with 759 additions and 393 deletions

View File

@@ -10,8 +10,12 @@ import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/dev_tools/test_cases.dart';
import 'package:eatvio_app/core/env_config.dart';
import 'package:eatvio_app/features/diary/presentation/pages/barcode_scanner_screen.dart';
import 'package:eatvio_app/features/barcode/repositories/barcode_repository.dart';
import 'package:eatvio_app/features/barcode/models/barcode_product_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
class DevToolsScreen extends StatelessWidget {
const DevToolsScreen({super.key});
@@ -296,6 +300,20 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
}
},
),
const SizedBox(height: 10),
CustomOutlinedButton(
text: 'Сканирование QR/штрихкода',
buttonStyle: const ButtonStyle(),
onPressed: () async {
if (!context.mounted) return;
final barcode = await Navigator.of(context).push<String>(
MaterialPageRoute(builder: (context) => const BarcodeScannerScreen()),
);
if (barcode != null && barcode.isNotEmpty && context.mounted) {
await _handleBarcodeScanned(barcode, context);
}
},
),
const SizedBox(height: 300),
],
),
@@ -303,6 +321,195 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
),
);
}
Future<void> _handleBarcodeScanned(String barcode, BuildContext context) async {
try {
final barcodeRepository = sl<BarcodeRepository>();
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const AlertDialog(
content: Row(
children: [
CircularProgressIndicator(),
SizedBox(width: 16),
Text('Загрузка данных...'),
],
),
),
);
final result = await barcodeRepository.getProductByBarcode(barcode);
if (!context.mounted) return;
Navigator.of(context).pop(); // Закрываем индикатор загрузки
if (result.type == BarcodeResultType.success && result.product != null) {
// Данные получены сразу
_showProductDialog(context, result.product!, barcode);
} else if (result.type == BarcodeResultType.processing && result.taskId != null) {
// Запущена задача обработки, нужно опрашивать статус
await _pollTaskStatus(context, result.taskId!, barcode);
}
} catch (e) {
if (!context.mounted) return;
Navigator.of(context).pop(); // Закрываем индикатор загрузки если открыт
showErrorToast('Ошибка: $e');
_showBarcodeDialog(context, barcode, null);
}
}
Future<void> _pollTaskStatus(BuildContext context, String taskId, String barcode) async {
final barcodeRepository = sl<BarcodeRepository>();
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 16),
const Text('Обработка штрихкода...'),
const SizedBox(height: 8),
Text('Это может занять некоторое время', style: TextStyle(fontSize: 12, color: Colors.grey[600])),
],
),
),
);
final completer = Completer<void>();
int attempts = 0;
const maxAttempts = 30; // Максимум 30 попыток (примерно 5 минут при интервале 10 секунд)
// ignore: unused_local_variable
final timer = Timer.periodic(const Duration(seconds: 10), (timer) async {
attempts++;
try {
final status = await barcodeRepository.getTaskStatus(taskId);
if (status.type == BarcodeTaskStatusType.completed && status.product != null) {
timer.cancel();
if (!context.mounted) return;
Navigator.of(context).pop(); // Закрываем индикатор загрузки
_showProductDialog(context, status.product!, barcode);
completer.complete();
} else if (status.type == BarcodeTaskStatusType.failed) {
timer.cancel();
if (!context.mounted) return;
Navigator.of(context).pop(); // Закрываем индикатор загрузки
showErrorToast('Не удалось получить данные о продукте');
_showBarcodeDialog(context, barcode, null);
completer.complete();
} else if (attempts >= maxAttempts) {
timer.cancel();
if (!context.mounted) return;
Navigator.of(context).pop(); // Закрываем индикатор загрузки
showErrorToast('Превышено время ожидания');
_showBarcodeDialog(context, barcode, null);
completer.complete();
}
} catch (e) {
timer.cancel();
if (!context.mounted) return;
Navigator.of(context).pop(); // Закрываем индикатор загрузки
showErrorToast('Ошибка при проверке статуса: $e');
_showBarcodeDialog(context, barcode, null);
completer.complete();
}
});
await completer.future;
}
void _showProductDialog(BuildContext context, BarcodeProductModel product, String barcode) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Информация о продукте'),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
_buildInfoRow('Штрихкод:', barcode),
if (product.brandName != null) _buildInfoRow('Бренд:', product.brandName!),
if (product.description != null) _buildInfoRow('Описание:', product.description!),
if (product.quantity != null) _buildInfoRow('Количество:', product.quantity!),
if (product.market != null) _buildInfoRow('Рынок сбыта:', product.market!),
if (product.calories != null) _buildInfoRow('Калорийность:', '${product.calories} ккал'),
if (product.proteins != null) _buildInfoRow('Белки:', '${product.proteins} г'),
if (product.fats != null) _buildInfoRow('Жиры:', '${product.fats} г'),
if (product.carbohydrates != null) _buildInfoRow('Углеводы:', '${product.carbohydrates} г'),
],
),
),
actions: [
TextButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: barcode));
showInfoToast('Штрихкод скопирован в буфер обмена');
},
child: const Text('Скопировать код'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Закрыть'),
),
],
),
);
}
void _showBarcodeDialog(BuildContext context, String barcode, BarcodeProductModel? product) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Отсканированный код'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(barcode),
if (product == null) ...[
const SizedBox(height: 16),
const Text('Данные о продукте не найдены', style: TextStyle(color: Colors.grey)),
],
],
),
actions: [
TextButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: barcode));
Navigator.of(context).pop();
showInfoToast('Код скопирован в буфер обмена');
},
child: const Text('Скопировать'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Закрыть'),
),
],
),
);
}
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: RichText(
text: TextSpan(
style: const TextStyle(color: Colors.black, fontSize: 14),
children: [
TextSpan(text: '$label ', style: const TextStyle(fontWeight: FontWeight.bold)),
TextSpan(text: value),
],
),
),
);
}
}
enum TestState { success, fail, notRun, loading }