Хотите создать настраиваемый пользовательский интерфейс с потрясающей анимацией? Не бойтесь, Flutter CustomPaint Widget поможет вам

В этой статье мы рассмотрим как явную анимацию, так и то, как работать с виджетом CustomPaint во флаттере.

Вы можете ознакомиться с образцом кода в моем GitHub Repository.

Пользовательская краска: виджет, предоставляющий холст для рисования на этапе рисования.

Просто добавьте виджет CustomPaint в дерево виджетов и предоставьте ему painter, который является подклассом абстрактного класса CustomPainter.

Поскольку CustomPainter является абстрактным классом, это заставляет нас реализовать два важных метода, то есть paint() и shouldRepaint(). sizeзадает размер нарисованного виджета. Запачкаем руки:

Метод paint() предоставляет нам два важных параметра:

  1. Canvas: Холст — это область экрана, где мы рисуем наш виджет. Он имеет различные методы рисования пользовательского виджета. Вот некоторые из них: canvas.drawLine(), canvas.drawCircle(), canvas.drawArc(), canvas.drawOval() и т. д.
  2. Size: Насколько большим должен быть рисунок. По умолчанию он принимает размер виджета-оболочки, то есть виджета, обертывающего наш виджет CustomPaint. Кроме того, мы можем предоставить параметры размера внутри нашего виджета CustomPaint. Поскольку мы хотим нарисовать круг, мы передали одно и то же значение ширины и высоты.

Метод shouldRepaint() вызывается всякий раз, когда нужно перестроить CustomPainter. Это станет ясно, когда мы реализуем наш проект.

Теперь, когда мы разобрались с классом CustomPainter, давайте нарисуем что-нибудь на холсте. Наша цель — нарисовать круг с некоторой шириной штриха.

Давайте посмотрим, как мы это делаем:

Поскольку мы хотим нарисовать круг, мы использовали предварительно созданный метод canvas.drawCircle() the , который ожидает три параметра, то есть центр круга, радиус круга и объект рисования. Мы рассчитали центр и радиус круга по sizeпараметру. Класс Paint() предоставляет нам различные свойства, такие как цвет paint, storkeWidth, style краски, т. е. должен ли он быть обведен или заполнен. Текущая реализация приведет к следующему:

Наша следующая цель — нарисовать дугу над тем же кругом, который будет анимироваться вокруг обводки. Для этого:

Поскольку мы хотим нарисовать дугу, мы использовали метод canvas.drawArc(), который ожидает пять параметров, то есть Rect object, startAngle в радианах, sweepAngle в radian, useCenter логическое значение и объект рисования.

Кроме того, мы хотим нарисовать дугу вокруг круга, поэтому мы использовали Rect.fromCircle() метод в качестве первого параметра. Второй параметр — это начальный угол, который мы применили к -pi/2, так как мы хотим, чтобы угол начинался с вершины круга.

Угол развертки указывает, где должна заканчиваться дуга, и мы применили 40% от общего угла, т.е. наша дуга будет рисовать от -pi/2 до 40% от общих 360 радиан.

Если useCenterboolean равно true, дуга замыкается обратно к центру, образуя круговой сектор. В противном случае дуга не замыкается, образуя сегмент окружности. И последний аргумент — это наш объект рисования, с которым мы уже знакомы.

Текущая реализация с useCenter, установленным в true, приведет к следующему:

Но нам не нужен этот круговой сектор, поэтому установка для него значения false приведет к следующему:

Вы можете настроить startAngle, endAngle, чтобы увидеть различные эффекты.

Теперь мы почти приблизились к тому, чего хотим достичь. Осталась только анимация прогресса. Чтобы сделать это, мы должны использовать явную анимацию, то есть мы должны создать AnimationController и передать его значение классу RingPainter:

Различные вещи, которые мы сделали сейчас:

  1. Преобразовали наш виджет RingPainter StatelessWidget в виджет с отслеживанием состояния, чтобы перестраивать пользовательский интерфейс всякий раз, когда изменяется значение анимации.
  2. Создал AnimationController _controller, смешав наш класс _RingPainterState с SingleTickerProviderStateMixin и инициализировав его в методе initState().
  3. Начал анимацию с _controller.forward()
  4. Добавлен слушатель к контроллеру, и вызывается setState((){}), так что весь наш пользовательский интерфейс перестраивается всякий раз, когда значение нашего _controller изменяется.
  5. Теперь наш класс MyPainter ожидает три обязательных аргумента: progress, defaultColor обводки круга и цвета заливки. Это сделано для того, чтобы передавать различные значения из-за пределов этого класса и сделать этот компонент многоразовым. Обратите внимание, что метод shouldRepaint() обновлен таким образом, что наш Circle следует рисовать только тогда, когда значение прогресса отличается от предыдущего значения.
  6. Наконец, передал эти три аргумента классу MyPainter со значением прогресса, являющимся текущим значением анимации.

С текущей реализацией нам удалось добиться этого:

Ура, мы почти до конца! Осталось только добавить значок в середине круга и изменить его цвет в зависимости от того, завершена анимация или нет.

Чтобы добиться этого, обновите метод сборки следующим образом:

Следующая реализация приведет к этому:

Так что теперь успешно добились желаемого.

Надеюсь, вы узнали что-то о виджете CustomPaint. Теперь вы можете исследовать самостоятельно, чтобы добиться различных других эффектов.

Спасибо за прочтение.