Я написал кое-что, что может дать вам представление. Это ни в коем случае не законченное, качественное приложение, и в нем наверняка есть несколько ошибок.
Во-первых, я создал 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,
);
}
}
Примечание :
- a
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
расширяется при нажатии следующей буквы. - a
Stack
и GridView
используются, но GestureDetector
упаковывает каждую букву индивидуально. - Каждый маркер сохраняется вместе с индексом его первой буквы, поэтому его можно легко достичь при создании пути между ним и следующей буквой, к которой производится нажатие. Обратите внимание, что я думаю, что это не лучшее решение.
- С точки зрения функциональности - доска позволяет вам нажимать любые две буквы, одну за другой. Если они оба дают путь к правильному ответу - он обведен. В противном случае круг удаляется. Я надеюсь, что это поможет вам gr asp код.
Вы также можете открыть проект с обоими виджетами, и он должен легко запускаться. Я запустил его со словами и алфавитом, которые вы отправили:
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'
],
),