ShaderMaskLayer: Phù phép UI Flutter với Hiệu Ứng Mặt Nạ Đỉnh Cao
Flutter

ShaderMaskLayer: Phù phép UI Flutter với Hiệu Ứng Mặt Nạ Đỉnh Cao

Author

Admin System

@root

Ngày xuất bản

21 Mar, 2026

Lượt xem

2 Lượt

"ShaderMaskLayer"

Hôm nay, anh Creyt sẽ 'bóc tách' cho tụi em một cái 'magic trick' cực đỉnh trong Flutter, giúp UI của tụi em từ 'bình thường' hóa 'phi thường' chỉ trong một nốt nhạc: đó chính là ShaderMask (và đằng sau nó là ShaderMaskLayer).

Tưởng tượng thế này: em có một bức tranh (child widget), và em muốn 'che' một phần của nó đi, hoặc tô màu cho nó theo một kiểu 'gradient' siêu ngầu, hoặc thậm chí là dùng một bức ảnh khác làm 'khuôn' để cắt cái bức tranh gốc. ShaderMask chính là cái 'khuôn thần kỳ' đó!

Nó không chỉ đơn thuần là cắt hình vuông, hình tròn đâu nha. Cái 'khuôn' này có thể là một dải màu chuyển sắc (gradient), một tấm ảnh mờ ảo, hay thậm chí là một hiệu ứng 'glitch' do em tự code ra. Về cơ bản, nó dùng một Shader (bộ tô màu) để làm mặt nạ. Chỗ nào cái Shader này 'tô' màu rõ, thì cái widget con của em sẽ hiện ra. Chỗ nào nó 'tô' trong suốt, thì widget con sẽ biến mất. Đơn giản là vậy!

ShaderMaskLayer? À, đó là 'công nhân' cần mẫn phía sau hậu trường, là cái 'bàn vẽ' mà Flutter dùng để thực hiện tất cả các phép màu về mặt nạ này. Tụi em dùng ShaderMask trên bề mặt, còn ShaderMaskLayer là cái 'công cụ' mà Flutter gọi ra để vẽ vời, xử lý pixel các kiểu con đà điểu.

Code Ví Dụ: Chữ Gradient Siêu Ngầu

Nói nhiều lý thuyết khô khan quá đúng không? Thôi, mình 'nhảy' thẳng vào code để thấy nó 'cool' cỡ nào nè. Ví dụ kinh điển nhất, và cũng là cái tụi em hay thấy trên mấy cái app 'xịn xò' là: Chữ chuyển màu gradient.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Creyt\'s ShaderMask Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const GradientTextScreen(),
    );
  }
}

class GradientTextScreen extends StatelessWidget {
  const GradientTextScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ShaderMask: Chữ Gradient Siêu Ngầu'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Đây là màn trình diễn của ShaderMask
            ShaderMask(
              // Cái này là 'bộ lọc màu' hay 'khuôn' của mình nè
              shaderCallback: (bounds) {
                return const LinearGradient(
                  colors: [Colors.purple, Colors.pink, Colors.red], // Dải màu chuyển sắc
                  begin: Alignment.topLeft, // Bắt đầu từ góc trên bên trái
                  end: Alignment.bottomRight, // Kết thúc ở góc dưới bên phải
                ).createShader(bounds); // Tạo shader từ dải màu đó
              },
              blendMode: BlendMode.srcIn, // Cách mà shader hòa trộn với widget con
              // Đây là 'bức tranh' mà mình muốn áp dụng mặt nạ
              child: const Text(
                'Creyt\'s Code Vibes',
                style: TextStyle(
                  fontSize: 48,
                  fontWeight: FontWeight.bold,
                  // Màu ở đây không quan trọng lắm vì sẽ bị ShaderMask thay thế
                  color: Colors.white, // Mặc định là trắng, nhưng shader sẽ override
                ),
              ),
            ),
            const SizedBox(height: 30),
            ShaderMask(
              shaderCallback: (bounds) {
                return const RadialGradient(
                  colors: [Colors.yellow, Colors.orange, Colors.red],
                  center: Alignment.center,
                  radius: 0.8,
                ).createShader(bounds);
              },
              blendMode: BlendMode.srcIn,
              child: const Icon(
                Icons.star,
                size: 100,
                color: Colors.white, // Cũng sẽ bị override
              ),
            ),
            const SizedBox(height: 30),
            // Thử với một Image làm mask (hoặc áp dụng mask lên Image)
            ShaderMask(
              shaderCallback: (bounds) {
                // Tưởng tượng bạn có một hình ảnh đen trắng,
                // phần màu trắng sẽ cho phép child hiện ra,
                // phần màu đen sẽ che đi. Ở đây mình dùng gradient giả lập.
                return const LinearGradient(
                  colors: [Colors.transparent, Colors.black, Colors.transparent],
                  stops: [0.0, 0.5, 1.0],
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                ).createShader(bounds);
              },
              blendMode: BlendMode.dstIn, // DstIn: hiển thị nơi cả mask và child đều có pixel
              child: Image.network(
                'https://picsum.photos/200', // Một hình ảnh bất kỳ
                width: 200,
                height: 200,
                fit: BoxFit.cover,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Trong ví dụ này, anh dùng LinearGradient để tạo ra một dải màu chuyển sắc từ tím, hồng đến đỏ. Cái dải màu này chính là Shader của chúng ta, và nó được dùng làm 'mặt nạ' cho widget Text con. Kết quả là, chữ 'Creyt's Code Vibes' sẽ được tô màu gradient siêu ngầu!

Illustration

Mẹo Vặt Từ Lão Làng Creyt (Best Practices)

Mấy đứa nghe kỹ đây, đây là mấy cái 'mẹo vặt' từ lão làng Creyt mà tụi em nên 'bỏ túi' để dùng ShaderMask cho 'chuẩn bài' nè:

  • Hiểu BlendMode: Cái thuộc tính blendMode trong ShaderMask quan trọng lắm nha. Nó quyết định cách Shader (mask) và child (nội dung) hòa trộn với nhau.
    • BlendMode.srcIn: Thường dùng nhất. Nó sẽ chỉ hiển thị phần child nằm trong vùng 'có màu' của shader. Như ví dụ chữ gradient ấy.
    • BlendMode.dstIn: Hiển thị phần child nơi cả shaderchild đều có pixel. Thường dùng khi shader là một hình ảnh 'texture' để tạo hiệu ứng 'cắt gọt' cho child.
    • Cứ thử nghiệm mấy cái blendMode khác nhau để xem hiệu ứng nào 'hợp gu' nhất.
  • Performance (Hiệu suất): ShaderMask khá 'ngốn' tài nguyên, đặc biệt nếu Shader của em phức tạp (ví dụ, dùng ImageShader với ảnh lớn, hoặc custom shader phức tạp). Nên dùng có chọn lọc, đừng lạm dụng quá mức nếu không cần thiết.
  • Kết hợp với các Widget khác: ShaderMask thường đi kèm với các widget khác như ClipRRect để tạo ra những hiệu ứng mặt nạ trên các hình dạng đặc biệt, hoặc AnimatedBuilder để tạo hiệu ứng động cho Shader.
  • Thử nghiệm với các loại Gradient: Đừng chỉ dừng lại ở LinearGradient. Hãy thử RadialGradient (chuyển màu từ tâm ra) hay SweepGradient (chuyển màu xoay tròn) để tạo ra các hiệu ứng độc đáo hơn.
  • Custom Shader (Level Up): Nếu muốn 'đỉnh của chóp', em có thể tự viết CustomShader bằng ngôn ngữ GLSL rồi nhúng vào Flutter. Cái này thì hơi 'khoai' một chút nhưng kết quả thì 'ảo diệu' khỏi bàn! (Cái này thì để dành cho buổi học khác nha, hôm nay mình 'nhẹ nhàng' thôi).

Ứng Dụng Thực Tế: Ai Đã Dùng?

Tụi em có biết mấy cái app 'hot hit' mà tụi em dùng hàng ngày đã ứng dụng cái 'chiêu' này như thế nào không?

  • Spotify: Thường xuyên sử dụng gradient cho các tiêu đề bài hát, tên nghệ sĩ, hoặc các nút bấm để tạo cảm giác hiện đại, 'chill' và thu hút thị giác. ShaderMask là một trong những công cụ để họ làm điều đó.
  • Instagram/TikTok: Mặc dù không phải ShaderMask trực tiếp, nhưng concept 'filter' ảnh/video mà tụi em dùng hàng ngày chính là ứng dụng của Shader (bộ tô màu). Tưởng tượng ShaderMask là một 'filter' cho các widget UI của em.
  • Các ứng dụng ngân hàng/tài chính: Đôi khi họ dùng gradient để làm nổi bật số dư, các chỉ số quan trọng, tạo cảm giác 'sang chảnh' và đáng tin cậy.
  • Game UI: Các thanh máu, thanh mana trong game thường có hiệu ứng gradient hoặc texture fill. Khi thanh máu giảm, phần gradient cũng có thể thay đổi để tạo hiệu ứng thị giác mạnh mẽ hơn. ShaderMask có thể giúp tạo ra những hiệu ứng này một cách linh hoạt.

Khi Nào Nên Dùng và Tránh Dùng?

Anh Creyt đã 'chinh chiến' với ShaderMask này không ít lần rồi, và đây là vài lời khuyên 'xương máu' từ kinh nghiệm thực tế:

  • Nên dùng khi nào?
    • Tạo điểm nhấn thương hiệu (Branding): Khi muốn logo, tiêu đề, hoặc các yếu tố quan trọng của app có một dải màu gradient đặc trưng, 'không đụng hàng'.
    • Hiệu ứng thị giác 'sang chảnh': Các button, card, hoặc text cần một vẻ ngoài cao cấp, hiện đại, thu hút ánh nhìn.
    • UI động (Dynamic UI): Khi em muốn hiệu ứng chuyển màu thay đổi theo trạng thái (ví dụ, thanh tiến trình, thanh máu thay đổi màu khi gần hết).
    • Masking ảnh/widget với hình dạng phức tạp: Mặc dù ClipRRect hay ClipPath cũng làm được, nhưng ShaderMask cho phép em dùng một ImageShader để tạo mặt nạ dựa trên độ trong suốt của một bức ảnh khác, mở ra nhiều khả năng sáng tạo hơn.
  • Khi nào thì 'tạm dừng' và suy nghĩ lại?
    • Khi chỉ cần một màu solid: Đừng 'lấy dao mổ trâu giết gà' khi chỉ cần tô một màu đơn giản. Dùng TextStyle(color: ...) hoặc Container(color: ...) là đủ.
    • Hiệu suất là ưu tiên hàng đầu: Nếu em đang làm một app mà mỗi mili giây đều quý giá, và em muốn áp dụng ShaderMask cho rất nhiều element cùng lúc, hãy cẩn thận. Test kỹ hiệu suất trên các thiết bị yếu hơn trước khi 'nhảy' vào.
    • Khi chỉ cần bo góc đơn giản: ClipRRect sẽ là lựa chọn tốt hơn nhiều so với ShaderMask nếu mục đích chỉ là bo tròn các góc của một widget.

Tóm lại, ShaderMask là một công cụ cực kỳ mạnh mẽ trong 'kho vũ khí' của một 'dev' Flutter để tạo ra những UI 'đỉnh cao' và có tính thẩm mỹ. Hãy 'nghịch' nó thật nhiều, 'vọc' nó thật kỹ, và em sẽ thấy UI của mình 'lên một tầm cao mới'!

Thuộc Series: Flutter

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!