Flutter: how to play animation with Provider?
Asked Answered
D

4

16

I use Provider to manage state in my Flutter demo.

I want to animate my widget but it seems that AnimateController needs a sync parameter that comes from the state of a stateful widget.

As far as I know, Provider is not recommended to use with a stateful widget.

Can I manage the AnimationController with Provider?

Or must use both Provider and stateful widget?

Donnell answered 3/11, 2019 at 7:49 Comment(4)
Animations belong to the UI, not the state. You need to provide a Ticker to Animation controller. You can use SingleTickerProviderStateMixin to avoid creating it manually. api.flutter.dev/flutter/widgets/…Interne
@RicardoMarkiewicz what if I want to trigger an animation from a different child of same parentSudor
@RicardoMarkiewicz but we all agree that state defines UI? I think Javan wants to know how to make nice animations upon state change when the widget tree gets destroyed. Provider is easy to handle but the instant switch of widgets on state change is strange. How do you tackle that? Say you push login, app starts to login, then switches to downloading profile, downloading this and that and at the end revel the final logged in interface?Dungaree
Your animation controller should be in the UI part, not in the state class because animations are not part of your application state (e.g. are your use logged in?) but part of the UI transient state (state get lost when you leave that screen). You need to use boh Provider and a StatefulWidget, you can use AnimatedWidget, you can use FlutterHooks, but never manage your animations from your state/mode classes.Interne
S
5

Can I manage the AnimationController with Provider?

Or must use both Provider and stateful widget?

I don't know if this is the best way to do it, but here is how I do this :

I use didUpdateWidget in a Stateful widget. I pass the value from the provider to the parent and trigger the animation in the didUpdateWidget method defined in the child. didUpdateWidget being triggered itself when I notify listeners from a change in the Provider.

class MyWidget extends StatefulWidget {
  final String valueFromProvider;

  MyWidget({this.valueFromProvider});

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

class _MyWidgetState extends State<MyWidget> with TickerProviderStateMixin {
  @override
  void didUpdateWidget(MyWidget oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);

    if (oldWidget.valueFromProvider == "whatever you want" &&
        widget.valueFromProvider == "what you want that changed") {
      // trigger animations methods here
    }
  }

  // here all the animations methods,dispose method, etc. 

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
Starlike answered 25/4, 2020 at 9:0 Comment(0)
P
1

If you want to create AnimationController in the StatelessWidget you must pass TickerProviderStateMixin in the Constructor of your StatelessWidget, look at my sample code :

import 'package:flutter/material.dart';

main() {
  runApp(TestyApp());
}

class TestyApp extends StatefulWidget {
  @override
  _TestyAppState createState() => _TestyAppState();
}

class _TestyAppState extends State<TestyApp> with TickerProviderStateMixin {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: TestAnimationInStatelessWidget(
        tickerProviderStateMixin: this,
      ),
    );
  }
}

class TestAnimationInStatelessWidget extends StatelessWidget {
  final TickerProviderStateMixin tickerProviderStateMixin;

  const TestAnimationInStatelessWidget(
      {Key key, @required this.tickerProviderStateMixin})
      : assert(tickerProviderStateMixin != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {

    var _animationController = AnimationController(
        vsync: tickerProviderStateMixin, duration: Duration(seconds: 1));

    var _animation = Tween<double>(begin: 0, end: 2).animate(CurvedAnimation(
        parent: _animationController, curve: Curves.easeInOut));

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            RotationTransition(
              turns: _animation,
              child: Container(
                width: 200,
                height: 200,
                decoration: BoxDecoration(
                  color: Colors.blueGrey,
                  borderRadius: BorderRadius.all(Radius.circular(15)),
                ),
              ),
            ),
            SizedBox(
              height: 30,
            ),
            RaisedButton(
              onPressed: () {
                _animationController.reset();
                _animationController.forward();
              },
              child: Text("Start Animation"),
            )
          ],
        ),
      ),
    );
  }
}
Parallel answered 2/7, 2020 at 13:43 Comment(1)
in your example you are using a stateful widget.Constitutionalism
A
1

I made a work-around overriding the didChangeDependencies method, which fires off AnimationController.forward when prompted.

I got the idea from this stackoverflow thread link

@override
  void didChangeDependencies() {
    super.didChangeDependencies();

    var result = Provider.of<Home>(context).notification;
    if (result == "Play") {
      _controller.forward();
    }
  }

However, this is just a work-around and I am still looking to find a more efficient way to manage this (start/stop animation in a stateful widget from another unrelated widget, using Provider / or other solution).

Accusation answered 8/6, 2021 at 2:58 Comment(0)
S
0

A ValueNotifier<T> from the provider package has the following properties:

  • A value property of type T
  • A hasListeners getter, inherited from ChangeNotifier
  • A dispose method, inherited from ChangeNotifier
  • A notifyListeners method, inherited from ChangeNotifier
  • A addListener method, inherited from Listenable
  • A removeListener method, inherited from Listenable

A AnimationController from the Flutter SDK has the following properties:

  • A value property of type bool
  • didRegisterListener and didUnregisterListener methods, inherited from AnimationLocalListenersMixin
  • A dispose method, inherited from AnimationLocalListenersMixin
  • A notifyListeners method, inherited from AnimationLocalListenersMixin
  • A addListener method, inherited from Listenable
  • A removeListener method, inherited from Listenable

Note that, except by hasListeners getter, an AnimationController can implement a ValueNotifier<bool>, which we can do as follows:

class AnimationControllerNotifier extends AnimationController
    implements ValueNotifier<double> {
  AnimationControllerNotifier({required super.vsync});

  int _listenersCount = 0;

  @override
  void didRegisterListener() {
    ++_listenersCount;
    super.didRegisterListener();
  }

  @override
  void didUnregisterListener() {
    --_listenersCount;
    super.didUnregisterListener();
  }

  @override
  bool get hasListeners => _listenersCount > 0;
}

Now we can use our AnimationControllerNotifier with the provider package:

ChangeNotifierProvider<AnimationControllerNotifier>(
  create: (_) => AnimationControllerNotifier(vsync: /* ... */),
);
Sphacelus answered 9/3, 2023 at 16:36 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.