Код флаттера, создающий запаздывающую анимацию. Что здесь не так?

Я создал флаттер-виджет, который перемещает круг с помощью акселерометра. Это очень тормозит, так как мне приходится использовать setState для изменения положения круга при перемещении телефона. Есть ли альтернатива созданию этого?

Я использовал здесь AnimatedBuilder, но не уверен, как это может изменить положение круга при плавном перемещении устройства.

class _AnimationWidgetState extends State<AnimationWidget>
    with TickerProviderStateMixin {
  AnimationController _animeController;
  Animation _anime;
  double x = 0.0, y = 0.0;
  @override
  void initState() {
    super.initState();
    _animeController =
        AnimationController(vsync: this, duration: const Duration(seconds: 2));
    _anime = Tween(begin: 0.5, end: 0.5).animate(
        CurvedAnimation(parent: _animeController, curve: Curves.ease));
    accelerometerEvents.listen((AccelerometerEvent event) {
      var a = ((event.x * 100).round() / 100).clamp(-1.0, 1.0) * -1;
      var b = ((event.y * 100).round() / 100).clamp(-1.0, 1.0);
      if ((x - a).abs() > 0.02 || (y - b).abs() > 0.02) {
        setState(() {
          x = a; y = b;
        });
      }
    });
  }

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

  @override
  Widget build(BuildContext context) {
    _animeController.forward();
    final double width = MediaQuery.of(context).size.width;
    final double height = MediaQuery.of(context).size.height;
    return AnimatedBuilder(
      animation: _animeController,
      builder: (context, child) {
        return Scaffold(
          body: Transform(
            transform: Matrix4.translationValues(
                _anime.value * width * x, _anime.value * height * y, 0.0),
            child: Center(
              child: CircleAvatar(
                radius: 15.0,
                backgroundColor: Colors.green,
              ),
            ),
          ),
        );
      },
    );
  }
}

Анимация вообще не плавная. Это потому, что я должен использовать setState, но движение круга работает как надо.


person Shikhir Aggarwal    schedule 12.06.2019    source источник
comment
Вы запускаете его в режиме отладки? Вы пробовали режим выпуска? Попробуйте flutter run --release или просто flutter build   -  person Julio Henrique Bitencourt    schedule 12.06.2019
comment
как часто вызывается метод build? почему порог 0.02? и почему вы вызываете _animeController.forward(); внутри него (и с постоянной продолжительностью 2 секунды)?   -  person pskink    schedule 12.06.2019


Ответы (1)


Вся цель использования AnimationController состоит в том, чтобы прослушивать его события - это то, что делает AnimatedBuilder и соответствующим образом перестраивает свое поддерево.

Я буду публиковать здесь свои общие рекомендации о том, что нужно изменить в коде.


Удалите setState - вот что заставляет всю вашу верстку пересобирать заново, т.е. лагать.

Также запустите слушателей _animeController, т.е. AnimatedBuilder в вашем случае - чтобы пересобрать себя.

accelerometerEvents.listen((AccelerometerEvent event) {
  var a = ((event.x * 100).round() / 100).clamp(-1.0, 1.0) * -1;
  var b = ((event.y * 100).round() / 100).clamp(-1.0, 1.0);
  if ((x - a).abs() > 0.02 || (y - b).abs() > 0.02) {
    x = a; y = b;
    _animeController.value = _animeController.value; // Trigger controller's listeners
  }
});

Запустите анимацию в initState, вместо build. Это второе, что вызывает лаги в вашем случае. .forward вызывает перестроение ваших виджетов, что приводит к бесконечному циклу.

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

Используйте свойство child вашего AnimatedBuilder, чтобы каждый раз экономить ресурсы на перестроении блока аватара. Также я не уверен, для чего здесь Scaffold - давайте удалим его, если он не нужен.

AnimatedBuilder(
  animation: _animeController,
  builder: (context, child) => Transform(
    transform: Matrix4.translationValues(_anime.value * width * x, _anime.value * height * y, 0.0),
    child: child,
  ),
  child: Center(
    child: CircleAvatar(
      radius: 15.0,
      backgroundColor: Colors.green,
    ),
  ),
);

Следуйте официальному руководству по анимации, чтобы освоить анимацию Flutter.

Позвольте мне знать, если это помогает.

person George    schedule 12.06.2019
comment
Убрал скаффолд, убрал setState, выставил в initState. Анимация стала лучше, но все еще недостаточно хороша и тормозит. Как будто он не может обновляться достаточно быстро при каждом изменении значения события акселерометра. - person Shikhir Aggarwal; 13.06.2019
comment
@ShikhirAggarwal попробуйте удалить условие if в accelerometerEvents.listen. Это может ограничивать частоту обновления. - person George; 13.06.2019
comment
Да убрал, но все равно лагает. Но в любом случае это намного лучше, чем раньше, и даже немного лучше в режиме релиза. Так что в целом хорошо спасибо - person Shikhir Aggarwal; 13.06.2019
comment
@ShikhirAggarwal рад помочь! - person George; 13.06.2019