flutter-quill icon indicating copy to clipboard operation
flutter-quill copied to clipboard

Null check operator is used on a null value when long press a link

Open tschiekdev opened this issue 1 year ago • 2 comments

Is there an existing issue for this?

Flutter Quill version

10.7.5

Steps to reproduce

dont know

Expected results

no error

Actual results

Null check operator used on a null value

#0 _TextLineState._longPressLink (package:flutter_quill/src/editor/widgets/text/text_line.dart:656)#1 _TextLineState._getRecognizer. (package:flutter_quill/src/editor/widgets/text/text_line.dart:623) #2 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:351) #3 LongPressGestureRecognizer._checkLongPressStart (package:flutter/src/gestures/long_press.dart:709) #4 LongPressGestureRecognizer.didExceedDeadline (package:flutter/src/gestures/long_press.dart:610) #5 PrimaryPointerGestureRecognizer.didExceedDeadlineWithEvent (package:flutter/src/gestures/recognizer.dart:731) #6 PrimaryPointerGestureRecognizer.addAllowedPointer. (package:flutter/src/gestures/recognizer.dart:674) #7 _rootRun (dart:async/zone.dart:1391) #8 _CustomZone.run (dart:async/zone.dart:1301) #9 _CustomZone.runGuarded (dart:async/zone.dart:1209) #10 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1249) #11 _rootRun (dart:async/zone.dart:1399) #12 _CustomZone.run (dart:async/zone.dart:1301) #13 _CustomZone.bindCallback. (dart:async/zone.dart:1233) #14 Timer._createTimer. (dart:async-patch/timer_patch.dart:18) #15 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398) #16 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429) #17 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184)

Additional Context

Screenshots / Video demonstration

[Upload media here]

Logs
[Paste your logs here]

tschiekdev avatar Sep 22 '24 18:09 tschiekdev

This line is causing the issue. Need to handle this in a better way without using the null check operator, if we're not sure why then should use assert or throw an exception with a clear message instead of return and then causing unexpected behavior that's more difficult to trace.

This commit is related.

I would avoid using dynamic in Dart (and any other language) when possible which is something that's used in Delta and Attribute, instead manually cast and handle exceptions.

EchoEllet avatar Sep 22 '24 18:09 EchoEllet

Hi @tschiekdev @EchoEllet,

The error occurs when you perform a long press on a piece of text, add a link, then long press again and choose “remove.” After that, performing another long press on the same text triggers the reported error. To reproduce the issue, you need to start with a completely empty field.

The identified fix involves modifying the _longPressLink function in the text_line file. Here’s the updated code:

Future<void> _longPressLink(Node node) async {
  final link = node.style.attributes[Attribute.link.key]!.value!;
  final action = await widget.linkActionPicker(node);
  switch (action) {
    case LinkMenuAction.launch:
      _tapLink(link);
      break;
    case LinkMenuAction.copy:
      Clipboard.setData(ClipboardData(text: link));
      break;
    case LinkMenuAction.remove:
      final range = getLinkRange(node);
      widget.controller
          .formatText(range.start, range.end - range.start, Attribute.link);
      // ADDED CODE
      final recognizer = _linkRecognizers.remove(node);
      recognizer?.dispose();
      // END ADDED CODE
      break;
    case LinkMenuAction.none:
      break;
  }
}

The issue happens because, at line 659 of the text_line file, a class is instantiated that registers a callback, which isn’t properly removed when the link is deleted:

if (isLink && canLaunchLinks) {
  if (isDesktop || widget.readOnly) {
    _linkRecognizers[segment] = TapGestureRecognizer()
      ..onTap = () => _tapNodeLink(segment);
  } else {
    _linkRecognizers[segment] = LongPressGestureRecognizer()
      ..onLongPress = () => _longPressLink(segment);
  }
}

I hope this information is helpful and that the issue will be fixed in the main branch 🚀

Best regards, Leonard

leonardmatasel-dinova avatar Oct 27 '25 10:10 leonardmatasel-dinova