@@ -78,6 +78,8 @@ final class BBCodeSpoilerEmbedBuilder extends EmbedBuilder {
7878 final info = BBCodeSpoilerInfo .fromJson (node.value.data as String );
7979
8080 return _SpoilerCard (
81+ onTap: () =>
82+ controller.moveCursorToPosition (node.documentOffset + node.length),
8183 onEdited: (info) {
8284 final offset = getEmbedNode (
8385 controller,
@@ -111,6 +113,7 @@ final class BBCodeSpoilerEmbedBuilder extends EmbedBuilder {
111113class _SpoilerCard extends StatefulWidget {
112114 const _SpoilerCard ({
113115 required this .onEdited,
116+ required this .onTap,
114117 required this .initialData,
115118 required this .emojiPicker,
116119 required this .emojiProvider,
@@ -127,6 +130,22 @@ class _SpoilerCard extends StatefulWidget {
127130 /// Callback when spoiler content is edited.
128131 final void Function (BBCodeSpoilerInfo ) onEdited;
129132
133+ /// Callback when spoiler card is tapped.
134+ ///
135+ /// This callback is for anything need update in the outer editor.
136+ ///
137+ /// Though the function body is defined by caller, things determined to be
138+ /// done includes:
139+ ///
140+ /// * Update cursor position to the end side of spoiler card, when user
141+ /// finish edit, ensuring the content save process in [onEdited] do find
142+ /// the embed node using `getEmbedNode` .
143+ /// This may be an internal but upstream that an embed builder contains
144+ /// [GestureDetector] sometimes make the cursor not moved around embed
145+ /// widget causing `getEmbedNode` failed to find node: a failure on saving
146+ /// contents in document.
147+ final VoidCallback onTap;
148+
130149 final BBCodeEmojiPicker emojiPicker;
131150 final BBCodeColorPicker ? colorPicker;
132151 final BBCodeColorPicker ? backgroundColorPicker;
@@ -307,10 +326,14 @@ class _SpoilerCardState extends State<_SpoilerCard> {
307326 @override
308327 Widget build (BuildContext context) {
309328 return GestureDetector (
310- onTap: () async => showDialog <void >(
311- context: context,
312- builder: (_) => buildDialog (context),
313- ),
329+ behavior: HitTestBehavior .deferToChild,
330+ onTap: () async {
331+ widget.onTap.call ();
332+ await showDialog <void >(
333+ context: context,
334+ builder: (_) => buildDialog (context),
335+ );
336+ },
314337 child: Card (
315338 margin: EdgeInsets .zero,
316339 clipBehavior: Clip .hardEdge,
0 commit comments