В качестве примера того, как построить FRP (функционально-реактивная программа [s]) в Dart, я решил попробовать построить счетчик FPS - то есть утилиту для определения частоты кадров запущенное приложение.

Я решил, что хочу иметь несколько целей:

  1. Библиотека, в данном случае package: fps, не должна зависеть от платформы. То есть я не хотел требовать использования браузера (requestAnimationFrame) - он должен работать также в автономной виртуальной машине командной строки и Flutter.
  2. Я хотел, чтобы он был расширяемым - получение среднего FPS за определенный период времени должно быть относительно тривиальным, чтобы число не менялось взад и вперед во время бега.
  3. Я хотел иметь возможность указать целевой FPS (скажем, 60) и иметь возможность динамически изменять это целевое значение, если ясно, что приложение не сможет работать.

Вот как я хотел, чтобы мой API выглядел:

Определение количества кадров в секунду означает, что я хочу периодически проверять в своем приложении, сколько времени прошло с тех пор, как я последний раз мог проверить. Предположим следующее:

  • Рендеринг обычно измеряется в секундах, или сколько кадров умещается
  • 1000 миллисекунд (мс) - одна секунда
  • Время, необходимое для рендеринга кадра, - это разница между двумя отметками времени.
  • Мы можем начать с отправки метки времени каждый «кадр».

В Dart, который является однопоточным и основан на цикле событий, мы предполагаем, что если мы сможем получить контроль над циклом событий хотя бы раз в ‹duration› (время простоя), то вся предыдущая работа фактически была фреймом.

Я попытался реализовать это довольно наивно - я создал новый StreamController и использовал Future.delayed (, который делегирует Timer на всех платформах) для сообщить, когда следующий кадр:

Примечание. Это не всегда верно - в Интернете мы можем сделать это намного лучше с помощью window.animationFrame, но опять же, я хотел что-то, что работало бы на разных платформах вне коробка.

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

Теперь я получаю что-то вроде этого:

60
60
60
60
60
60
60
60
60

Выглядит неплохо! Я разместил исходный код и пример репозитория на github.