forked from eatvio/eatvio_app
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:
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user