【问题标题】:How do i access an inner widget's animation controller (or state) from outer widget?如何从外部小部件访问内部小部件的动画控制器(或状态)?
【发布时间】:2018-04-12 13:16:06
【问题描述】:

所以过去几天我一直在试验 Flutter 和 Dart。 我已经坚持了一天多,所以我在这里。

所以我有 AlarmScreen,里面有 2 个对象。一个是DraggableMoonWidget,另一个是RisingSunWidget

目前,RisingSunWidget 从底部动画到屏幕上,而 DraggableMoonWidget 可以通过触摸拖动。

我想要实现的是,当 RisingSunWidget 的 动画停止并在 DraggableMoonWidget 被拖动时发生变化。所以我有 MoonDragListener 并正在工作,但我仍然无法弄清楚。 (当前将监听器调用回AlarmScreen,但接下来呢?)

我已经尝试了一大堆方法来做到这一点,从那以后都被删除了,因为没有一个有效。

TLDR 当用户触摸 DraggableMoonWidget 时如何控制 RisingSunWidget 的 动画控制器,例如,我想停止控制器并将其动画到不同的点。

飞镖/颤振中最好的方法是什么?

报警屏幕

import 'package:flutter/widgets.dart';
import 'package:moonworshiper_app/backgrounds.dart';
import 'package:moonworshiper_app/ui/alarm/moon_draggable.dart';
import 'package:moonworshiper_app/ui/alarm/rising_sun.dart';

class AlarmScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _AlarmScreenState();
  }
}

class _AlarmScreenState extends State<AlarmScreen> {
  bool _moonWasTouched = false;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new Stack(
      children: <Widget>[
        new DraggableMoonWidget(new MoonDragListener(this)),
        new LayoutBuilder(
            builder: (BuildContext context, BoxConstraints constraints) {
          return new RisingSunWidget(constraints.heightConstraints().maxHeight, _moonWasTouched);
        })
      ],
    );
  }

  void _refreshSun() {
    setState(() {
      _moonWasTouched = true;
    });
  }
}

class MoonDragListener {
  _AlarmScreenState state;

  MoonDragListener(this.state);

  void onMoonDragStarted() {
    state._refreshSun();
  }
}

DraggableMoonWidget

import 'package:flutter/widgets.dart';
import 'package:moonworshiper_app/ui/alarm/alarm_screen.dart';

class DraggableMoonWidget extends StatefulWidget {

  final MoonDragListener moonStartListener;

  DraggableMoonWidget(this.moonStartListener);

  State<StatefulWidget> createState() => new _DraggableMoonState();
}

class _DraggableMoonState extends State<DraggableMoonWidget>
    with TickerProviderStateMixin {


  final moonDragTween = new Tween<Offset>(
    begin: new Offset(0.0, -0.5),
    end: new Offset(0.0, 0.5),
  );

  var moonAnimListener;
  AnimationController _animationController;
  Animation<Offset> _dragAnimation;
  AnimationController _dragAnimationController;
  bool isFirstDraw = true;

  @override
  initState() {
    super.initState();
    _animationController = new AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 3000),
    );
    _dragAnimationController = new AnimationController(vsync: this);

    moonAnimListener = (AnimationStatus status) {
      if (status == AnimationStatus.dismissed) {
        _animationController.forward();
      } else if (status == AnimationStatus.completed) {
        _animationController.reverse();
      } else if (status == AnimationStatus.forward) {}
    };

    _dragAnimation = moonDragTween.animate(new CurvedAnimation(
        parent: _dragAnimationController,
        curve: Curves.easeInOut,
        reverseCurve: Curves.easeInOut));

    _dragAnimationController.animateTo(0.5, duration: new Duration());


    _animationController.addStatusListener(moonAnimListener);


    _animationController.forward();
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new Center(
        child: new SlideTransition(
          position: _dragAnimation,
          child: new GestureDetector(
            child: new Image.asset(
              "assets/moon.png",
              width: 280.0,
              height: 280.0,
            ),
            onVerticalDragStart: (DragStartDetails details) {
              print("start:" + details.globalPosition.toString());
              _animationController.removeStatusListener(moonAnimListener);
              _animationController.stop();
              _dragStartDetails = details;

              _dragAnimationController.animateTo(0.5,
                  duration: new Duration(milliseconds: 50));

              if (isFirstDraw) {
                isFirstDraw = false;
                widget.moonStartListener.onMoonDragStarted();
              }
            },
          ),
        ),
      ),
//      margin: new EdgeInsets.only(top: 48.0),
    );
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }
}

RisingSunWidget

import 'package:flutter/widgets.dart';

class RisingSunWidget extends StatefulWidget {
  // needed to calculate the offset map
  final double screenHeight;

  // that's how we know if the use touched the moon
  final bool moonWasTouched;

  RisingSunWidget(this.screenHeight, this.moonWasTouched);

  @override
  State<StatefulWidget> createState() {
    return new RisingSunState();
  }
}

class RisingSunState extends State<RisingSunWidget> with TickerProviderStateMixin {
  AnimationController _animationController;
  Animation<Offset> _sunAnimation;

  final double sunSize = 320.0;

  @override
  initState() {
    super.initState();

    _animationController = new AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 6000),
    );

    // how many suns fit in the height of our screen

    assert(widget.screenHeight > sunSize);

    double sunsInHeight = widget.screenHeight / sunSize;

    print(sunsInHeight.toString() + " suns could fit on the user's screen");

    var sunsPlusMargins = sunsInHeight + 1; // required margins

    final moonTween = new Tween<Offset>(
      begin: new Offset(0.0, -0.5 * sunsPlusMargins),
      end: new Offset(0.0, 0.5 * sunsPlusMargins), //move by 8% of height max
    );

    _sunAnimation = moonTween.animate(new CurvedAnimation(
      parent: _animationController,
      curve: Curves.easeInOut,
      reverseCurve: Curves.easeInOut,
    ));


    if (widget.moonWasTouched) {
      _animationController.stop();
      _animationController.animateTo(0.68,
          duration: new Duration(milliseconds: 2000));
    } else {
      _animationController.animateTo(0.88,
          duration: new Duration(milliseconds: 0));
      _animationController.animateTo(0.75,
          duration: new Duration(milliseconds: 15000));
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Center(
      child: new SlideTransition(
        position: _sunAnimation,
        child: new Image.asset(
          "assets/sun.png",
          width: sunSize,
          height: sunSize,
        ),
      ),
    );
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }


}

【问题讨论】:

标签: flutter


【解决方案1】:

2 种可能性:

  • 使用您的构建方法中提供的BuildContext contextBuildContext 有一些方法可以获取特定类型的最近父级。
  • key 属性传递给所需的小部件。 GlobalKey 确切地说。 GlobalKey 允许您直接访问 Widget 或其状态。

【讨论】:

  • 你能给我举个例子吗?我在某处读到过这个,但我找不到示例代码。
猜你喜欢
  • 2021-06-10
  • 2021-05-19
  • 2019-08-16
  • 1970-01-01
  • 2017-03-19
  • 2021-09-11
  • 2021-12-24
  • 1970-01-01
  • 2018-02-21
相关资源
最近更新 更多