Flutter’da CustomPaint Widget ve Curve

Javascript üzerinde canvas kullanarak pek çok yaratıcı işler yapılıyor. Flutter’da da benzer karmaşık animasyonları ve çizimleri yapabileceğimiz sınıf ve metotlarımız var.

Aynı isimli Canvas sınıfı Flutter’da da var fakat bunu direkt olarak kullanmayacağız bunun yerine CustomWidget widget’ını kullanacağız.

CustomPaint’in 5 parametresi var, aşağıda açıklamaya çalıştım.

const CustomPaint({
  Key key,
  this.painter, // CustomPainter nesnesi
  this.foregroundPainter, // CustomPainter nesnesi, child widgetından sonra çizmeye başlar
  this.size = Size.zero, // child widget'ı yoksa boyut girebiliriz
  this.isComplex = false, // true ise sürekli çizim yapmaz, önbellek kullanır
  this.willChange = false, // tekrar çizim olması için true olmalı
  Widget child, // child widget
}) : assert(size != null),
     assert(isComplex != null),
     assert(willChange != null),
     super(key: key, child: child);

Buradaki painter parametresi için kullanacağımız CustomPainter’a genişletilmiş bir sınıfımız olmalı.

class myCustomPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // ana painter metodu, tüm çizim burada yapılır
  }

  @override
  bool shouldRepaint(myCustomPainter oldDelegate) => false;
  // önceki frame verisiyle kontrol ederek 

Bir şeyler çizelim.

import 'package:flutter/material.dart';

class CustomPainterPractice extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Material(
      child: Padding(
        padding: const EdgeInsets.all(30.0),
        child: CustomPaint(
          painter: BobRoss(),
        ),
      ),
    );
  }
}

class BobRoss extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint rectPaint = Paint();
    rectPaint.style = PaintingStyle.fill;
    rectPaint.color = Colors.greenAccent;

    canvas.drawRect(
      Rect.fromLTWH(0.0, 0.0, size.width, 50),
      rectPaint
    );
  }

  @override
  bool shouldRepaint(BobRoss oldDelegate) => false;
}

Tam genişlikte ve 50 birim yükseklikte yeşil bir dikdörtgen çizdik. Material ve Padding widgetları ile sardığımız CustomWidget’a painter olarak oluşturduğumuz BobRoss widget’ını verdik. BobRoss widget’ımızın paint metodu bir canvas parametresi alıyor ve bu canvas nesnesine ait onlarca çizim metodu var.

Burada cascade notation denen bir çok faydalı bir syntax var. Kodumuzu daha derli toplu hale getirebiliriz bu yazım ile. Art arda iki nokta kullanılan bu yazımda aynı nesneye ait pek çok metodu ardışık olarak çağırabiliriz.

void paint(Canvas canvas, Size size) {
  canvas.drawRect(
    Rect.fromLTWH(0.0, 0.0, 50, 50),
    Paint()
      ..style = PaintingStyle.fill
      ..color = Colors.greenAccent,
  );
}

Şimdi canvası hareketlendirmeye geldi. shouldRepaint metodu true dönmeli öncelikle. Sonra gerekli verileri painter sınıfına ileterek çizime hareket katabiliriz. Verilerden kastım çizdiğiniz objenin koordinatları rengi vs. ya da zaman çizelgesinde hangi anın çizilmesini istediğiniz olabilir. Aşağıda tween ve curve kullanarak hareketli bir çizim oluşturmaya çalıştım.

import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';

class CustomPainterPractice extends StatefulWidget {
  @override
  _CustomPainterPracticeState createState() => _CustomPainterPracticeState();
}

class _CustomPainterPracticeState extends State<CustomPainterPractice> with SingleTickerProviderStateMixin{
  Animation<double> animation;
  AnimationController controller;

  double drawTime = 0.0;
  double drawDuration = 2.0;

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

    controller =
        AnimationController(vsync: this, duration: Duration(seconds: drawDuration.toInt()));
    
    animation = Tween<double>(begin: 0.001, end: drawDuration).animate(controller)
      ..addListener(() {
        setState(() {
          drawTime = animation.value;
        });
      });


    controller.forward();
  }

  @override
  Widget build(BuildContext context) {

    return Material(
      child: Padding(
        padding: const EdgeInsets.all(30.0),
        child: CustomPaint(
          painter: BobRoss(drawTime, drawDuration),
        ),
      ),
    );
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

class BobRoss extends CustomPainter {
  final double drawTime;
  final double drawDuration;

  BobRoss(this.drawTime, this.drawDuration);

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(
      Rect.fromLTWH(0.0, 0.0, size.width * (drawTime/drawDuration), 50),
      Paint()
        ..style = PaintingStyle.fill
        ..color = Colors.greenAccent,
    );
  }

  @override
  bool shouldRepaint(BobRoss oldDelegate) => true;
}

Önce StatefulWidget’a çevirdik widgetımızı çünkü bir state’imiz var artık. initState metodunda gerekli tanımlamaları yapıyoruz, bunları build metodunda yapamayız çinkü her setState çağrıldığında build metodu tekrar çalışacağından işler karışacaktır. İki saniyelik bir tween oluşturduk. drawTime / drawDuration değeri bize zaman çizelgesinde nerede olduğumuz verecek ve CustomPainter bu değer ile hesaplamasını yapıp çizimini yapacak.

Düz bir şekilde büyüyen animasyonumuz oldu. Curve ekleyerek animasyonu monotonluktan çıkarabiliriz. Aşağıdaki gif’in kodlarını şu gistte görebilirsiniz.

 

Medium hesabımdaki şu yazımın çevirisidir.


Yayımlandı

kategorisi

yazarı:

Yorumlar

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir