首页 > 解决方案 > 如何使用 GridView 或 TableView 在颤振中制作填字游戏

问题描述

我有一个任务,我必须做一个像填字游戏这样的东西。首先,我将向您展示我想要实现的确切图像。

在此处输入图像描述

我尝试过使用许多可能的方式,例如

但问题是我无法得到用户用手指拖动的单词。字母表和正确的单词来自服务器端。此外,当用户拖动一些字母时,如果单词匹配,那么我必须在正确的单词上制作一个椭圆形,因此可能会有很多单词这么多椭圆形。这意味着如何制作椭圆形?

使用Positioned或其他一些技巧?

我在颤振中搜索了任何可以帮助我的包,但不幸的是,我没有找到任何包。

标签: flutterflutter-layoutgesture

解决方案


我写了一些东西,可能会给你一个想法。它绝不是一个完整的、高质量的应用程序,它肯定有几个错误。

首先,我创建了一个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,
    );
  }
}

注意

  • aRect结合了大小和偏移量,用于将标记定位在正确的单词上方。

然后我们有一个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点击下一个字母时如何扩展。
  • aStack和 aGridView被使用,但 aGestureDetector单独包装每个字母。
  • 每个标记都与其第一个字母的索引一起保存,因此在创建它和被点击的下一个字母之间的路径时可以轻松实现。请注意,我认为这不是最好的解决方案。
  • 在功能方面 - 该板可让您一个接一个地点击任意两个字母。如果他们都给出了正确答案的路径 - 它被圈起来。否则圆圈被删除。我希望它可以帮助您掌握代码。

您还可以打开一个包含两个小部件的项目,它应该很容易运行。我用你发送的单词和字母运行它:

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'
          ],
        ),

运行应用


推荐阅读