You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter文本组件中可拖拽文本片段的逻辑错误排查求助

Flutter拖拽文本片段显示混乱问题修复

问题背景

开发Flutter游戏时,需实现拖拽文本中特定高亮片段的功能,用x{String}x格式标记可拖拽区域。通过_constructGame函数解析文本后,期望得到排版规整的文本,但实际显示混乱,无法定位逻辑错误。当前采用固定文本、可拖拽片段交替的Widget列表实现功能,相关代码如下:

GameRoute代码

import 'dart:math';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:schoolproject/GameTextList.dart';

class GameRoute extends StatefulWidget {

  @override
  State<StatefulWidget> createState() => _GameRouteState();
  
}

class _GameRouteState extends State<GameRoute>{

  // Random().nextInt(GameTextList.texts.length)
  String gameText = GameTextList.texts[0];

  Draggable _createDraggableText(String text) {
    return Draggable<String>(
      data: text,
      child: Text(
        text,
        style: TextStyle(fontWeight: FontWeight.bold, color: Colors.red),
      ),
      feedback: Text(
        text,
        style: TextStyle(fontSize: 16.0,fontWeight: FontWeight.bold, color: Colors.red),
      ),
      childWhenDragging: Text(
        text,
        style: TextStyle(color: Colors.grey),
      ),
    );
  }

  // Precondition: There must be at least one draggable
  List<Widget> _constructGame(String mes) {
    List<Widget> widgets = <Widget>[];

    // Decode the message
    int i = 0;
    int j = 0;
    while (i != mes.length - 1) {
      if (mes[i] == "x" && mes[i + 1] == "{") {
        var last = mes.indexOf("}x", i);
        widgets.add(Text(mes.substring(j, i))); // Bugün hava çok
        widgets.add(_createDraggableText(mes.substring(i + 2, last))); // soğuktu
        j = last + 2;
      }
      i++;
    }
    // add the remaining part of the text
    widgets.add(Text(mes.substring(j, mes.length)));
    return widgets;
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Egzersiz")),
      body: Wrap(
        children: _constructGame(gameText),
      )
      );
  }

}

GameTextList代码

class GameTextList {
    static List<String> texts = [
      "Bugün hava çok x{soğuktuuuuuuuuuuuuuuu}x. Fakat üstümüze mont giymeyi x{unuttuk}x. Şu an yağmur x{yağıyor}x. Son zamanlarda"
          " hava düzensiz olabileceğinden tedbirli olmak x{gerekecekmiş}x.",

      "Planktonların varlığı ekosistem için x{mühimdir}x hatta onlar x{olmasaydı}x  Dünyadaki canlıların bir çoğu yok "
          "x{olurdu}x. Nitekim bu canlılar atmosferdeki oksijenin %50'sini x{oluştururlar}x. Yine de insanlar bilinçsizce"
          " denize atık bırakmaya devam x{ederse}x solunulacak bir x{kalmayacak}x.",

      "Uranyumun keşfedildiği tarih x{1789'dur}x.",

      """
Yapma x{ne olursun}x, bırak o yerimi x{doldursun}x. Bana ihtiyacın x{olursa}x sarıl ona, elbet x{unutursun}x.x{Girmişşe}x kalbine bambaşka biri...""",

    ];
}

问题根源

  1. 循环逻辑漏洞:while (i != mes.length - 1)会忽略最后一个字符,若文本末尾无拖拽标记会遗漏内容;找到x{后未跳过已处理字符,可能导致重复解析。
  2. 排版组件适配问题:直接将多个独立的Text、Draggable组件放入Wrap,每个组件都是独立排版单元,会打乱文本的连贯性,导致换行拆分混乱。
  3. 连续标记处理失效:遇到x{unutursun}x.x{Girmişşe}x这类连续拖拽标记时,原逻辑会出现解析错误。

修复方案

1. 修正解析逻辑

重写_constructGame函数,用更可靠的遍历方式解析标记,避免遗漏和重复:

List<Widget> _constructGame(String mes) {
  List<Widget> widgets = <Widget>[];
  int currentIndex = 0;

  while (currentIndex < mes.length) {
    int startTag = mes.indexOf('x{', currentIndex);
    if (startTag == -1) {
      widgets.add(Text(mes.substring(currentIndex)));
      break;
    }
    if (startTag > currentIndex) {
      widgets.add(Text(mes.substring(currentIndex, startTag)));
    }
    int endTag = mes.indexOf('}x', startTag);
    if (endTag == -1) {
      widgets.add(Text(mes.substring(startTag)));
      break;
    }
    String dragText = mes.substring(startTag + 2, endTag);
    widgets.add(_createDraggableText(dragText));
    currentIndex = endTag + 2;
  }

  return widgets;
}

2. 优化排版连贯性

使用RichText结合WidgetSpan,将Draggable嵌入文本流,保证排版连贯:

Widget _constructGameRichText(String mes) {
  List<InlineSpan> spans = [];
  int currentIndex = 0;

  while (currentIndex < mes.length) {
    int startTag = mes.indexOf('x{', currentIndex);
    if (startTag == -1) {
      spans.add(TextSpan(text: mes.substring(currentIndex)));
      break;
    }
    if (startTag > currentIndex) {
      spans.add(TextSpan(text: mes.substring(currentIndex, startTag)));
    }
    int endTag = mes.indexOf('}x', startTag);
    if (endTag == -1) {
      spans.add(TextSpan(text: mes.substring(startTag)));
      break;
    }
    String dragText = mes.substring(startTag + 2, endTag);
    spans.add(WidgetSpan(
      child: _createDraggableText(dragText),
      alignment: PlaceholderAlignment.middle,
    ));
    currentIndex = endTag + 2;
  }

  return RichText(
    text: TextSpan(
      style: DefaultTextStyle.of(context).style,
      children: spans,
    ),
    textAlign: TextAlign.justify,
  );
}

// 更新build方法中的body
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text("Egzersiz")),
    body: SingleChildScrollView(
      padding: const EdgeInsets.all(16.0),
      child: _constructGameRichText(gameText),
    ),
  );
}

3. 特殊场景处理

  • 确保所有x{标记都有对应的}x,避免解析异常;
  • 长文本添加SingleChildScrollView防止内容溢出。

内容的提问来源于stack exchange,提问作者Snefru Clone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 04:40:54