Flutter Mobile Development · Lekcja

API Canvas: ścieżki, pędzle i shadery

Rysuj kształty, gradienty i obrysy za pomocą prymitywów Canvas oraz konfiguracji Paint

Lekcja 1 z 413 kroki

API Canvas: ścieżki, pędzle i shadery to bezpłatna lekcja Flutter Mobile Development na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Flutter Mobile Development, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Flutter Mobile Development zawiera 4 lekcji w sumie.

Poznajemy Canvas

Podczas tworzenia CustomPainter Flutter przekazuje Państwu w paint() dwie rzeczy: Canvas i Size.

  • Canvas to obszar rysowania. Udostępnia takie prymitywy jak drawRect, drawCircle, drawPath i drawLine.
  • Size określa rozmiar obszaru, dzięki czemu można skalować rysunek tak, aby pasował do dowolnego urządzenia.

Każde wywołanie rysowania wymaga obiektu Paint, który opisuje sposób rysowania: kolor, obrys lub wypełnienie, szerokość i inne właściwości.

void paint(Canvas canvas, Size size) {
  final paint = Paint()..color = const Color(0xFF2196F3);
  canvas.drawRect(
    Rect.fromLTWH(0, 0, size.width, size.height),
    paint,
  );
}

Obiekt Paint

Paint to zestaw opcji stylu. Najważniejsze pola to:

  • color — kolor używany do rysowania.
  • style — PaintingStyle.fill (wypełnienie) lub PaintingStyle.stroke (tylko obrys).
  • strokeWidth — grubość linii podczas rysowania obrysu.
  • isAntiAlias — wygładzanie krawędzi, domyślnie włączone.

W kodzie Fluttera często używa się operatora kaskadowego .., aby skonfigurować nowo utworzony Paint w jednym wyrażeniu.

final fill = Paint()
  ..color = const Color(0xFFE91E63)
  ..style = PaintingStyle.fill;

final outline = Paint()
  ..color = const Color(0xFF000000)
  ..style = PaintingStyle.stroke
  ..strokeWidth = 4.0;

Rysowanie podstawowych kształtów

Canvas udostępnia gotowe prymitywy, więc nie zawsze trzeba używać ścieżki:

  • drawRect(Rect, Paint) — prostokąt o bokach równoległych do osi.
  • drawRRect(RRect, Paint) — prostokąt z zaokrąglonymi rogami.
  • drawCircle(Offset center, double radius, Paint)
  • drawOval(Rect, Paint) — wypełnia prostokąt ograniczający elipsą.

Współrzędne zaczynają się od (0,0) w lewym górnym rogu; x rośnie w prawo, a y w dół.

void paint(Canvas canvas, Size size) {
  final p = Paint()..color = const Color(0xFF4CAF50);
  final center = Offset(size.width / 2, size.height / 2);

  canvas.drawCircle(center, 40, p);
  canvas.drawRRect(
    RRect.fromRectAndRadius(
      Rect.fromLTWH(10, 10, 80, 50),
      const Radius.circular(12),
    ),
    p,
  );
}

Obrysy: zakończenia, połączenia i szerokość

Gdy style ma wartość PaintingStyle.stroke, kilka właściwości wpływa na wygląd linii:

  • strokeWidth — grubość w pikselach logicznych.
  • strokeCap — wygląd zakończeń linii: butt, round lub square.
  • strokeJoin — sposób łączenia narożników: miter, round lub bevel.

Zaokrąglone zakończenia i połączenia są często używane do tworzenia przyjaznych, gładkich wykresów i podpisów.

final stroke = Paint()
  ..color = const Color(0xFF9C27B0)
  ..style = PaintingStyle.stroke
  ..strokeWidth = 6.0
  ..strokeCap = StrokeCap.round
  ..strokeJoin = StrokeJoin.round;

// canvas.drawLine(Offset(10, 10), Offset(120, 60), stroke);

Tworzenie ścieżki

Path opisuje dowolny obrys złożony z odcinków prostych i krzywych. Najważniejsze metody to:

  • moveTo(x, y) — podnosi pióro i rozpoczyna nową podścieżkę.
  • lineTo(x, y) — rysuje odcinek prosty.
  • quadraticBezierTo / cubicTo — odcinki krzywe.
  • close() — łączy z powrotem z początkiem podścieżki.

Gotową ścieżkę należy narysować za pomocą canvas.drawPath(path, paint).

void paint(Canvas canvas, Size size) {
  final path = Path()
    ..moveTo(size.width / 2, 0)
    ..lineTo(size.width, size.height)
    ..lineTo(0, size.height)
    ..close();

  final paint = Paint()..color = const Color(0xFFFF5722);
  canvas.drawPath(path, paint);
}

Krzywe z odcinkami Beziera

Gładkie kształty tworzy się za pomocą krzywych Beziera. Najczęściej używana jest kwadratowa krzywa Beziera, która korzysta z jednego punktu kontrolnego.

  • quadraticBezierTo(cpx, cpy, x, y) zakrzywia linię w stronę punktu kontrolnego (cpx, cpy) na drodze do punktu końcowego (x, y).
  • cubicTo(c1x, c1y, c2x, c2y, x, y) korzysta z dwóch punktów kontrolnych do tworzenia krzywych w kształcie litery S.

Służą one do tworzenia falistych separatorów, linii wykresów i ogonków dymków dialogowych.

void paint(Canvas canvas, Size size) {
  final wave = Path()
    ..moveTo(0, size.height * 0.5)
    ..quadraticBezierTo(
      size.width * 0.25, size.height * 0.2,
      size.width * 0.5, size.height * 0.5,
    )
    ..quadraticBezierTo(
      size.width * 0.75, size.height * 0.8,
      size.width, size.height * 0.5,
    );

  final paint = Paint()
    ..color = const Color(0xFF00BCD4)
    ..style = PaintingStyle.stroke
    ..strokeWidth = 3;
  canvas.drawPath(wave, paint);
}

Czysty Dart: obliczanie punktów

Obliczenia dla Canvas to po prostu geometria. Przed rysowaniem często oblicza się współrzędne za pomocą czystego języka Dart. W tym przykładzie generujemy równomiernie rozmieszczone punkty na fali sinusoidalnej — te same wartości, które można przekazać do path.lineTo.

Ta logika nie zależy od frameworka, dlatego można ją testować jednostkowo bez renderowania czegokolwiek.

import 'dart:math';

List<Point<double>> wavePoints(double width, double height, int n) {
  final pts = <Point<double>>[];
  for (var i = 0; i <= n; i++) {
    final x = width * i / n;
    final y = height / 2 + sin(i / n * 2 * pi) * height / 4;
    pts.add(Point(x, y));
  }
  return pts;
}

void main() {
  final pts = wavePoints(200, 100, 4);
  for (final p in pts) {
    print('(${p.x.toStringAsFixed(1)}, ${p.y.toStringAsFixed(1)})');
  }
}

Shadery: gradienty liniowe

Shader wypełnia kształt czymś więcej niż jednolitym kolorem. Najczęściej używa się gradientu liniowego. Tworzy się go za pomocą ui.Gradient.linear i przypisuje do paint.shader.

  • from / to to początkowy i końcowy Offset linii gradientu.
  • colors zawiera punkty gradientu, które są mieszane wzdłuż tej linii.

Po ustawieniu shader kolor color obiektu paint jest ignorowany przy wypełnianiu.

import 'dart:ui' as ui;

Paint gradientPaint(Size size) {
  return Paint()
    ..shader = ui.Gradient.linear(
      Offset.zero,
      Offset(size.width, size.height),
      const [Color(0xFF2196F3), Color(0xFF9C27B0)],
    );
}

Gradienty radialne i Sweep

Oprócz gradientu liniowego Flutter oferuje jeszcze dwa shadery gradientowe:

  • Radial — ui.Gradient.radial(center, radius, colors) rozprowadza kolory od punktu centralnego na zewnątrz; świetnie nadaje się do poświat i reflektorów.
  • Sweep — ui.Gradient.sweep(center, colors) obraca kolory wokół środka; idealnie nadaje się do okrągłych wskaźników postępu i kół kolorów.

Można dodać opcjonalną listę stops (od 0.0 do 1.0), aby dokładnie określić położenie każdego koloru.

import 'dart:ui' as ui;

Paint glow(Offset center) {
  return Paint()
    ..shader = ui.Gradient.radial(
      center,
      60,
      const [Color(0xFFFFEB3B), Color(0x00FFEB3B)],
      const [0.0, 1.0],
    );
}

Łączenie elementów w painterze

Rzeczywisty CustomPainter łączy ścieżki, obiekty paint i shadery. Należy pamiętać o dwóch metodach przesłoniętych:

  • paint(canvas, size) — wykonuje rysowanie.
  • shouldRepaint(old) — zwraca true tylko wtedy, gdy zmieniły się dane wejściowe, dzięki czemu Flutter unika niepotrzebnego ponownego rysowania.

Ten fragment rysuje kartę z zaokrąglonymi rogami, wypełnioną gradientem i obramowaną obrysem.

class CardPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final rect = Rect.fromLTWH(0, 0, size.width, size.height);
    final rrect = RRect.fromRectAndRadius(rect, const Radius.circular(16));

    final fill = Paint()
      ..shader = ui.Gradient.linear(
        rect.topLeft, rect.bottomRight,
        const [Color(0xFF42A5F5), Color(0xFF7E57C2)],
      );
    canvas.drawRRect(rrect, fill);

    final border = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2
      ..color = const Color(0x33000000);
    canvas.drawRRect(rrect, border);
  }

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

Zapisywanie, przywracanie i transformacje

Canvas przechowuje stos transformacji i przycięć. Można go używać do rysowania obróconej lub przeskalowanej zawartości bez trwałej zmiany stanu:

  • canvas.save() odkłada bieżącą macierz na stos.
  • canvas.translate / rotate / scale modyfikują tę macierz.
  • canvas.restore() przywraca poprzedni stan.

Zawsze należy parować każde save() z restore(), w przeciwnym razie kolejne rysunki odziedziczą transformację.

void paint(Canvas canvas, Size size) {
  final p = Paint()..color = const Color(0xFF3F51B5);
  canvas.save();
  canvas.translate(size.width / 2, size.height / 2);
  canvas.rotate(0.785398); // 45 degrees in radians
  canvas.drawRect(
    const Rect.fromLTWH(-30, -30, 60, 60),
    p,
  );
  canvas.restore();
}

Szybkie sprawdzenie

Proszę sprawdzić zrozumienie konfiguracji obiektu Paint.

Podsumowanie

Znają już Państwo API Canvas:

  • Paint steruje sposobem rysowania: color, style (wypełnienie lub obrys), strokeWidth, zakończenia i połączenia.
  • Prymitywy, takie jak drawRect, drawRRect i drawCircle, obsługują typowe kształty; Path z metodami moveTo, lineTo i krzywymi Beziera pozwala tworzyć dowolne kształty niestandardowe.
  • Shadery (Gradient.linear, radial, sweep) wypełniają lub obrysowują kształty gradientami i zastępują jednolity kolor.
  • save/restore izoluje transformacje, a shouldRepaint zapewnia wydajne renderowanie.

Dzięki tym elementom można tworzyć wykresy, niestandardowe kontrolki i bogate dekoracje wyłącznie w języku Dart.

Bezpłatny start

Ucz się Dart dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
22
Lekcje
88

Często zadawane pytania

Czy lekcja „API Canvas: ścieżki, pędzle i shadery” jest bezpłatna?

Tak — pełny tekst „API Canvas: ścieżki, pędzle i shadery” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Flutter Mobile Development, przejdź na CoddyKit PRO. Kurs Flutter Mobile Development zawiera 4 lekcji w sumie.

Co nauczysz się w „API Canvas: ścieżki, pędzle i shadery”?

Rysuj kształty, gradienty i obrysy za pomocą prymitywów Canvas oraz konfiguracji Paint Ćwiczysz Flutter Mobile Development z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Flutter Mobile Development?

Nie wymagamy żadnego doświadczenia. Flutter Mobile Development w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „API Canvas: ścieżki, pędzle i shadery”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Flutter Mobile Development?

Tak. Każda lekcja Flutter Mobile Development zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. API Canvas: ścieżki, pędzle i shadery
  2. Tworzenie interaktywnego niestandardowego widgetu wykresu
  3. Przycinanie, tryby mieszania i komponowanie warstw
  4. Testowanie trafień i optymalizacja przemalowywania
← Powrót do Flutter Mobile Development