/
siblun
/
ReadFlow
Обзор
Документация
Войти
/
siblun
/
ReadFlow
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
lib/presentation/widgets/note_editor_sheet.dart
233 строки
7 KB
siblun
Refactoring
05 май 2026, 05:44
05 май 2026, 05:44
c76c9b2
Код
Авторство
О чём код?
import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:go_router/go_router.dart'; import 'package:uuid/uuid.dart'; import '../../domain/models/note_model.dart'; import '../theme/app_colors.dart'; import '../theme/app_typography.dart'; import 'note_editor_widget.dart'; /// Модальный интерфейс для создания или редактирования существующей заметки. /// /// Особенности: /// - **Инициализация**: Умеет подтягивать данные из [existingNote] для режима редактирования. /// - **ID Generation**: Использует пакет [Uuid] для генерации уникальных идентификаторов /// при создании новых записей. /// - **Keyboard Handling**: Управляет отступами через [viewInsets.bottom] и /// принудительно закрывает клавиатуру через [FocusScope] при отмене. /// - **Data Integrity**: Гарантирует наличие текста перед активацией процесса сохранения. class NoteEditorBottomSheet extends StatefulWidget { final String bookId; final Note? existingNote; final Function(Note) onSave; const NoteEditorBottomSheet({ super.key, required this.bookId, this.existingNote, required this.onSave, }); @override State<NoteEditorBottomSheet> createState() => _NoteEditorBottomSheetState(); } class _NoteEditorBottomSheetState extends State<NoteEditorBottomSheet> { bool _isSaving = false; NoteEditorData? _editorData; bool _showError = false; @override void initState() { super.initState(); if (widget.existingNote != null) { _editorData = NoteEditorData( text: widget.existingNote!.text, location: widget.existingNote!.location, isQuote: widget.existingNote!.isQuote, ); } } @override Widget build(BuildContext context) { return Container( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.85, ), decoration: ShapeDecoration( color: AppColors.background, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(28.r)), ), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ _buildHandle(), Flexible( child: SingleChildScrollView( padding: EdgeInsets.symmetric(vertical: 8.h), child: Column( mainAxisSize: MainAxisSize.min, children: [ _buildHeader(), NoteEditorWidget( config: NoteEditorConfig( editorHeight: 160.h, hintText: 'О чем думаете?', initialText: widget.existingNote?.text, initialLocation: widget.existingNote?.location, initialIsQuote: widget.existingNote?.isQuote ?? false, showError: _showError, ), onChanged: (data) { setState(() { _editorData = data; if (data.text.isNotEmpty) _showError = false; }); }, ), SizedBox(height: 24.h), _buildButtons(), SizedBox(height: MediaQuery.of(context).viewInsets.bottom + 24.h), ], ), ), ), ], ), ); } Widget _buildHandle() { return SizedBox( width: double.infinity, height: 36.h, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 32.w, height: 4.h, decoration: ShapeDecoration( color: AppColors.accentSecondary.withValues(alpha: 0.3), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(9999), ), ), ), ], ), ); } Widget _buildHeader() { return Container( padding: EdgeInsets.only(left: 24.w, right: 24.w, bottom: 16.h), child: Text( widget.existingNote == null ? 'Новая заметка' : 'Редактировать', style: AppTypography.h2.copyWith( fontSize: 24.sp, height: 1.33, letterSpacing: -0.6, color: AppColors.textMain, ), ), ); } Widget _buildButtons() { return Container( padding: EdgeInsets.symmetric(horizontal: 24.w), child: Column( children: [ GestureDetector( onTap: _isSaving ? null : _saveNote, child: Container( width: double.infinity, height: 56.h, decoration: ShapeDecoration( color: AppColors.primaryAction, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(9999), ), shadows: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 2.r, offset: Offset(0, 1.h), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (_isSaving) SizedBox( width: 20.w, height: 20.h, child: CircularProgressIndicator(), ) else Icon(Icons.save_rounded, color: AppColors.background, size: 20.w), SizedBox(width: 8.w), Text( 'Сохранить', style: AppTypography.buttonText.copyWith( fontSize: 16.sp, color: AppColors.background, ), ), ], ), ), ), SizedBox(height: 12.h), GestureDetector( onTap: () { _closeKeyboard(); context.pop(); }, child: Text( 'Отмена', style: AppTypography.buttonText.copyWith( fontSize: 14.sp, color: AppColors.primaryAction, ), ), ), ], ), ); } void _closeKeyboard() => FocusScope.of(context).unfocus(); Future<void> _saveNote() async { if (_editorData == null || _editorData!.text.trim().isEmpty) { setState(() { _showError = true; }); return; } setState(() { _isSaving = true; _showError = false; }); final note = Note( id: widget.existingNote?.id ?? const Uuid().v4(), bookId: widget.bookId, text: _editorData!.text, location: _editorData!.location, createdAt: DateTime.now(), isQuote: _editorData!.isQuote, ); await widget.onSave(note); if (mounted) setState(() => _isSaving = false); } }