flutter 构建函数是否创建了新的小部件来呈现或重用?

fumotvh3  于 2022-11-25  发布在  Flutter
关注(0)|答案(2)|浏览(97)

我已经写了一个工作片段,但我有点不明白flutter是如何(重新)使用在build方法中创建的小部件的:

import 'dart:math';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyGame());
}

class MyGame extends StatelessWidget {
  const MyGame({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: GameWidget());
  }
}

class GameWidget extends StatefulWidget {
  const GameWidget({Key? key}) : super(key: key);

  static const squareWidth = 50.0;
  static const squareHeight = 50.0;

  @override
  State<GameWidget> createState() => _GameWidgetState();
}

class _GameWidgetState extends State<GameWidget> {
  List<Offset> offsets = [];

  @override
  Widget build(BuildContext context) {
    if (offsets.isEmpty) {
      for(int i = 0; i < 20; i++) {
        offsets.add(calculateNextOffset());
      }
    }

    List<Widget> squareWidgets = [];

    for (int j = 0; j < offsets.length; j++) {
      squareWidgets.add(AnimatedPositioned(
        left: offsets[j].dx,
        top: offsets[j].dy,
        curve: Curves.easeIn,
        duration: const Duration(milliseconds: 500),
        child: GestureDetector(
          onTapDown: (tapDownDetails) {
            setState(() {
              offsets.removeAt(j);
              for (int k = 0; k < offsets.length; k++) {
                offsets[k] = calculateNextOffset();
              }
            });
          },
          behavior: HitTestBehavior.opaque,
          child: Container(
            width: GameWidget.squareWidth,
            height: GameWidget.squareHeight,
            color: Colors.blue,

          ),
        ),
      ));
    }

    return Stack(
      children: squareWidgets,
    );
  }

  Offset calculateNextOffset() {
    return randomOffset(
        MediaQuery.of(context).size,
        const Size(GameWidget.squareWidth, GameWidget.squareHeight),
        MediaQuery.of(context).viewPadding.top);
  }

  double randomNumber(double min, double max) =>
      min + Random().nextDouble() * (max - min);

  Offset randomOffset(
      Size parentSize, Size childSize, double statusBarHeight) {
    var parentWidth = parentSize.width;
    var parentHeight = parentSize.height;

    var randomPosition = Offset(
      randomNumber(parentWidth, childSize.width),
      randomNumber(statusBarHeight,parentHeight - childSize.height),
    );

    return randomPosition;
  }
}

每次单击容器时,我都希望“偏移”状态得到更新,但我也希望重新呈现所有AnimationPositioned小部件、GestureDetector小部件和您看到的方形小部件。
使用重新渲染,我的意思是它们将从屏幕上消失,新的将被重新渲染(第一个小部件的动画将被取消,永远不会显示)
它是怎么工作的?有人能给我解释一下吗?

编辑:我已经更新了问题中的代码片段,以符合我的要求,我也将在这里重新措辞:

每次我点击一个正方形,我希望这个正方形消失,所有其他的正方形随机动画到另一个位置。但是每次我点击一个正方形,另一个随机的正方形被删除,我点击的正方形是动画。
我希望我点击的方块消失,其余的将动画。

9lowa7mx

9lowa7mx1#

Following up from the comments - Actually, the square you click is disappears. However, to see this visually add this to your container color:

color:Colors.primaries[Random().nextInt(Colors.primaries.length)],

Now, your offsets are generating just fine and random. However, because you have an AnimatedContainer widget. This widget will remember the last x & y position of your square and animate the new square starting from that old x,y value to the new on you passed it. So if you really want the square you click on disappear - you will need to either use Positioned widget:

import 'dart:math';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyGame());
}

class MyGame extends StatelessWidget {
  const MyGame({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: GameWidget());
  }
}

class GameWidget extends StatefulWidget {
  const GameWidget({Key? key}) : super(key: key);

  static const squareWidth = 100.0;
  static const squareHeight = 100.0;

  @override
  State<GameWidget> createState() => _GameWidgetState();
}

class _GameWidgetState extends State<GameWidget> {
  List<Offset> offsets = [];

  @override
  Widget build(BuildContext context) {
    if (offsets.isEmpty) {
      offsets.add(calculateNextOffset());
    }  
  
  
    print(offsets);
    List<Widget> squareWidgets = [];
    for (var offset in offsets) {

      squareWidgets.add(Positioned(
         left: offset.dx,
         top: offset.dy,
         //curve: Curves.easeIn,
         //duration: const Duration(milliseconds: 500),
        child: GestureDetector(
          onTapDown: (tapDownDetails) {
            setState(() {
              for (var i = 0; i < offsets.length; i++) {
                offsets[i] = calculateNextOffset();
              }
              offsets.add(calculateNextOffset());
              
            });
          },
          behavior: HitTestBehavior.opaque,
          child: Container(
            width: GameWidget.squareWidth,
            height: GameWidget.squareHeight,
            color:Colors.primaries[Random().nextInt(Colors.primaries.length)],

          ),
        ),
      ));
    }

    return Stack(
      children: squareWidgets,
    );
  }

  Offset calculateNextOffset() {
    return randomOffset(
        MediaQuery.of(context).size,
        const Size(GameWidget.squareWidth, GameWidget.squareHeight),
        MediaQuery.of(context).viewPadding.top);
  }

  double randomNumber(double min, double max) =>
      min + Random().nextDouble() * (max - min);

  Offset randomOffset(
      Size parentSize, Size childSize, double statusBarHeight) {
    var parentWidth = parentSize.width;
    var parentHeight = parentSize.height;

    var randomPosition = Offset(
      randomNumber(parentWidth, childSize.width),
      randomNumber(statusBarHeight,parentHeight - childSize.height),
    );

    return randomPosition;
  }
}

If you want the rest of the squares to animate while only the one that is clicked disappears. You will need to rethink your implementation and track all square perhaps using unique keys and custom animations. Hope that helps!

q5lcpyga

q5lcpyga2#

我终于找到了
在问题片段的上下文中:每次单击一个方块,它都会正确地删除该项目,但新列表中的小部件会重新呈现,之前呈现的最后一个小部件将被删除,而不是我单击的那个。
这是因为widget树中的每个widget都被呈现为元素树中的一个元素。如果元素树中的元素状态相同,它就不会重新呈现那个元素。而且它们最后都只是蓝色的方块,所以没有区别。
你可以在这里找到一段由flutter开发人员制作的非常棒的视频:When to Use Keys - Flutter Widgets 101 Ep. 4
长话短说:以下是修复的代码片段,即为每个小部件添加一个Key,然后元素树中元素的状态将发生变化,并将重新呈现(并删除)正确的小部件/元素:

import 'dart:math';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyGame());
}

class MyGame extends StatelessWidget {
  const MyGame({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: GameWidget());
  }
}

class GameWidget extends StatefulWidget {
  const GameWidget({Key? key}) : super(key: key);

  static const squareWidth = 50.0;
  static const squareHeight = 50.0;

  @override
  State<GameWidget> createState() => _GameWidgetState();
}

class _GameWidgetState extends State<GameWidget> {
  List<OffsetData> offsets = [];

  @override
  Widget build(BuildContext context) {
    if (offsets.isEmpty) {
      for(int i = 0; i < 20; i++) {
        offsets.add(OffsetData(UniqueKey(), calculateNextOffset()));
      }
    }

    List<Widget> squareWidgets = [];

    for (int j = 0; j < offsets.length; j++) {
      squareWidgets.add(AnimatedPositioned(
        key: offsets[j].key, // This line is the trick
        left: offsets[j].offset.dx,
        top: offsets[j].offset.dy,
        curve: Curves.easeIn,
        duration: const Duration(milliseconds: 500),
        child: GestureDetector(
          onTapDown: (tapDownDetails) {
            setState(() {
              offsets.removeAt(j);
              for (var offsetData in offsets) {
                offsetData.offset = calculateNextOffset();
              }
            });
          },
          behavior: HitTestBehavior.opaque,
          child: Container(
            width: GameWidget.squareWidth,
            height: GameWidget.squareHeight,
            color: Colors.blue,

          ),
        ),
      ));
    }

    return Stack(
      children: squareWidgets,
    );
  }

  Offset calculateNextOffset() {
    return randomOffset(
        MediaQuery.of(context).size,
        const Size(GameWidget.squareWidth, GameWidget.squareHeight),
        MediaQuery.of(context).viewPadding.top);
  }

  double randomNumber(double min, double max) =>
      min + Random().nextDouble() * (max - min);

  Offset randomOffset(
      Size parentSize, Size childSize, double statusBarHeight) {
    var parentWidth = parentSize.width;
    var parentHeight = parentSize.height;

    var randomPosition = Offset(
      randomNumber(parentWidth, childSize.width),
      randomNumber(statusBarHeight,parentHeight - childSize.height),
    );

    return randomPosition;
  }
}

class OffsetData {
  Offset offset;
  final Key key;

  OffsetData(this.key, this.offset);
}

相关问题