添加GridView.builder后Flutter 3D立方体扭曲

问题描述 投票:0回答:1

我有这样的代码,是我跟随一位 YouTuber 同伴编写的,用于创建带有动画的 3D 立方体,它工作得很好,直到我在任何面中引入 GridView 或 ListView 小部件。

import 'dart:async';
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sizer/sizer.dart';
import 'package:tictactoe/Controllers/gameEngine.dart';
import 'package:tictactoe/UIUX/themesAndStyles.dart';
import 'UIUX/customWidgets.dart';
import 'gamePage.dart';



enum faces {top, bottom, right, left, front, back}

class CubeGame extends StatefulWidget {
  const CubeGame({super.key});

  @override
  State<CubeGame> createState() => _CubeGameState();
}

class _CubeGameState extends State<CubeGame> with SingleTickerProviderStateMixin{

  Offset offset = Offset.zero;

  double rx = 0.0;
  double ry = 0.0;
  double rz = 0.0;
  List<Widget> children = [];

  GameEngine frontGame = GameEngine();
  GameEngine backGame = GameEngine();
  GameEngine rightGame = GameEngine();
  GameEngine leftGame = GameEngine();

  late AnimationController _animationController;

  late Animation _animation;

  late Widget right;
  late Widget front;
  late Widget back ;
  late Widget left;
  late Widget top;
  late Widget bottom;

  double cubeSize = 70.w;

  updateFaces(double bX, double bY){
    rx += bX;
    ry += bY;
    rx %= pi*2;
    ry %= pi*2;
    if ((rx >= 0 && rx <= 1.70) || (rx >= 4.70)){
      if (ry < pi / 4) {
        children = [right, front];
      } else if (ry < pi / 2) {
        children = [front, right];
      } else if (ry < 3 * pi / 4) {
        children = [back, right];
      } else if (ry < pi) {
        children = [right, back];
      } else if (ry < 5 * pi / 4) {
        children = [left, back];
      } else if (ry < 3 * pi / 2) {
        children = [back, left];
      } else if (ry < 7 * pi / 4) {
        children = [front, left];
      } else {
        children = [left, front];
      }
    }else{
      if (ry < pi / 4) {
        children = [left, back];
      } else if (ry < pi / 2) {
        children = [back, left];
      } else if (ry < 3 * pi / 4) {
        children = [front, left];
      } else if (ry < pi) {
        children = [left, front];
      } else if (ry < 5 * pi / 4) {
        children = [right, front];
      } else if (ry < 3 * pi / 2) {
        children = [front, right];
      } else if (ry < 7 * pi / 4) {
        children = [back, right];
      } else {
        children = [right, back];
      }
    }
    if (rx  >= 0 && rx <= pi) {
      // TODO: not perfect - does not consider perspective:
      // When `rotateX` is positive but very small, like 0.1, when taking
      // account of perspective, the "top" face should be drawn *behind* the
      // front face, not *in front* of it. But this works reasonably well for
      // larger values (like > 0.1) of `rotateX`.
      if (rx >= 0 && rx <= 0.1 || rx > 3){
        children = [top,...children];
      }else{
        children = [...children, top];
      }

    } else if(rx >= pi && rx <= pi*2) {
      if (rx <= pi + 0.1 || (rx >= 6.18)){
        children = [bottom, ...children];
      }else{
        children = [...children, bottom];
      }
    }
    setState(() {});
  }

  generateFaces(){
    for (var face in faces.values){
      switch (face){

        case faces.top:
          top = buildBoardFace(face: faces.top, size: cubeSize);
          break;
        case faces.bottom:
          bottom = buildBoardFace(face: faces.bottom, size: cubeSize);
          break;
        case faces.right:
          right = buildBoardFace(face: faces.right, size: cubeSize, customEngine: frontGame);
          break;
        case faces.front:
          front = buildBoardFace(face: faces.front, size: cubeSize, customEngine: frontGame);
          break;
        case faces.back:
          back = buildBoardFace(face: faces.back, size: cubeSize, customEngine: frontGame);
          break;
        case faces.left:
          left = buildBoardFace(face: faces.left, size: cubeSize, customEngine: frontGame);
      }
    }
  }

  @override
  void initState() {
    _animationController = AnimationController(duration: Duration(milliseconds: 3000), vsync: this);

    _animation = Tween(begin: 0.0, end: 1.0).animate(_animationController);
    generateFaces();
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      updateFaces(0.65, 0.8);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanUpdate: (det){
        double bX = 0.0, bY = 0.0;
      if ((rx >= 0 && rx <= 1.70) || (rx >= 4.70)){
        bX = det.delta.dy * 0.01;
        bY = -det.delta.dx * 0.01;
      }else{
        bX = det.delta.dy * 0.01;
        bY = det.delta.dx * 0.01;
      }

        updateFaces(bX, bY);
      },
      child: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ChangeNotifierProvider(
                create: (context)=>frontGame,
                child: Transform(
                  transform: Matrix4.identity()
                  ..setEntry(3, 2, 0.001)
                  ..rotateX(rx)
                  ..rotateY(ry)
                  ..rotateZ(rz),
                  alignment: Alignment.center,
                  child: SizedBox(
                    height: cubeSize,
                    width: cubeSize,
                    child: Stack(
                      children: [
                        ...children
                      ],
                    ),
                  ),
                ),
              ),
              SizedBox(height: 20.h),
              Slider(value: rx,
                  min: 0,
                  max: pi * 2,
                  onChanged: (value){
                    rx = value;
                    setState(() {
                      updateFaces(rx, 0);
                    });
              }),
              Text(rx.toString()),
              Slider(value: ry,
                  min: 0,
                  max: pi * 2,
                  onChanged: (value){
                    ry = value;
                    setState(() {
                      updateFaces(0, ry);
                    });
                  }),
              Text(ry.toString()),
              Slider(value: rz,
                  min: 0,
                  max: pi * 2,
                  onChanged: (value){
                    rz = value;
                    setState(() {
                    });
                  }),
              Text(rz.toString())
            ],
          )
        ),
      ),
    );
  }

  buildBoardFace({required faces face, required double size, GameEngine? customEngine}){

    late double rotateX;
    late double rotateY;
    late double trX;
    late double trY;
    late double trZ;
    switch (face){

      case faces.top:
        trX = 0.0;
        trY = -cubeSize/2;
        trZ = 0.0;
        rotateX = -pi / 2;
        rotateY = 0.0;
      case faces.bottom:
        trX = 0.0;
        trY = cubeSize/2;
        trZ = 0.0;
        rotateX = pi / 2;
        rotateY = 0.0;
      case faces.right:
        trX = cubeSize/2;
        trY = 0.0;
        trZ = 0.0;
        rotateX = 0.0;
        rotateY = -pi / 2;
      case faces.left:
        trX = -cubeSize/2;
        trY = 0.0;
        trZ = 0.0;
        rotateX = 0.0;
        rotateY = pi / 2;
      case faces.front:
        trX = 0.0;
        trY = 0.0;
        trZ = -cubeSize/2;
        rotateX = 0.0;
        rotateY = 0.0;
      case faces.back:
        trX = 0.0;
        trY = 0.0;
        trZ = cubeSize/2;
        rotateX = 0.0;
        rotateY = pi;
    }
    List<Widget> lines = [];
    // Draw box lines
    init(size, size, lines, Colors.lightBlueAccent.withOpacity(0.5), _animationController);
    final linearGrid = <int>[];
    if (customEngine != null) {
      for (var i in customEngine.grid){
        linearGrid.addAll(i);
      }
    }
    return face != faces.front ? Transform(
        transform: Matrix4.identity()
    ..translate(trX, trY, trZ)
    ..rotateX(rotateX)
    ..rotateY(rotateY),
    alignment: Alignment.center,
    child: Container(
      height: size,
      width: size,
      color: colorLightYellow,
      child: Image.asset('assets/patternXO.png'),
    )
    ) :
    SizedBox(
      height: size,
      width: size,
      child: Container(
        alignment: Alignment.topLeft,
        child: Stack(
          children: lines..addAll([
            Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                GridView.builder(
                    gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 3,
                    ),
                    physics: const NeverScrollableScrollPhysics(),
                    padding: EdgeInsets.zero,
                    shrinkWrap: true,
                    itemCount: linearGrid.length,
                    itemBuilder: (context, index){
                      return InkWell(
                        onTap: () async{
                          gameWinner? winner;
                          if (linearGrid[index] == -1){
                            winner = customEngine?.setManualMove(isO: false, ((index ~/ 3),(index % 3)));
                            setState(() {});
                            await Future.delayed(const Duration(milliseconds: 1000));
                            winner = customEngine?.setAiMove(isO: true);
                          }
                          setState(() {});

                        },
                        child: Container(
                          padding: const EdgeInsets.all(5),
                          child: linearGrid[index] == -1 ? Container() :
                          Icon(linearGrid[index] == 0 ? CupertinoIcons.circle :  CupertinoIcons.xmark,
                              color: Colors.blue, size: 12.w),
                        ),
                      );
                    }),
              ],
            ),
          ]),
        ),
      ),
    );

  }
}

下面是我运行此顶部代码时的记录,该代码仅给正面(1/6 面)一个 gridView 子项:

a busy cat

现在我已经尝试了多种解决方案,但问题似乎主要是因为一旦我引入这些 GridView 或 ListView 小部件,我为旋转并将侧面固定到其 3d 位置而进行的计算就会变得混乱,应该重新计算为其他一些我无法想象。

供参考:

这段代码我已经尝试过,并添加了带有 gridview 的代码部分,它工作正常,它有另一种计算位置的方法,并且只渲染 3 个边,我不希望它发生在我的应用程序:

https://stackoverflow.com/a/74942907/15008725

flutter dart gridview 3d cube
1个回答
0
投票

你能解决这个问题吗?因为我atm也面临这个问题

© www.soinside.com 2019 - 2024. All rights reserved.