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...""", ]; }
问题根源
- 循环逻辑漏洞:
while (i != mes.length - 1)会忽略最后一个字符,若文本末尾无拖拽标记会遗漏内容;找到x{后未跳过已处理字符,可能导致重复解析。 - 排版组件适配问题:直接将多个独立的Text、Draggable组件放入Wrap,每个组件都是独立排版单元,会打乱文本的连贯性,导致换行拆分混乱。
- 连续标记处理失效:遇到
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
相关产品推荐
相关产品推荐

