[我有一个任务,我必须做类似填字游戏的事情。首先,我将向您展示我想要实现的确切图像。
我尝试过使用许多可能的方式,例如
使用flutter中提供的GridView
和Table
小部件进行了尝试。
试图将GridView/Table
放入GestureDetector
中
但是问题是我听不见用户用手指在其上拖动的单词。字母和正确的单词来自服务器端。同样,当用户在某些字母上拖动时,如果单词匹配,则我必须在正确的单词上制作一个椭圆形,因此可能会有很多单词有很多椭圆形。这意味着我如何制作椭圆形?
使用Positioned
或其他技巧吗?
我在扑扑中搜索了可以帮助我的任何包裹,但很遗憾,我没有找到任何包裹。
我对扑扑非常陌生。
我写的东西可能会给你一个主意。它绝不是一个完成的,高质量的应用程序,并且肯定有几个错误。
首先,我创建了一个WordMarker
。这是圈出单词的黄色矩形。
class WordMarker extends StatelessWidget {
const WordMarker({
Key key,
@required this.rect,
@required this.startIndex,
this.color = Colors.yellow,
this.width = 2.0,
this.radius = 6.0,
}) : super(key: key);
final Rect rect;
final Color color;
final double width;
final double radius;
final int startIndex;
@override
Widget build(BuildContext context) {
return Positioned.fromRect(
rect: rect,
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
color: color,
width: width,
),
borderRadius: BorderRadius.circular(radius),
),
),
);
}
WordMarker copyWith({Rect rect}) {
return WordMarker(
key: key,
rect: rect ?? this.rect,
startIndex: startIndex,
color: color,
width: width,
radius: radius,
);
}
}
注:
Rect
,结合了大小和偏移量,用于在正确单词上方的位置标记。然后我们有了WordSearch
小部件,它是拼图板。
class WordSearch extends StatefulWidget {
const WordSearch({Key key, this.alphabet, this.words, this.wordsPerLine})
: super(key: key);
final int wordsPerLine;
final List<String> alphabet;
final List<String> words;
@override
_WordSearchState createState() => _WordSearchState();
}
class _WordSearchState extends State<WordSearch> {
final markers = <WordMarker>[];
int correctAnswers = 0;
var uniqueLetters;
@override
void initState() {
super.initState();
uniqueLetters = widget.alphabet
.map((letter) => {'letter': letter, 'key': GlobalKey()})
.toList();
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
GridView.count(
crossAxisCount: widget.wordsPerLine,
children: <Widget>[
for (int i = 0; i != uniqueLetters.length; ++i)
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
final key = uniqueLetters[i]['key'];
final renderBox = key.currentContext.findRenderObject();
final markerRect = renderBox.localToGlobal(Offset.zero,
ancestor: context.findRenderObject()) &
renderBox.size;
if (markers.length == correctAnswers) {
addMarker(markerRect, i);
} else if (widget.words
.contains(pathAsString(markers.last.startIndex, i))) {
markers.last = adjustedMarker(markers.last, markerRect);
++correctAnswers;
} else {
markers.removeLast();
}
});
},
child: Center(
child: Padding(
padding: const EdgeInsets.all(4.0),
key: uniqueLetters[i]['key'],
child: Text(
uniqueLetters[i]['letter'],
),
),
),
),
],
),
...markers,
],
);
}
void addMarker(Rect rect, int startIndex) {
markers.add(WordMarker(
rect: rect,
startIndex: startIndex,
));
}
WordMarker adjustedMarker(WordMarker originalMarker, Rect endRect) {
return originalMarker.copyWith(
rect: originalMarker.rect.expandToInclude(endRect));
}
String pathAsString(int start, int end) {
final isHorizontal =
start ~/ widget.wordsPerLine == end ~/ widget.wordsPerLine;
final isVertical = start % widget.wordsPerLine == end % widget.wordsPerLine;
String result = '';
if (isHorizontal) {
result = widget.alphabet.sublist(start, end + 1).join();
} else if (isVertical) {
for (int i = start;
i < widget.alphabet.length;
i += widget.wordsPerLine) {
result += widget.alphabet[i];
}
}
return result;
}
}
注:
GlobalKey
。这样就可以在用户点击时识别此字母,并获取其偏移量和大小。markerRect
了解Rect
计算。另请参阅adjustedMarker()
以了解点击下一个字母时Rect
的扩展方式。Stack
和GridView
正在使用,但是GestureDetector
分别包装每个字母。 您还可以同时使用两个小部件打开项目,并且该项目应该易于运行。我用您发送的单词和字母来运行它:
WordSearch(
wordsPerLine: 11,
alphabet: [
'I',
'A',
'G',
'M',
'F',
'Y',
'L',
'I',
'R',
'V',
'P',
'D',
'B',
'R',
'A',
'I',
'N',
'S',
'T',
'O',
'R',
'M',
'E',
'S',
'S',
'T',
'R',
'A',
'T',
'E',
'G',
'Y',
'E',
'A',
'B',
'W',
'O',
'M',
'G',
'O',
'A',
'L',
'S',
'X',
'S',
'Q',
'U',
'K',
'H',
'J',
'P',
'M',
'D',
'W',
'S'
],
words: [
'ARTHER',
'GOLDEN',
'AMADEUS',
'IDEAS',
'GOALS',
'BRAINSTORM'
],
),
[好吧,我已经答应了,我想对你的回答感到抱歉。这里真的已经很晚了,我想今晚给你。现在,这可能不是最好的方法,但是它可以工作,您肯定可以将我的代码的某些部分模块化为它们自己的函数。您可能要测试一下,因为我确定它现在是易碎的,并根据需要添加条件。似乎应该有一种更简单的方法来执行此操作,但是我找不到一个,所以这就是我所追求的。
List<bool> isSelected = [];
List<String> selectedLetters = [];
Map<GlobalKey, String> lettersMap;
Offset initialTappedPosition = Offset(0, 0);
Offset initialPosition = Offset(0, 0);
Offset finalPosition;
int intialSquare;
int crossAxisCount = 4; //whether you use GridView or not still need to provide this
int index = -1;
bool isTapped = false;
String selectedWord = '';
double width = 50;
double height = 50;
Size size;
List<String> letters = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'b',
'b',
'b',
'b',
'z',
];
@override
void initState() {
super.initState();
lettersMap =
Map.fromIterable(letters, key: (i) => GlobalKey(), value: (i) => i[0]);
isSelected = List.generate(letters.length, (e) => false);
}
_determineWord() {
double differnce;
int numberOfSquares;
if ((finalPosition.dx - initialPosition.dx) > 20) {
print('right');
//moved right
differnce = finalPosition.dx - initialPosition.dx;
numberOfSquares = (differnce / size.width).ceil();
for (int i = intialSquare + 1;
i < (intialSquare + numberOfSquares);
i++) {
isSelected[i] = true;
}
for (int i = 0; i < isSelected.length; i++) {
if (isSelected[i]) {
selectedWord += letters[i];
}
}
print(selectedWord);
} else if ((initialPosition.dx - finalPosition.dx) > 20) {
print('left');
// moved left
differnce = finalPosition.dx + initialPosition.dx;
numberOfSquares = (differnce / size.width).ceil();
for (int i = intialSquare - 1;
i >= (intialSquare - numberOfSquares + 1);
i--) {
isSelected[i] = true;
}
for (int i = 0; i < isSelected.length; i++) {
if (isSelected[i]) {
selectedWord += letters[i];
}
}
print(selectedWord);
} else if ((finalPosition.dy - initialPosition.dy) > 20) {
//moved up when moving up/down number of squares numberOfSquares is also number of rows
differnce = finalPosition.dy - initialPosition.dy;
numberOfSquares = (differnce / size.height).ceil();
for (int i = intialSquare + crossAxisCount;
i < (intialSquare + (numberOfSquares * crossAxisCount));
i += 4) {
isSelected[i] = true;
}
for (int i = 0; i < isSelected.length; i++) {
if (isSelected[i]) {
selectedWord += letters[i];
}
}
print(selectedWord);
} else if ((initialPosition.dy - finalPosition.dy) > 20) {
//moved down
differnce = initialPosition.dy - finalPosition.dy;
numberOfSquares = (differnce / size.height).ceil();
for (int i = intialSquare - crossAxisCount;
i > (intialSquare - (numberOfSquares * crossAxisCount));
i -= 4) {
isSelected[i] = true;
print('$i');
}
for (int i = isSelected.length - 1; i >= 0; i--) {
if (isSelected[i]) {
selectedWord += letters[i];
}
}
print(selectedWord);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.all(30.0),
child: GestureDetector(
child: GridView(
physics: NeverScrollableScrollPhysics(), //Very Important if
// you don't have this line you will have conflicting touch inputs and with
// gridview being the child will win
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
childAspectRatio: 2,
),
children: <Widget>[
for (int i = 0; i != lettersMap.length; ++i)
Listener(
child: Container(
key: lettersMap.keys.toList()[i],
child: Text(
lettersMap.values.toList()[i],
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.amber,
fontSize: 18,
),
),
),
onPointerDown: (PointerDownEvent event) {
final RenderBox renderBox = lettersMap.keys
.toList()[i]
.currentContext
.findRenderObject();
size = renderBox.size;
setState(() {
isSelected[i] = true;
intialSquare = i;
});
},
),
],
),
onTapDown: (TapDownDetails details) {
//User Taps Screen
// print('Global Position: ${details.globalPosition}');
setState(() {
initialPosition = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
initialTappedPosition = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
isTapped = true;
});
// print(initialPosition);
},
onVerticalDragUpdate: (DragUpdateDetails details) {
// print('${details.delta.dy}');
setState(() {
if (details.delta.dy < 0) {
initialTappedPosition = Offset(initialTappedPosition.dx,
initialTappedPosition.dy + details.delta.dy);
height -= details.delta.dy;
} else {
height += details.delta.dy;
}
finalPosition = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
});
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
// print('${details.delta.dx}');
setState(() {
if (details.delta.dx < 0) {
initialTappedPosition = Offset(
initialTappedPosition.dx + details.delta.dx,
initialTappedPosition.dy,
);
width -= details.delta.dx;
} else {
width += details.delta.dx;
}
finalPosition = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
});
},
onHorizontalDragEnd: (DragEndDetails details) {
_determineWord();
},
onVerticalDragEnd: (DragEndDetails details) {
_determineWord();
},
),
),
),
Positioned(
top: initialTappedPosition.dy,
left: initialTappedPosition.dx,
child: Container(
height: height,
width: width,
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
side: BorderSide(
color: isTapped ? Colors.blue : Colors.transparent,
width: 3.0,
),
),
),
),
),
],
),
);
}
很高兴与您合作,希望您的项目进展顺利。