Что такое vsync flutter
AnimationController class Null safety
A controller for an animation.
This class lets you perform tasks such as:
By default, an AnimationController linearly produces values that range from 0.0 to 1.0, during a given duration. The animation controller generates a new value whenever the device running your app is ready to display a new frame (typically, this rate is around 60 values per second).
Ticker providers
An AnimationController needs a TickerProvider, which is configured using the vsync argument on the constructor.
The TickerProvider interface describes a factory for Ticker objects. A Ticker is an object that knows how to register itself with the SchedulerBinding and fires a callback every frame. The AnimationController class uses a Ticker to step through the animation that it controls.
If an AnimationController is being created from a State, then the State can use the TickerProviderStateMixin and SingleTickerProviderStateMixin classes to implement the TickerProvider interface. The TickerProviderStateMixin class always works for this purpose; the SingleTickerProviderStateMixin is slightly more efficient in the case of the class only ever needing one Ticker (e.g. if the class creates only a single AnimationController during its entire lifetime).
The widget test framework WidgetTester object can be used as a ticker provider in the context of tests. In other contexts, you will have to either pass a TickerProvider from a higher level (e.g. indirectly from a State that mixes in TickerProviderStateMixin), or create a custom TickerProvider subclass.
Life cycle
An AnimationController should be disposed when it is no longer needed. This reduces the likelihood of leaks. When used with a StatefulWidget, it is common for an AnimationController to be created in the State.initState method and then disposed in the State.dispose method.
Using Futures with AnimationController
The methods that start animations return a TickerFuture object which completes when the animation completes successfully, and never throws an error; if the animation is canceled, the future never completes. This object also has a TickerFuture.orCancel property which returns a future that completes when the animation completes successfully, and completes with an error when the animation is aborted.
This can be used to write code such as the fadeOutAndUpdateState method below.
In Flutter, why some animation classes need vsync while others don’t?
In Flutter, some animation classes need you to pass vsync in the constructor, while others don’t. For example, AnimatedSize needs vsync. AnimatedContainer doesn’t.
1) Why some animation classes need it and some don’t?
2) Does that relate to those classes having state (their own internal vsync)?
3) All classes that use vsync could be turned into classes that don’t? And what’s the trade-off?
2 Answers 2
vsync is the property which represents the TickerProvider (i.e., Tick is similar to clock’s tick which means that at every certain duration TickerProvider will render the class state and redraw the object.)
vsync property is required only on that constructors which requires to render it’s class state at every certain off-set time when we need to render our components or widgets to redraw and reflect the UI.
vsync can be used with the classes which requires certain transition or animation to re-render to draw different objects.
For Ex : vsync with AnimationController() class will inform our app to redraw the frames at every fraction of seconds to generate the animation for providing greater user experience.
Flutter good animation framework has
vsync takes a TickerProvider as an argument, that’s why we use SingleTickerProviderStateMixin and as the named describes TickerProvider provides Ticker which simply means it tells our app about the Frame update(or Screen Update), so that our AnimationController can generate a new value and we can redraw the animated widget.
The main advantage of having vsync(TickerProvider) is used with Controllers which can be paused when your widgets are not on screen. If we don’t have this then rendering will perform continuously even if the screen is not visible to user.
Not the answer you’re looking for? Browse other questions tagged flutter flutter-animation or ask your own question.
Related
Hot Network Questions
Subscribe to RSS
To subscribe to this RSS feed, copy and paste this URL into your RSS reader.
site design / logo © 2021 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2021.12.22.41046
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.
articles
Recent Posts
Why Flutter animations need a vsync/TickerProvider
The difference between a «factory constructor» and a «static method»
Getting started: Creating your Flutter project
Let’s build a todo-list
What’s new in provider 4.0.0!
Share this post
Why Flutter animations need a vsync/TickerProvider
An article about Ticker and TickerProvider
If you have used Flutter animations before, you likely came across a parameter named vsync or the mixin SingleTickerProviderStateMixin.
But we are rarely using them ourselves. So what do they even do?
To answer these questions, we will use a simple animation as an example: A clock
For a clock, AnimationController doesn’t seem very useful:
All in all, we may be tempted to not use AnimationController.
A naive approach could be to setState every few frames and use DateTime.now() like so:
At first glance, it seems to work (the previous gif was recorded using it after-all). But plot twist, it suffers from major drawbacks:
But more importantly, it is inefficient:
An animated widget may not be visible
You see, while our Clock widget is inside the widget tree, there is absolutely no guarantee that it is visible:
One way to highlight this problem is to enable the in-app performance overlay:
From this gif, we can see that even when a new page appeared on the top of our clock, our screen still keeps refreshing. This is bad.
This is why we have that vsync parameter on AnimationController,
Vsync/TickerProvider allows animations to be muted, slowed, or fast-forwarded
Be it tests, dev tools, or because the animation is no longer visible, the speed of our animation may depend on factors external to the animation itself.
Using Flutter’s animation framework, this is achieved through a Ticker + TickerProvider.
More specifically, when writing:
Then we are saying “the animation associated to controller can be paused when our Example widget is hidden”.
Note:
This isn’t exactly about whether or not Example is visible, but being more specific would be confusing.
If you are interested in it, you can read about TickerMode.
Reimplementing our Clock with Ticker/TickerProvider
Let’s fix the performance issues of our clock by using Ticker/TickerProvider to control the animation.
Creating a Ticker with TickerProvider
Our first goal is to create a Ticker. For this, we will need a TickerProvider, such as SingleTickerProviderStateMixin.
It is important to use an official TickerProvider, as we could otherwise break the “pause the animation” feature.
Don’t create one TickerProvider and reuse it for all widgets either.
Your TickerProvider should be associated with the widget that wants to perform an animation. As such, avoid storing a TickerProvider as a global/singleton or passing it with provider.
A good rule of thumb is to accept that StatefulWidget + SingleTickerProviderStateMixin is verbose, and not fight against it.
Anyway, let’s create our Ticker:
Do not forget to call dispose on your Ticker when the state is destroyed.
Using our Ticker to keep track of the current time
Now that we have created a Ticker, let’s start using it. We will want to do two things:
First, let’s obtain the current time when the animation starts. Nothing special here, we will simply call DateTime.now() (although we will come back to this code later for testing).
Then, we will use that _currentTime variable inside our build method to render our clock:
Finally, we will use our Ticker to update our _currentTime variable.
This can be done with the elapsed parameter of the function passed when creating our Ticker.
That elapsed parameter represents how much time has passed since the beginning of the animation. As such, the current time is the time when the animation started + the time elapsed.
We can update our createTicker with this logic:
That’s it! At this stage, we should have a working clock.
The difference with before? Let’s do the same test (pushing a new route) with the performance overlay enabled:
As you can see from this new gif, now as soon as a new route is pushed, the screen stops refreshing: Our clock animation is paused.
Info:
A bonus for using Ticker is that this allows the dev-tool to “slow” our animation.
If we use “Slow animations”, then our clock is slowed by 50%. This is a good sign, as it means it will be a lot easier to test our clock!
Writing tests for our clock
It would be a shame to stop here.
Now that we’ve used Ticker for our animation, let’s showcase how Ticker also makes your animations testable.
For this, we will use “golden tests”, which consists of taking screenshots of our widget at specific times.
A naive approach would be:
But this causes two questions:
How do we make the animation progress?
We can’t reasonably use await Future.delayed and make our test wait for an hour.
Mocking the time
First, we will want to mock the behavior of DateTime.now inside our tests.
For this, we can use a class called Clock, from package:clock. It’s a package made by Google developers to help with time manipulation with testability in mind.
This can be combined with provider to provide an instance of Clock to our application, such that instead of:
From there, we need to add a provider in our main that creates a Clock:
Then, our tests can override this provider with a mocked Clock that returns a specific time.
Widget tests come with a built-in mocked clock, so we will use that:
That’s it for mocking time!
Our test at this stage should look like this:
Which generates the following initial_frame.png :
Simulating time changes
Now that we have mocked the initial frame of our test, what remains is testing the scenario where our clock updates.
This will typically look like this:
Which generates the following screenshot:
Thanks to Ticker, we now have both fixed performance issues with our clock and we were able to write UI tests for our animation.
For the full code of both the test and the widgets, you can see the following gist:
`vsync` property in TabController constructor
I created my own implementation of TabController:
In line: _tabController = new TabController(vsync: this, length: choices.length); I got error this message:
error: The argument type ‘_MyAppState’ can’t be assigned to the parameter type ‘TickerProvider’. (argument_type_not_assignable at [swap] lib/main.dart:24)
What is wrong with my code?
8 Answers 8
Add with TickerProviderStateMixin to the end of your State ’s class declaration.
Simply add with TickerProviderStateMixin at the end of extends state class as follows:
But what is Does TickerProviders really do?
Just change this line:
Here is the complete example of how to do this
Question is very generic, so need to describe more
Vsync used for
vsync is the property that represents the TickerProvider (i.e., Tick is similar to clock’s tick which means that at every certain duration TickerProvider will render the class state and redraw the object.)
vsync property is required only on that constructor which requires to render its class state at every certain off-set time when we need to render our components or widgets to redraw and reflect the UI.
vsync can be used with the classes which require certain transition or animation to re-render to draw different objects.
Internal Implementation
TabController uses AnimationController internally for the rendering of the tab bar state
Виды анимаций во Flutter и их реализация
Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.
Всем доброго времени суток, меня зовут Андрей, я являюсь Flutter-разработчиком. В прошлой статье, мы с вами рассмотрели создание объекта при помощи canvas и его анимирование. Поэтому в этот раз, я решил детальнее рассказать о различных видах анимации во flutter, их принципах работы и реализации.
Для начала, задавались ли вы когда-то вопросом, а зачем в целом нужна анимация в мобильном приложение? Это ведь не является, необходимой деталью в приложении, тогда зачем тратить на это время? Вряд-ли пользователь обратит внимание, на то как какой-то объект плавно исчезает. Однако дело в том что хоть пользователь может и не заметить плавное понижение прозрачности, но он точно обратит внимание, на то если это произойдет резко. Вы можете сами сравнить это на небольшом примере и сделать для себя выводы.
У хорошей анимации есть основные функции:
Сам flutter, в плане анимации и плавность очень хорош. Главными его преимуществами в плане анимации является, огромное количество заранее готовых виджетов, которые легко позволяют работать с анимацией и его работа с анимациями под «капотом».
Система анимации в flutter основана на типизированных анимационных объектах. Виджеты могут либо включать эти анимации в свои функции построения непосредственно, читая их текущее значение и прослушивая их изменения состояния, либо использовать анимации в качестве основы более сложной анимации, которую они передают другим виджетам.
Что ж давайте, перейдем от теории к практике и рассмотрим каждый из этих видов анимации более детально.
Как я и упоминал ранее, в flutter, огромное количество заранее готовых виджетов для создания анимация. На самом деле их настолько много, что для большинства задач связанных с анимацией их вполне хватает.
Так как рассмотреть каждый виджет мы не сможем, остановимся на нескольких самых часто используемых и самых интересных. Однако ознакомиться с каждым вы сможете на официальном сайте flutter в каталоге виджетов для анимации.
И так, для начала давайте детально разберем виджет AnimatedOpacity. Как и следует из названия, данный виджет отвечает за прозрачность объекта, который будет передан в параметр child. По сути является анимированным аналогом виджета Opactity. Почти все анимированные виджеты имеют обязательный параметр, duration, который отвечает за время перехода от изначального состояния до конечного. Интересным является то что если к примеру во время перехода из стартового состояния в конечное, которое длиться 4 секунды, прошла 1 секунда и мы опять изменим состояния на стартовое, то время обратной анимации будет занимать 1 секунду, а не 4.
Следующим важным параметром является curve. Это кривая, которая отвечает, за то как будут изменяться параметр, в нашем случае opactity, во время выполнения анимаций.
Используются для регулировки скорости изменения анимации с течением времени, позволяя им ускоряться и замедляться, а не двигаться с постоянной скоростью. Чтобы использовать кривую, вы можете выбрать одну из многих заранее готовых, которые находяться в классе Curves. По умолчанию используется Curves.linear, тоесть просто прямая линейная анимация. Ознакомиться с полным списком кривых вы можете на официальном сайте flutter.
Вторым вариантом, будет создание собственных curves, однако это очень редко используемый вариант, так-как, нам как правило хватает и стандартных кривых. Для создания собственных кривых мы должны создать новый класс на основе класса Curve.
Таким образом на примере виджета AnimatedOpacity, мы рассмотрели основные параметры для любой анимации, давайте менее детально рассмотрим еще несколько виджетов.
AnimatedPosition, как и AnimatedOpacity, является анимированной версией другого виджета, а именно Position. Выполняет анимацию, если изменилась позицию одной или нескольких сторон.
Следующим полезным виджетом, будет AnimatedCrossFade, с его помощью можно создать анимированный переход с понижающейся прозрачностью, между двумя виджетами. Он имеет несколько особых параметров, а именно firstChild и secondChild, что соответственно представляют из себя первый виджет и второй. Для их изменения мы должны изменять, параметр crossFadeState, указывая какой виджет мы должны отображать. Рассмотрим небольшой пример:
При нажатии выбранный виджет будет меняться в зависимости от переменной isChangeWidget.
AnimateContainer, очень простой для понимания виджет, представляет из себя аналог обычного виджета Container, с разницей в том, что почти каждый его параметр анимирован. В случае, если у вас есть Container, который имеет изменяемый параметр, к примеру, цвет или размер, рекомендую заменить его на AnimatedContainer, по логике, ничего не измениться, зато для пользователь все будет происходить плавнее.
Хоть и функционал, базовых виджетов для анимации, довольно обширен, он не безграничен и самая большая проблема, это когда анимация сильно усложняется и становиться комплексной, то есть недостаточно просто изменить прозрачность или размер, а необходимо, для примера, сначала понизить прозрачность, а после уменьшить размер.
Нет конечно можно решить эту проблема исспользуя несколько переменных и множество виджетов или же иные «костыли», однако наилучшим и наипростейшим решением будет использование AnimationController и виджет Animation. Однако для начала наш класс мы должны создать с помощью SingleTickerProviderStateMixin.
Он необходим для создания класса, в котором используется только один AnimationController. При инициализацию, которую необходимо делать в initState, в параметр vsync, мы передает текущий объект, this. Этот миксин поддерживает только один тикер. Если за время существования состояния у вас может быть несколько объектов AnimationController, используйте вместо этого TickerProviderStateMixin.
В flutter анимационный объект ничего не знает о том, что находится на экране. Анимация — это абстрактный класс, который понимает своё текущее значение и свое состояние. Одним из наиболее часто используемых типов анимации является Animation.
Он позволяет переходить с течением времени плавно переходить от одного значение в другое, давайте для примера попробуем создать контейнер, который будет изменять размер и прозрачность при нажатии.
И так из важного следует сказать, что после инициализации контроллера, мы создаем слушателя:
Это необходимо, для того чтобы обновлять состояния, по ходу выполнения анимации. Для запуска анимации, при нажатии на кнопку вызывается метод forward:
Это позволяет запустить анимацию, для запуска в обратную сторону, используется метод reverse:
По умолчанию объект AnimationController находится в диапазоне от 0.0 до 1.0. Если Вам нужен другой диапазон или другой тип данных, Вы можете использовать Tween для настройки анимации для интерполяции на другой диапазон или другой тип данных.
Например, следующий Tween переходит от −200.0 к 0.0:
На самом деле, эта деталь очень существенна, так как благодаря, tween, можно имеет более расширенный функционал, к примеру с его помощью намного легче можно анимировано изменять цвет. Давайте для примера попробуем изменить цвет контейнера.
Сначала мы создали контроллер и анимацию, в методе initState, мы инициализируем контролер. Далее на основе ColorTween, которому мы задали изначальное значение синий, а конечный результат красный, мы инициализируем animate и связываем его с контроллером. Далее просто создаем слушателя, который будет обновлять состояния, каждый раз при изменение состояния.
Потом просто при нажатии на контейнер, вызываем у _animationConroller метод repeat, который постоянно выполнять анимацию, каждый раз повторяя ее.
Давайте под конец попробуем используя несколько контролеров, изменить размер виджета, и его цвет. При этом изменение размера, должно произойти после изменения цвета. Для этого нам потребуется использовать TickerProviderStateMixin, а не SIngleTickerProviderStateMixin, как ранее. Также нам придется использовать новый вид слушателя, а именно addStatusListener, который будет вызываться только при изменение состояние, нас интересует чтобы при конце первой анимации, был вызван второй контроллер.
Думаю на этом, можно остановиться, в этот раз мы узнали различные способы создания анимации, рассмотрели различные примеры и виджеты, которые могут нам в этом помочь. Оставляйте комментарии, что вам понравилось, а что не очень или чтобы хотели от себя добавить. Возможно у вас имеются вопросы, буду рад на них ответить.