【问题标题】:How to create Confetti animation in Flutter如何在 Flutter 中创建五彩纸屑动画
【发布时间】:2021-07-17 06:38:46
【问题描述】:

我想在我的 Flutter 应用中制作 Confetti 效果动画?

如何在 Flutter 中探索五彩纸屑动画,以及如何在我们的 Flutter 应用程序中添加五彩纸屑动画并展示多彩的爆炸效果。

【问题讨论】:

    标签: flutter dart animation flutter-animation


    【解决方案1】:

    这里是实现五彩纸屑效果动画的过程

    首先为BlastDirectionality创建两个枚举

    enum BlastDirectionality {
      directional,
      explosive,
    }
    
    enum ConfettiControllerState {
      playing,
      stopped,
    }
    

    创建一个ConfettiWidget

    import 'dart:math';
    
    import 'package:flutter/material.dart';
    import 'package:prokit_flutter/main/utils/confetti/src/particle.dart';
    
    import 'enums/blast_directionality.dart';
    import 'enums/confetti_controller_state.dart';
    
    class ConfettiWidget extends StatefulWidget {
      const ConfettiWidget({
        Key key,
        @required this.confettiController,
        this.emissionFrequency = 0.02,
        this.numberOfParticles = 10,
        this.maxBlastForce = 20,
        this.minBlastForce = 5,
        this.blastDirectionality = BlastDirectionality.directional,
        this.blastDirection = pi,
        this.gravity = 0.2,
        this.shouldLoop = false,
        this.displayTarget = false,
        this.colors,
        this.minimumSize = const Size(20, 10),
        this.maximumSize = const Size(30, 15),
        this.particleDrag = 0.05,
        this.canvas,
        this.child,
      })  : assert(
                confettiController != null,
                emissionFrequency != null &&
                    numberOfParticles != null &&
                    maxBlastForce != null &&
                    minBlastForce != null &&
                    blastDirectionality != null &&
                    blastDirection != null),
            assert(emissionFrequency >= 0 &&
                emissionFrequency <= 1 &&
                numberOfParticles > 0 &&
                maxBlastForce > 0 &&
                minBlastForce > 0 &&
                maxBlastForce > minBlastForce),
            assert(gravity >= 0 && gravity <= 1),
            super(key: key);
    
      final ConfettiController confettiController;
    
      final double maxBlastForce;
    
      final double minBlastForce;
    
      final BlastDirectionality blastDirectionality;
    
      final double blastDirection;
      final double gravity;
      final double emissionFrequency;
      final int numberOfParticles;
      final bool shouldLoop;
      final bool displayTarget;
    
      final List<Color> colors;
      final Size minimumSize;
      final Size maximumSize;
    
      final double particleDrag;
      final Size canvas;
    
      final Widget child;
    
      @override
      _ConfettiWidgetState createState() => _ConfettiWidgetState();
    }
    
    class _ConfettiWidgetState extends State<ConfettiWidget>
        with SingleTickerProviderStateMixin {
      final GlobalKey _particleSystemKey = GlobalKey();
    
      AnimationController _animController;
      Animation<double> _animation;
      ParticleSystem _particleSystem;
    
      Offset _emitterPosition;
    
      Size _screenSize = const Size(0, 0);
    
      @override
      void initState() {
        widget.confettiController.addListener(_handleChange);
    
        _particleSystem = ParticleSystem(
            emissionFrequency: widget.emissionFrequency,
            numberOfParticles: widget.numberOfParticles,
            maxBlastForce: widget.maxBlastForce,
            minBlastForce: widget.minBlastForce,
            gravity: widget.gravity,
            blastDirection: widget.blastDirection,
            blastDirectionality: widget.blastDirectionality,
            colors: widget.colors,
            minimumSize: widget.minimumSize,
            maximumsize: widget.maximumSize,
            particleDrag: widget.particleDrag);
    
        _particleSystem.addListener(_particleSystemListener);
    
        _initAnimation();
        super.initState();
      }
    
      void _initAnimation() {
        _animController = AnimationController(
            vsync: this, duration: widget.confettiController.duration);
        _animation = Tween<double>(begin: 0, end: 1).animate(_animController);
        _animation.addListener(_animationListener);
        _animation.addStatusListener(_animationStatusListener);
    
        if (widget.confettiController.state == ConfettiControllerState.playing) {
          _startAnimation();
          _startEmission();
        }
      }
    
      void _handleChange() {
        if (widget.confettiController.state == ConfettiControllerState.playing) {
          _startAnimation();
          _startEmission();
        } else if (widget.confettiController.state ==
            ConfettiControllerState.stopped) {
          _stopEmission();
        }
      }
    
      void _animationListener() {
        if (_particleSystem.particleSystemStatus == ParticleSystemStatus.finished) {
          _animController.stop();
          return;
        }
        _particleSystem.update();
      }
    
      void _animationStatusListener(AnimationStatus status) {
        if (status == AnimationStatus.completed) {
          if (!widget.shouldLoop) {
            _stopEmission();
          }
          _continueAnimation();
        }
      }
    
      void _particleSystemListener() {
        if (_particleSystem.particleSystemStatus == ParticleSystemStatus.finished) {
          _stopAnimation();
        }
      }
    
      void _startEmission() {
        _particleSystem.startParticleEmission();
      }
    
      void _stopEmission() {
        if (_particleSystem.particleSystemStatus == ParticleSystemStatus.stopped) {
          return;
        }
        _particleSystem.stopParticleEmission();
      }
    
      void _startAnimation() {
        WidgetsBinding.instance.addPostFrameCallback((_) {
          _setScreenSize();
          _setEmitterPosition();
          _animController.forward(from: 0);
        });
      }
    
      void _stopAnimation() {
        _animController.stop();
      }
    
      void _continueAnimation() {
        _animController.forward(from: 0);
      }
    
      void _setScreenSize() {
        _screenSize = _getScreenSize();
        _particleSystem.screenSize = _screenSize;
      }
    
      void _setEmitterPosition() {
        _emitterPosition = _getContainerPosition();
        _particleSystem.particleSystemPosition = _emitterPosition;
      }
    
      Offset _getContainerPosition() {
        final RenderBox containerRenderBox =
            _particleSystemKey.currentContext.findRenderObject();
        return containerRenderBox.localToGlobal(Offset.zero);
      }
    
      Size _getScreenSize() {
        return widget.canvas ?? MediaQuery.of(context).size;
      }
    
      void _updatePositionAndSize() {
        if (_getScreenSize() != _screenSize) {
          _setScreenSize();
          if (_emitterPosition != null) {
            _setEmitterPosition();
          }
        }
      }
    
      @override
      Widget build(BuildContext context) {
        _updatePositionAndSize();
        return RepaintBoundary(
          child: CustomPaint(
            key: _particleSystemKey,
            foregroundPainter: ParticlePainter(
              _animController,
              particles: _particleSystem.particles,
              paintEmitterTarget: widget.displayTarget,
            ),
            child: widget.child,
          ),
        );
      }
    
      @override
      void dispose() {
        widget.confettiController.stop();
        _animController.dispose();
        widget.confettiController.removeListener(_handleChange);
        _particleSystem.removeListener(_particleSystemListener);
        _particleSystem = null;
        super.dispose();
      }
    }
    
    class ParticlePainter extends CustomPainter {
      ParticlePainter(Listenable repaint,
          {@required this.particles,
          paintEmitterTarget = true,
          emitterTargetColor = Colors.black})
          : _paintEmitterTarget = paintEmitterTarget,
            _emitterPaint = Paint()
              ..color = emitterTargetColor
              ..style = PaintingStyle.stroke
              ..strokeWidth = 2.0,
            _particlePaint = Paint()
              ..color = Colors.green
              ..style = PaintingStyle.fill,
            super(repaint: repaint);
    
      final List<Particle> particles;
    
      final Paint _emitterPaint;
      final bool _paintEmitterTarget;
      final Paint _particlePaint;
    
      @override
      void paint(Canvas canvas, Size size) {
        if (_paintEmitterTarget) {
          _paintEmitter(canvas);
        }
        if (particles == null) {
          return;
        }
        _paintParticles(canvas);
      }
    
      void _paintEmitter(Canvas canvas) {
        const radius = 10.0;
        canvas.drawCircle(Offset.zero, radius, _emitterPaint);
        final path = Path();
        path.moveTo(0, -radius);
        path.lineTo(0, radius);
        path.moveTo(-radius, 0);
        path.lineTo(radius, 0);
        canvas.drawPath(path, _emitterPaint);
      }
    
      void _paintParticles(Canvas canvas) {
        for (final particle in particles) {
          final rotationMatrix4 = Matrix4.identity();
          rotationMatrix4
            ..translate(particle.location.dx, particle.location.dy)
            ..rotateX(particle.angleX)
            ..rotateY(particle.angleY)
            ..rotateZ(particle.angleZ);
    
          final finalPath = particle.path.transform(rotationMatrix4.storage);
          canvas.drawPath(finalPath, _particlePaint..color = particle.color);
        }
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        return true;
      }
    }
    
    class ConfettiController extends ChangeNotifier {
      ConfettiController({this.duration = const Duration(seconds: 30)})
          : assert(duration != null &&
                !duration.isNegative &&
                duration.inMicroseconds > 0);
    
      Duration duration;
    
      ConfettiControllerState _state = ConfettiControllerState.stopped;
    
      ConfettiControllerState get state => _state;
    
      void play() {
        _state = ConfettiControllerState.playing;
        notifyListeners();
      }
    
      void stop() {
        _state = ConfettiControllerState.stopped;
        notifyListeners();
      }
    }
    

    创建一个Helper

    import 'dart:math';
    import 'dart:ui' as ui;
    
    final _rand = Random();
    
    double randomize(double min, double max) {
      return ui.lerpDouble(min, max, _rand.nextDouble());
    }
    
    void debugPrint(String message) {
      assert(() {
        print('__debug__confetti__$message');
        return true;
      }());
    }
    

    再创建一个类 ParticleSystem

    import 'dart:math';
    import 'dart:ui';
    
    import 'package:flutter/material.dart';
    import 'package:prokit_flutter/main/utils/confetti/src/helper.dart';
    import 'package:prokit_flutter/main/utils/randomcolor/random_color.dart';
    
    import 'package:vector_math/vector_math.dart' as vmath;
    
    import 'enums/blast_directionality.dart';
    
    enum ParticleSystemStatus {
      started,
      finished,
      stopped,
    }
    
    class ParticleSystem extends ChangeNotifier {
      ParticleSystem(
          {@required double emissionFrequency,
          @required int numberOfParticles,
          @required double maxBlastForce,
          @required double minBlastForce,
          @required double blastDirection,
          @required BlastDirectionality blastDirectionality,
          @required List<Color> colors,
          @required Size minimumSize,
          @required Size maximumsize,
          @required double particleDrag,
          @required double gravity})
          : assert(
              emissionFrequency != null &&
                  numberOfParticles != null &&
                  maxBlastForce != null &&
                  minBlastForce != null &&
                  blastDirection != null &&
                  minimumSize != null &&
                  maximumsize != null &&
                  particleDrag != null &&
                  blastDirectionality != null,
            ),
            assert(maxBlastForce > 0 &&
                minBlastForce > 0 &&
                emissionFrequency >= 0 &&
                emissionFrequency <= 1 &&
                numberOfParticles > 0 &&
                minimumSize.width > 0 &&
                minimumSize.height > 0 &&
                maximumsize.width > 0 &&
                maximumsize.height > 0 &&
                minimumSize.width <= maximumsize.width &&
                minimumSize.height <= maximumsize.height &&
                particleDrag >= 0.0 &&
                particleDrag <= 1 &&
                minimumSize.height <= maximumsize.height),
            assert(gravity >= 0 && gravity <= 1),
            _blastDirection = blastDirection,
            _blastDirectionality = blastDirectionality,
            _gravity = gravity,
            _maxBlastForce = maxBlastForce,
            _minBlastForce = minBlastForce,
            _frequency = emissionFrequency,
            _numberOfParticles = numberOfParticles,
            _colors = colors,
            _minimumSize = minimumSize,
            _maximumSize = maximumsize,
            _particleDrag = particleDrag,
            _rand = Random();
    
      ParticleSystemStatus _particleSystemStatus;
    
      final List<Particle> _particles = [];
      final double _frequency;
      final int _numberOfParticles;
      final double _maxBlastForce;
      final double _minBlastForce;
      final double _blastDirection;
      final BlastDirectionality _blastDirectionality;
      final double _gravity;
      final List<Color> _colors;
      final Size _minimumSize;
      final Size _maximumSize;
      final double _particleDrag;
    
      Offset _particleSystemPosition;
      Size _screenSize;
    
      double _bottomBorder;
      double _rightBorder;
      double _leftBorder;
    
      final Random _rand;
    
      set particleSystemPosition(Offset position) {
        _particleSystemPosition = position;
      }
    
      set screenSize(Size size) {
        _screenSize = size;
        _setScreenBorderPositions();
      }
    
      void stopParticleEmission() {
        _particleSystemStatus = ParticleSystemStatus.stopped;
      }
    
      void startParticleEmission() {
        _particleSystemStatus = ParticleSystemStatus.started;
      }
    
      void finishParticleEmission() {
        _particleSystemStatus = ParticleSystemStatus.finished;
      }
    
      List<Particle> get particles => _particles;
      ParticleSystemStatus get particleSystemStatus => _particleSystemStatus;
    
      void update() {
        _clean();
        if (_particleSystemStatus != ParticleSystemStatus.finished) {
          _updateParticles();
        }
    
        if (_particleSystemStatus == ParticleSystemStatus.started) {
          if (particles.isEmpty) {
            _particles.addAll(_generateParticles(number: _numberOfParticles));
            return;
          }
    
          final chanceToGenerate = _rand.nextDouble();
          if (chanceToGenerate < _frequency) {
            _particles.addAll(_generateParticles(number: _numberOfParticles));
          }
        }
    
        if (_particleSystemStatus == ParticleSystemStatus.stopped &&
            _particles.isEmpty) {
          finishParticleEmission();
          notifyListeners();
        }
      }
    
      void _setScreenBorderPositions() {
        _bottomBorder = _screenSize.height * 1.1;
        _rightBorder = _screenSize.width * 1.1;
        _leftBorder = _screenSize.width - _rightBorder;
      }
    
      void _updateParticles() {
        if (particles == null) {
          return;
        }
        for (final particle in _particles) {
          particle.update();
        }
      }
    
      void _clean() {
        if (_particleSystemPosition != null &&
            _screenSize != null &&
            particles != null) {
          _particles
              .removeWhere((particle) => _isOutsideOfBorder(particle.location));
        }
      }
    
      bool _isOutsideOfBorder(Offset particleLocation) {
        final globalParticlePosition = particleLocation + _particleSystemPosition;
        return (globalParticlePosition.dy >= _bottomBorder) ||
            (globalParticlePosition.dx >= _rightBorder) ||
            (globalParticlePosition.dx <= _leftBorder);
      }
    
      List<Particle> _generateParticles({int number = 1}) {
        return List<Particle>.generate(
            number,
            (i) => Particle(_generateParticleForce(), _randomColor(), _randomSize(),
                _gravity, _particleDrag));
      }
    
      double get _randomBlastDirection =>
          vmath.radians(Random().nextInt(359).toDouble());
    
      vmath.Vector2 _generateParticleForce() {
        var blastDirection = _blastDirection;
        if (_blastDirectionality == BlastDirectionality.explosive) {
          blastDirection = _randomBlastDirection;
        }
        final blastRadius = randomize(_minBlastForce, _maxBlastForce);
        final y = blastRadius * sin(blastDirection);
        final x = blastRadius * cos(blastDirection);
        return vmath.Vector2(x, y);
      }
    
      Color _randomColor() {
        if (_colors != null) {
          if (_colors.length == 1) {
            return _colors[0];
          }
          final index = _rand.nextInt(_colors.length);
          return _colors[index];
        }
        return RandomColor().randomColor();
      }
    
      Size _randomSize() {
        return Size(
          randomize(_minimumSize.width, _maximumSize.width),
          randomize(_minimumSize.height, _maximumSize.height),
        );
      }
    }
    
    class Particle {
      Particle(vmath.Vector2 startUpForce, Color color, Size size, double gravity,
          double particleDrag)
          : _startUpForce = startUpForce,
            _color = color,
            _mass = randomize(1, 11),
            _particleDrag = particleDrag,
            _location = vmath.Vector2.zero(),
            _acceleration = vmath.Vector2.zero(),
            _velocity = vmath.Vector2(randomize(-3, 3), randomize(-3, 3)),
            _pathShape = createPath(size),
            _aVelocityX = randomize(-0.1, 0.1),
            _aVelocityY = randomize(-0.1, 0.1),
            _aVelocityZ = randomize(-0.1, 0.1),
            _gravity = lerpDouble(0.1, 5, gravity);
    
      final vmath.Vector2 _startUpForce;
    
      final vmath.Vector2 _location;
      final vmath.Vector2 _velocity;
      final vmath.Vector2 _acceleration;
    
      final double _particleDrag;
      double _aX = 0;
      double _aVelocityX;
      double _aY = 0;
      double _aVelocityY;
      double _aZ = 0;
      double _aVelocityZ;
      final double _gravity;
      final _aAcceleration = 0.0001;
    
      final Color _color;
      final double _mass;
      final Path _pathShape;
    
      double _timeAlive = 0;
    
      static Path createPath(Size size) {
        final pathShape = Path();
        pathShape.moveTo(0, 0);
        pathShape.lineTo(-size.width, 0);
        pathShape.lineTo(-size.width, size.height);
        pathShape.lineTo(0, size.height);
        pathShape.close();
        return pathShape;
      }
    
      void applyForce(vmath.Vector2 force) {
        final f = force.clone();
        f.divide(vmath.Vector2.all(_mass));
        _acceleration.add(f);
      }
    
      void drag() {
        final speed = sqrt(pow(_velocity.x, 2) + pow(_velocity.y, 2));
        final dragMagnitude = _particleDrag * speed * speed;
        final drag = _velocity.clone();
        drag.multiply(vmath.Vector2.all(-1));
        drag.normalize();
        drag.multiply(vmath.Vector2.all(dragMagnitude));
        applyForce(drag);
      }
    
      void _applyStartUpForce() {
        applyForce(_startUpForce);
      }
    
      void _applyWindForceUp() {
        applyForce(vmath.Vector2(0, -1));
      }
    
      void update() {
        drag();
    
        if (_timeAlive < 5) {
          _applyStartUpForce();
        }
        if (_timeAlive < 25) {
          _applyWindForceUp();
        }
    
        _timeAlive += 1;
    
        applyForce(vmath.Vector2(0, _gravity));
    
        _velocity.add(_acceleration);
        _location.add(_velocity);
        _acceleration.setZero();
    
        _aVelocityX += _aAcceleration / _mass;
        _aVelocityY += _aAcceleration / _mass;
        _aVelocityZ += _aAcceleration / _mass;
        _aX += _aVelocityX;
        _aY += _aVelocityY;
        _aZ += _aVelocityZ;
      }
    
      Offset get location {
        if (_location.x.isNaN || _location.y.isNaN) {
          return const Offset(0, 0);
        }
        return Offset(_location.x, _location.y);
      }
    
      Color get color => _color;
      Path get path => _pathShape;
    
      double get angleX => _aX;
      double get angleY => _aY;
      double get angleZ => _aZ;
    }
    

    并导入所有这些文件并在您的屏幕上使用它们

    class CHomePage extends StatefulWidget {
      static String tag = '/CHomePage';
    
      @override
      _CHomePageState createState() => _CHomePageState();
    }
    
    class _CHomePageState extends State<CHomePage> {
      ConfettiController _controllerCenter;
      ConfettiController _controllerCenterRight;
      ConfettiController _controllerCenterLeft;
      ConfettiController _controllerTopCenter;
      ConfettiController _controllerBottomCenter;
    
      @override
      void initState() {
        _controllerCenter =
            ConfettiController(duration: const Duration(seconds: 5));
        _controllerCenterRight =
            ConfettiController(duration: const Duration(seconds: 5));
        _controllerCenterLeft =
            ConfettiController(duration: const Duration(seconds: 5));
        _controllerTopCenter =
            ConfettiController(duration: const Duration(seconds: 5));
        _controllerBottomCenter =
            ConfettiController(duration: const Duration(seconds: 5));
        super.initState();
      }
    
      @override
      void dispose() {
        _controllerCenter.dispose();
        _controllerCenterRight.dispose();
        _controllerCenterLeft.dispose();
        _controllerTopCenter.dispose();
        _controllerBottomCenter.dispose();
        super.dispose();
      }
    
      Text _display(String text) {
        return Text(
          text,
          style: const TextStyle(
            color: Colors.white,
            fontSize: 14,
            fontWeight: FontWeight.bold,
          ),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
            body: Stack(
              children: <Widget>[
                Container(
                  alignment: Alignment.topCenter,
                  child: ConfettiWidget(
                    confettiController: _controllerCenter,
                    blastDirectionality: BlastDirectionality.explosive,
                    shouldLoop: false,
                    emissionFrequency: 0.1,
                    canvas:
                        Size.fromRadius(MediaQuery.of(context).size.height * .35),
                    colors: const [
                      Colors.green,
                      Colors.blue,
                      Colors.pink,
                      Colors.orange,
                      Colors.purple
                    ], 
                  ),
                ),
                Container(
                  margin: EdgeInsets.only(bottom: 150),
                  alignment: Alignment.bottomCenter,
                  child: ConfettiWidget(
                    confettiController: _controllerBottomCenter,
                    blastDirectionality: BlastDirectionality
                        .explosive, 
                    shouldLoop: false,
                    emissionFrequency: 0.3,
    
                    canvas: Size.fromRadius(350),
                    colors: const [
                      Colors.green,
                      Colors.blue,
                      Colors.pink,
                      Colors.orange,
                      Colors.purple
                    ],
                  ),
                ),
                Container(
                  alignment: Alignment.centerRight,
                  child: ConfettiWidget(
                    confettiController: _controllerCenterRight,
                    blastDirectionality: BlastDirectionality
                        .explosive, 
                    shouldLoop: false, 
                    emissionFrequency: 0.2,
                    canvas:
                        Size.fromRadius(MediaQuery.of(context).size.height * .35),
                    colors: const [
                      Colors.black,
                      Colors.redAccent,
                      Colors.tealAccent,
                      Colors.yellowAccent,
                      Colors.orange
                    ], 
                  ),
                ),
                Container(
                  alignment: Alignment.centerLeft,
                  child: ConfettiWidget(
                    confettiController: _controllerCenterLeft,
                    blastDirectionality: BlastDirectionality
                        .explosive,
                    shouldLoop: false,
                    canvas:
                        Size.fromRadius(MediaQuery.of(context).size.height * .35),
                    emissionFrequency: 0.8,
                    colors: const [
                      Colors.deepPurple,
                      Colors.yellow,
                      Colors.blueAccent,
                      Colors.green,
                      Colors.purple
                    ], 
                  ),
                ),
                Container(
                  alignment: Alignment.bottomCenter,
                  margin: EdgeInsets.only(bottom: 150, left: 15, right: 15),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Expanded(
                        flex: 1,
                        child: Container(
                          margin: EdgeInsets.only(right: 4),
                          decoration: BoxDecoration(
                              color: Color(0xFF8998FF),
                              borderRadius: BorderRadius.circular(10)),
                          child: FlatButton(
                            onPressed: () {
                              _controllerCenterLeft.play();
                            },
                            child: _display('Left'),
                          ),
                        ),
                      ),
                      Expanded(
                        flex: 1,
                        child: Container(
                          margin: EdgeInsets.only(right: 4),
                          decoration: BoxDecoration(
                              color: Color(0xFF8998FF),
                              borderRadius: BorderRadius.circular(10)),
                          child: FlatButton(
                            onPressed: () {
                              _controllerCenter.play();
                            },
                            child: _display('Top'),
                          ),
                        ),
                      ),
                      Expanded(
                        flex: 1,
                        child: Container(
                          margin: EdgeInsets.only(right: 4),
                          decoration: BoxDecoration(
                              color: Color(0xFF8998FF),
                              borderRadius: BorderRadius.circular(10)),
                          child: FlatButton(
                            onPressed: () {
                              _controllerBottomCenter.play();
                            },
                            child: _display('Bottom'),
                          ),
                        ),
                      ),
                      Expanded(
                        flex: 1,
                        child: Container(
                          decoration: BoxDecoration(
                              color: Color(0xFF8998FF),
                              borderRadius: BorderRadius.circular(10)),
                          child: FlatButton(
                            onPressed: () {
                              _controllerCenterRight.play();
                            },
                            child: _display('Right'),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-02
      • 2018-02-11
      • 2015-09-12
      • 1970-01-01
      • 2021-11-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多