file_caption_widget.dart 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import 'package:flutter/material.dart';
  2. import 'package:photos/core/constants.dart';
  3. import 'package:photos/models/file.dart';
  4. import 'package:photos/theme/ente_theme.dart';
  5. import 'package:photos/ui/components/keyboard/keybiard_oveylay.dart';
  6. import 'package:photos/ui/components/keyboard/keyboard_top_button.dart';
  7. import 'package:photos/utils/magic_util.dart';
  8. class FileCaptionWidget extends StatefulWidget {
  9. final File file;
  10. const FileCaptionWidget({required this.file, super.key});
  11. @override
  12. State<FileCaptionWidget> createState() => _FileCaptionWidgetState();
  13. }
  14. class _FileCaptionWidgetState extends State<FileCaptionWidget> {
  15. static const int maxLength = 5000;
  16. // counterThreshold represents the nun of char after which
  17. // currentLength/maxLength will show up
  18. static const int counterThreshold = 1000;
  19. int currentLength = 0;
  20. final _textController = TextEditingController();
  21. final _focusNode = FocusNode();
  22. String? editedCaption;
  23. String hintText = fileCaptionDefaultHint;
  24. Widget? keyboardTopButtoms;
  25. @override
  26. void initState() {
  27. _focusNode.addListener(_focusNodeListener);
  28. editedCaption = widget.file.caption;
  29. if (editedCaption != null && editedCaption!.isNotEmpty) {
  30. hintText = editedCaption!;
  31. }
  32. super.initState();
  33. }
  34. @override
  35. void dispose() {
  36. if (editedCaption != null) {
  37. editFileCaption(null, widget.file, editedCaption);
  38. }
  39. _textController.dispose();
  40. _focusNode.removeListener(_focusNodeListener);
  41. super.dispose();
  42. }
  43. @override
  44. Widget build(BuildContext context) {
  45. final colorScheme = getEnteColorScheme(context);
  46. final textTheme = getEnteTextTheme(context);
  47. return TextField(
  48. onSubmitted: (value) async {
  49. await _onDoneClick(context);
  50. },
  51. controller: _textController,
  52. focusNode: _focusNode,
  53. decoration: InputDecoration(
  54. counterStyle: textTheme.mini.copyWith(color: colorScheme.textMuted),
  55. counterText: currentLength >= counterThreshold
  56. ? currentLength.toString() + " / " + maxLength.toString()
  57. : "",
  58. contentPadding: const EdgeInsets.all(16),
  59. border: OutlineInputBorder(
  60. borderRadius: BorderRadius.circular(2),
  61. borderSide: const BorderSide(
  62. width: 0,
  63. style: BorderStyle.none,
  64. ),
  65. ),
  66. focusedBorder: OutlineInputBorder(
  67. borderRadius: BorderRadius.circular(2),
  68. borderSide: const BorderSide(
  69. width: 0,
  70. style: BorderStyle.none,
  71. ),
  72. ),
  73. filled: true,
  74. fillColor: colorScheme.fillFaint,
  75. hintText: hintText,
  76. hintStyle: hintText == fileCaptionDefaultHint
  77. ? textTheme.small.copyWith(color: colorScheme.textMuted)
  78. : textTheme.small,
  79. ),
  80. style: textTheme.small,
  81. cursorWidth: 1.5,
  82. maxLength: maxLength,
  83. minLines: 1,
  84. maxLines: 10,
  85. textCapitalization: TextCapitalization.sentences,
  86. keyboardType: TextInputType.multiline,
  87. onChanged: (value) {
  88. setState(() {
  89. hintText = fileCaptionDefaultHint;
  90. currentLength = value.length;
  91. editedCaption = value;
  92. });
  93. },
  94. );
  95. }
  96. Future<void> _onDoneClick(BuildContext context) async {
  97. if (editedCaption != null) {
  98. final isSuccesful =
  99. await editFileCaption(context, widget.file, editedCaption);
  100. if (isSuccesful) {
  101. if (mounted) {
  102. Navigator.pop(context);
  103. }
  104. }
  105. }
  106. }
  107. void onCancelTap() {
  108. _textController.text = widget.file.caption ?? '';
  109. _focusNode.unfocus();
  110. editedCaption = null;
  111. }
  112. void onDoneTap() {
  113. _focusNode.unfocus();
  114. _onDoneClick(context);
  115. }
  116. void _focusNodeListener() {
  117. final caption = widget.file.caption;
  118. if (_focusNode.hasFocus && caption != null) {
  119. _textController.text = caption;
  120. editedCaption = caption;
  121. }
  122. final bool hasFocus = _focusNode.hasFocus;
  123. keyboardTopButtoms ??= KeyboardTopButton(
  124. onDoneTap: onDoneTap,
  125. onCancelTap: onCancelTap,
  126. );
  127. if (hasFocus) {
  128. KeyboardOverlay.showOverlay(context, keyboardTopButtoms!);
  129. } else {
  130. debugPrint("Removing listener");
  131. KeyboardOverlay.removeOverlay();
  132. }
  133. }
  134. }