import 'package:auto_route/auto_route.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_keyboard_size/flutter_keyboard_size.dart'; import 'package:remever/common/functions.dart'; import 'package:remever/common/resources.dart'; import 'package:remever/common/widgets/typography.dart'; import 'package:remever/common/widgets/wspace.dart'; import 'package:remever/components/extensions/context.dart'; import 'package:remever/gen/assets.gen.dart'; import 'package:remever/screens/dialogs/alert_dialog.dart'; @RoutePage() class CrudCollectionFullscreenField extends StatefulWidget { const CrudCollectionFullscreenField({ super.key, this.title = '', this.hint, this.content, this.height = 92, required this.onEditingComplete, }); final String title; final double height; final String? hint; final String? content; final void Function(String?) onEditingComplete; @override State createState() => _CrudCollectionFullscreenFieldState(); } class _CrudCollectionFullscreenFieldState extends State { final TextEditingController _controller = TextEditingController(); @override void initState() { super.initState(); if (widget.content != null) { _controller.text = widget.content!; } } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return KeyboardSizeProvider( child: SafeArea( top: false, child: Scaffold( backgroundColor: AppColors.gray_bg, appBar: _buildAppBar(), body: _buildMainBody(), ), ), ); } Widget _buildMainBody() { return Stack( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16).r, child: SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const HSpace(16), _buildField(), if (widget.hint != null) ...[ const HSpace(16), AppTypography( widget.hint!, type: Regular14px(), color: AppColors.disabled, ), ], const HSpace(50), ], ), ), ), Align(alignment: Alignment.bottomCenter, child: _buildMenu()), ], ); } Widget _buildMenu() { return Consumer( builder: (_, screenHeight, __) { return AnimatedOpacity( opacity: 1, duration: const Duration(milliseconds: 500), child: Container( height: 64.h, decoration: BoxDecoration( color: AppColors.white, border: Border( top: BorderSide(color: AppColors.gray, width: 1.w), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildPasteButton(), _buildCopyButton(), _buildSubmitButton(), ], ), ), ); }, ); } Widget _buildPasteButton() { return GestureDetector( onTap: _onPasteTap, child: Assets.icons.typePaste.image(height: 24.h, width: 24.w), ); } Future _onPasteTap() async { try { final data = await Clipboard.getData(Clipboard.kTextPlain); if (data?.text?.isEmpty ?? true) { showErrorToast('Не удалось получить текст из буфера обмена'); return; } _controller.text += ' ${data!.text}'; showSuccessToast('Текст вставлен из буфера обмена'); } catch (e) { showErrorToast('Ошибка при вставке текста: $e'); } } Widget _buildCopyButton() { return GestureDetector( onTap: _onCopyTap, child: Assets.icons.typeCopy.image(height: 24.h, width: 24.w), ); } Future _onCopyTap() async { if (_controller.text.isEmpty) { showErrorToast('Нет содержимого для отправки в буфер обмена'); return; } try { await Clipboard.setData(ClipboardData(text: _controller.text)); showSuccessToast('Текст скопирован в буфер обмена'); } catch (e) { showErrorToast('Ошибка при копировании текста: $e'); } } Widget _buildSubmitButton() { return GestureDetector( onTap: _onSubmitTap, child: SizedBox.square( dimension: 32.r, child: const DecoratedBox( decoration: BoxDecoration( shape: BoxShape.circle, color: AppColors.primary, ), child: Center( child: Icon(Icons.check, color: AppColors.white, size: 24), ), ), ), ); } void _onSubmitTap() { widget.onEditingComplete(_controller.text); context.back(); } Widget _buildField() { return SizedBox( height: widget.height.h, child: DecoratedBox( decoration: BoxDecoration( color: AppColors.white, borderRadius: BorderRadius.circular(12).r, ), child: Padding( padding: const EdgeInsets.all(12).r, child: TextField( autofocus: true, controller: _controller, textCapitalization: TextCapitalization.sentences, maxLines: 99, maxLength: 250, cursorColor: AppColors.danger, decoration: const InputDecoration.collapsed( hintText: 'Введите содержимое', hintStyle: TextStyle(color: AppColors.gray), ), ), ), ), ); } AppBar _buildAppBar() { return AppBar( toolbarHeight: 56.h, backgroundColor: AppColors.white, shadowColor: Colors.transparent, leading: IconButton( onPressed: () => _handleBackPress(), icon: const Icon(CupertinoIcons.left_chevron, color: Colors.black), ), centerTitle: true, title: AppTypography( widget.title, type: SemiBold20px(), color: AppColors.body_text, ), actions: [ Padding( padding: const EdgeInsets.only(right: 16).r, child: GestureDetector( onTap: _showResetDialog, child: Assets.icons.typeTrash.image( height: 24.h, width: 24.w, color: AppColors.danger, ), ), ), ], ); } Future _handleBackPress() async { final shouldExit = await _showExitDialog(); if (shouldExit ?? false) { context.back(); } } Future _showExitDialog() async { final res = await showCuperModalBottomSheet( context: context, height: 262.h, builder: (_) => const AlertInfoDialog( title: 'У вас есть несохраненные изменения', acceptTitle: 'Выйти', declineTitle: 'Сохранить и выйти', ), ); if (res == null) return false; if (res) return true; widget.onEditingComplete(_controller.text); return true; } Future _showResetDialog() async { final res = await showCuperModalBottomSheet( context: context, height: 262.h, builder: (_) => AlertInfoDialog( title: 'Удалить вcе содержимое поля "${widget.title}"?', acceptTitle: 'Удалить', declineTitle: 'Отменить', ), ); if (res == true) { _controller.clear(); } } }