Анимация флаттера не работает - PullRequest
0 голосов
/ 07 июня 2018

Я пытаюсь создать радиальное меню во флаттере и хочу, чтобы кнопка меню отображала анимацию вращения при каждом нажатии.Я следовал учебнику TensorProgramming на Youtube по основам анимации во Flutter, но моя анимация по какой-то причине не работает.Ниже я включил код моего RadialMenuWidget.

Я правильно расположил контроллер анимации, расширил состояние виджета с помощью SingleTickerProviderStateMixin.PS Я работаю на эмуляторе, и вращение иконки иногда меняется при горячей перезагрузке.

Любая помощь будет высоко ценится!

import 'package:flutter/material.dart';
import 'package:savings/utils/colors.dart';
import 'package:savings/widgets/themed_radial_menu_item.dart';

class CustomThemedRadialMenu extends StatefulWidget {
final List<CustomThemedRadialMenuItem> items;

 CustomThemedRadialMenu({@required this.items});

@override
_CustomThemedRadialMenuState createState() => 
_CustomThemedRadialMenuState();
}

class _CustomThemedRadialMenuState extends State<CustomThemedRadialMenu>
   with SingleTickerProviderStateMixin {
   Animation animationOpenClose;
   AnimationController animationControllerOpenClose;

 bool isOpen;

@override
void initState() {
  isOpen = false;

  animationControllerOpenClose =
      AnimationController(duration: new Duration(seconds: 5), vsync: this);

  animationOpenClose =
      Tween(begin: 0.0, end: 360.0).animate(animationControllerOpenClose)
        ..addListener(() {
          setState(() {});
        });

  animationControllerOpenClose.repeat();

  super.initState();
  }

 @override
 Widget build(BuildContext context) {
  ///A list of the items and the center menu button
  final List<Widget> menuContents = <Widget>[];

 for (int i = 0; i < widget.items.length; i++) {
   ///Menu items
   menuContents.add(widget.items[1]);

    ///Menu Close/Open button
    menuContents.add(new InkWell(
      onTap: () {},
      child: Container(
       padding: new EdgeInsets.all(10.0),
        decoration: new BoxDecoration(
           color: Colors.white,
          border: new Border.all(color: darkHeadingsTextColor),
          shape: BoxShape.circle,
        ),
        child: new Transform.rotate(
            angle: animationControllerOpenClose.value,
            child: isOpen ? new Icon(Icons.clear) : new Icon(Icons.menu)),
      ),
    ));
  }

  return new Stack(
    alignment: Alignment.center,
   children: menuContents,
 );
}

@override
void dispose() {
  animationControllerOpenClose.dispose();

  super.dispose();
 }

closeMenu() {
  animationControllerOpenClose.forward();
  setState(() {
    isOpen = false;
  });

  print("RadialMenu Closed");
}

openMenu() {
  animationControllerOpenClose.forward();
  setState(() {
    isOpen = true;
  });

 print("RadialMenu Opened");
}
}

1 Ответ

0 голосов
/ 07 июня 2018

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

При этом я реализовал то, что думаюты пытался сделать.Я удалил несколько вещей, которые у вас были там, чтобы он действительно собирался, поэтому вам нужно будет добавить их обратно.

Основные проблемы, с которыми вы столкнулись, следующие:

  1. SetState в слушателе

    ..addListener(() {
      setState(() {});
    });
    

Это не идеально, потому что заставит весь виджет перестраивать каждый тик анимации.Это вызовет серьезные проблемы с производительностью для виджета, который не является базовым.Вместо этого используйте AnimatedBuilder.Или, если вы ищете, когда анимация заканчивается, используйте ..addStateListener

Углы в радианах в трепетании.Я не удосужился импортировать Math для константы Pi, но вам, вероятно, следует.

У вас есть касание, которое на самом деле не вызывало openMenu или closeMenu, так чтоопределенно ничего не сделает = D.

Вы бы настроили контроллер анимации на повторение.Это означает, что это будет продолжаться вечно.

Вы не передавали ничего в animationControllerOpenClose.forward вызовы.Это означает, что он будет анимирован до состояния 1.0, где бы он ни находился в данный момент, даже если он уже был 1.0.Я только что передал 0, но вы можете захотеть что-то сделать с открытием / закрытием (если пользователь нажимает во время анимации или что-то в этом роде).

(тангенциальная проблема) ВыКажется, что добавление новой кнопки меню Открыть / Закрыть после каждого виджета меню.Это может быть то, что вы хотите сделать, но я думаю, вы просто хотите добавить один.

В любом случае, вот пример, который работает.Каждый раз, когда вы нажимаете, он меняет значки, а затем поворачивается.

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Spinnig Menu",
      theme: ThemeData(
        primaryColor: Colors.red,
      ),
      home: new Scaffold(
        body: new SafeArea(
          child: new Column(
            children: <Widget>[new CustomThemedRadialMenu()],
          ),
        ),
      ),
    );
  }
}

class CustomThemedRadialMenu extends StatefulWidget {

  @override
  _CustomThemedRadialMenuState createState() => _CustomThemedRadialMenuState();
}

class _CustomThemedRadialMenuState extends State<CustomThemedRadialMenu> with SingleTickerProviderStateMixin {
  Animation animationOpenClose;
  AnimationController animationControllerOpenClose;

  bool isOpen;

  @override
  void initState() {
    isOpen = false;
    animationControllerOpenClose = AnimationController(duration: new Duration(seconds: 5), vsync: this);
    animationOpenClose = Tween(begin: 0.0, end: 3.14159 * 2).animate(animationControllerOpenClose);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    ///A list of the items and the center menu button
    final List<Widget> menuContents = <Widget>[];

    ///Menu Close/Open button
    menuContents.add(new InkWell(
      onTap: () {
        if (isOpen) {
          closeMenu();
        } else {
          openMenu();
        }
      },
      child: Container(
          padding: new EdgeInsets.all(10.0),
          decoration: new BoxDecoration(
            color: Colors.white,
            border: new Border.all(color: Colors.black38),
            shape: BoxShape.circle,
          ),
          child: new AnimatedBuilder(
            animation: animationControllerOpenClose,
            builder: (context, child) {
              return new Transform.rotate(angle: animationOpenClose.value, child: child);
            },
            child: isOpen ? new Icon(Icons.clear) : new Icon(Icons.menu),
          )),
    ));

    return new Stack(
      alignment: Alignment.center,
      children: menuContents,
    );
  }

  @override
  void dispose() {
    animationControllerOpenClose.dispose();

    super.dispose();
  }

  closeMenu() {
    animationControllerOpenClose.forward(from: 0.0);
    setState(() {
      isOpen = false;
    });

    print("RadialMenu Closed");
  }

  openMenu() {
    animationControllerOpenClose.forward(from: 0.0);
    setState(() {
      isOpen = true;
    });

    print("RadialMenu Opened");
  }
}

Следует отметить, что я создаю минимально возможное количество виджетов в функции сборки AnimatedBuilder.Чем меньше там построено, тем лучше производительность.Поскольку значок не изменяется как часть вращения, вы можете просто передать его как дочерний элемент.

Я предполагаю, что это может произойти дальше, так что просто FYI - вы можете использовать анимированный крестисчезает виджет для перехода между иконками (просто добавьте это для потомка AnimatedBuilder):

new AnimatedCrossFade(
            firstChild: new Icon(Icons.clear),
            secondChild: new Icon(Icons.menu),
            crossFadeState: isOpen ? CrossFadeState.showFirst : CrossFadeState.showSecond,
            duration: Duration(milliseconds: 300)),
...