file_caption_widget.dart 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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/utils/magic_util.dart';
  6. class FileCaptionWidget extends StatefulWidget {
  7. final File file;
  8. const FileCaptionWidget({required this.file, super.key});
  9. @override
  10. State<FileCaptionWidget> createState() => _FileCaptionWidgetState();
  11. }
  12. class _FileCaptionWidgetState extends State<FileCaptionWidget> {
  13. static const int maxLength = 5000;
  14. int currentLength = 0;
  15. final _textController = TextEditingController();
  16. final _focusNode = FocusNode();
  17. String? editedCaption;
  18. String hintText = fileCaptionDefaultHint;
  19. @override
  20. void initState() {
  21. _focusNode.addListener(_focusNodeListener);
  22. editedCaption = widget.file.caption;
  23. if (editedCaption != null && editedCaption!.isNotEmpty) {
  24. hintText = editedCaption!;
  25. }
  26. super.initState();
  27. }
  28. @override
  29. void dispose() {
  30. if (editedCaption != null) {
  31. editFileCaption(null, widget.file, editedCaption);
  32. }
  33. _textController.dispose();
  34. _focusNode.removeListener(_focusNodeListener);
  35. super.dispose();
  36. }
  37. @override
  38. Widget build(BuildContext context) {
  39. final colorScheme = getEnteColorScheme(context);
  40. final textTheme = getEnteTextTheme(context);
  41. return TextField(
  42. onSubmitted: (value) async {
  43. if (editedCaption != null) {
  44. final isSuccesful =
  45. await editFileCaption(context, widget.file, editedCaption);
  46. if (isSuccesful) {
  47. if (mounted) {
  48. Navigator.pop(context);
  49. }
  50. }
  51. }
  52. },
  53. controller: _textController,
  54. focusNode: _focusNode,
  55. decoration: InputDecoration(
  56. counterStyle: textTheme.mini.copyWith(color: colorScheme.textMuted),
  57. counterText: currentLength > 99
  58. ? currentLength.toString() + " / " + maxLength.toString()
  59. : "",
  60. contentPadding: const EdgeInsets.all(16),
  61. border: OutlineInputBorder(
  62. borderRadius: BorderRadius.circular(2),
  63. borderSide: const BorderSide(
  64. width: 0,
  65. style: BorderStyle.none,
  66. ),
  67. ),
  68. focusedBorder: OutlineInputBorder(
  69. borderRadius: BorderRadius.circular(2),
  70. borderSide: const BorderSide(
  71. width: 0,
  72. style: BorderStyle.none,
  73. ),
  74. ),
  75. filled: true,
  76. fillColor: colorScheme.fillFaint,
  77. hintText: hintText,
  78. hintStyle: hintText == fileCaptionDefaultHint
  79. ? textTheme.small.copyWith(color: colorScheme.textMuted)
  80. : textTheme.small,
  81. ),
  82. style: textTheme.small,
  83. cursorWidth: 1.5,
  84. maxLength: maxLength,
  85. minLines: 1,
  86. maxLines: 6,
  87. textCapitalization: TextCapitalization.sentences,
  88. keyboardType: TextInputType.text,
  89. onChanged: (value) {
  90. setState(() {
  91. hintText = fileCaptionDefaultHint;
  92. currentLength = value.length;
  93. editedCaption = value;
  94. });
  95. },
  96. );
  97. }
  98. void _focusNodeListener() {
  99. final caption = widget.file.caption;
  100. if (_focusNode.hasFocus && caption != null) {
  101. _textController.text = caption;
  102. editedCaption = caption;
  103. }
  104. }
  105. }