Flutter: Dart Vẽ Từng Pixel Như Thế Nào

Series: Cross-Platform Mobile Development
Trước đó: React Native: JavaScript Render Native UI Như Thế Nào
Tiếp theo: Ionic vs React Native vs Flutter: Khi Nào Dùng Cái Nào
Flutter đi approach cực đoan nhất trong ba framework: không dùng UI component của platform nào cả. Không WebView. Không native button, text field, hay scroll view. Flutter tự vẽ từng pixel, dùng rendering engine riêng của mình.
Nghe như reinvent the wheel. Nhưng quyết định kiến trúc này cho Flutter thứ mà không cross-platform framework nào khác có: consistency pixel-perfect trên mọi platform và toàn quyền kiểm soát mọi visual detail.
Tại Sao Dart? Lựa Chọn Gây Tranh Cãi
Khi Google announce Flutter, pushback lớn nhất là: "Sao lại Dart? Chả ai dùng Dart."
Câu trả lời nằm ở yêu cầu kỹ thuật của Flutter. Họ cần ngôn ngữ có thể làm thứ mà không mainstream language nào làm được:
Vừa AOT Vừa JIT Compilation
Development (JIT):
Production (AOT):
- Lúc development: Dart dùng JIT (Just-In-Time) compilation, cho phép stateful hot reload nổi tiếng của Flutter — đổi code, thấy kết quả trong dưới một giây, app state được giữ nguyên
- Lúc production: Dart dùng AOT (Ahead-Of-Time) compilation thành native ARM machine code — không interpreter, không VM, không JIT warmup, chỉ raw native performance
JavaScript không thể AOT compile thành native code (cần VM). Swift/Kotlin có thể AOT nhưng không có JIT cho hot reload. Dart làm được cả hai.
Không Lock Contention
Dart là single-threaded với event loop (giống JavaScript), nhưng cũng support isolate — parallel execution thật không share memory.
Tại sao quan trọng cho UI framework?
- Frame timing có thể dự đoán — không garbage collection pause do lock contention giữa thread
- 60/120fps mượt — UI thread không bao giờ bị block bởi lock
- Parallel computation — task nặng chạy trong isolate không ảnh hưởng UI
Object-Oriented Với Sound Null Safety
class User {
final String name;
final String? email; // nullable — phải handle null explicitly
final int age;
const User({required this.name, this.email, required this.age});
}
// Compiler enforce null safety
void greet(User user) {
print('Hello, ${user.name}');
// Đây sẽ là compile error:
// print(user.email.length); // Error: email có thể null
// Phải handle null:
if (user.email != null) {
print('Email: ${user.email!.length}');
}
}Syntax của Dart quen thuộc với developer Java/C#/TypeScript, giúp learning curve dễ chịu dù là ngôn ngữ "mới".
Rendering Pipeline: Ba Cây
Concept kiến trúc quan trọng nhất của Flutter là hệ thống ba cây: Widget tree → Element tree → RenderObject tree.
Widget Tree (Cái Bạn Viết)
Widget là mô tả bất biến của UI. Chúng rẻ để tạo và bỏ:
class ProfileCard extends StatelessWidget {
final String name;
final String role;
const ProfileCard({required this.name, required this.role});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(name, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text(role, style: const TextStyle(color: Colors.grey)),
],
),
),
);
}
}Insight quan trọng: Widget không render gì cả. Chúng chỉ là configuration object. Flutter tạo và hủy hàng nghìn widget mỗi frame — chúng được thiết kế để dùng xong bỏ.
Element Tree (Keo Dính)
Element là object persistent quản lý quan hệ widget-to-render. Khi Flutter rebuild widget tree (ví dụ sau setState()), nó không destroy và recreate mọi thứ. Thay vào đó, nó duyệt element tree và hỏi: "Mình có thể reuse bạn với widget mới này không?"
RenderObject Tree (Những Họa Sĩ)
RenderObject làm công việc thật: layout và painting. Mỗi RenderObject biết:
- Cách tính size của mình với constraint từ parent
- Cách vẽ mình lên canvas
- Cách xử lý hit testing (phát hiện touch/click)
Cách Một Frame Render
Khi có gì đó thay đổi (user tap button, data đến), đây là những gì xảy ra trong một frame (~16ms cho 60fps):
Rendering Engine: Skia → Impeller
Đây là nơi Flutter khác biệt cơ bản so với mọi framework khác. Trong khi Ionic dùng browser engine và React Native dùng native UI component, Flutter tự vẽ mọi pixel.
Skia (Engine Ban Đầu)
Skia là thư viện đồ họa 2D open-source của Google — cùng engine chạy:
- Chrome (browser rendering)
- Android (system UI)
- Firefox (một phần)
Flutter dùng Skia từ đầu. Skia nhận drawing command bậc cao ("vẽ hình chữ nhật bo góc tại tọa độ này với gradient này") và biến thành GPU instruction.
Impeller (Engine Mới)
Team Flutter build Impeller riêng cho Flutter để giải quyết problem lớn nhất của Skia trên mobile: shader compilation jank.
Vấn đề với Skia trên mobile:
Skia compile GPU shader lúc runtime — lần đầu tiên visual effect xuất hiện, có stutter ngắn khi shader compile. Gây "first-time jank" rõ rệt và khó fix.
Cách Impeller giải quyết:
Impeller pre-compile tất cả shader lúc build time. Khi app chạy, mọi visual effect đã compile sẵn và ready — zero shader compilation jank.
Kết quả:
- Worst-frame performance cải thiện đáng kể (không còn stutter random)
- 120fps nhất quán trên device hiện đại
- Tiêu thụ điện ít hơn — GPU usage hiệu quả hơn
Impeller là default trên iOS từ Flutter 3.16 và trên Android từ Flutter 3.22.
Platform Channel: Nói Chuyện Với Native Code
Flutter không thể vẽ mọi thứ. Đôi khi cần platform-specific API — camera, Bluetooth, biometric, payment. Platform channel là bridge:
// Dart side
import 'package:flutter/services.dart';
class BatteryService {
static const platform = MethodChannel('com.example.app/battery');
static Future<int> getBatteryLevel() async {
try {
final int result = await platform.invokeMethod('getBatteryLevel');
return result;
} on PlatformException catch (e) {
throw Exception('Không lấy được battery level: ${e.message}');
}
}
}// iOS side (Swift)
import Flutter
class BatteryPlugin {
static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(
name: "com.example.app/battery",
binaryMessenger: registrar.messenger()
)
channel.setMethodCallHandler { call, result in
if call.method == "getBatteryLevel" {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
let level = Int(device.batteryLevel * 100)
result(level)
} else {
result(FlutterMethodNotImplemented)
}
}
}
}Platform channel dùng binary message passing — hiệu quả hơn JSON serialization (dùng bởi bridge cũ của React Native) nhưng vẫn asynchronous. Với đa số use case, latency không đáng kể.
Flutter cũng support FFI (Foreign Function Interface) để gọi C/C++ code trực tiếp từ Dart — hữu ích cho native operation cần performance cao.
State Management: Ecosystem
Flutter không bắt buộc state management solution nào. Community đã phát triển nhiều approach:
// 1. setState (built-in, đơn giản)
class CounterWidget extends StatefulWidget {
@override
State<CounterWidget> createState() => _CounterState();
}
class _CounterState extends State<CounterWidget> {
int count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $count'),
ElevatedButton(
onPressed: () => setState(() => count++),
child: const Text('Increment'),
),
],
);
}
}
// 2. Riverpod (phổ biến nhất năm 2026)
final counterProvider = StateProvider<int>((ref) => 0);
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('Count: $count');
}
}
// 3. BLoC (phổ biến trong enterprise)
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<Increment>((event, emit) => emit(state + 1));
}
}Option phổ biến nhất năm 2026:
- Riverpod — compile-safe, linh hoạt, testing support tuyệt vời
- BLoC — event-driven, tuyệt vời cho business logic phức tạp và enterprise app
- Provider — đơn giản, build trên InheritedWidget
- GetX — gây tranh cãi nhưng phổ biến cho rapid prototyping
Vượt Ra Ngoài Mobile: Flutter Everywhere
Flutter giờ target sáu platform từ một codebase duy nhất:
Flutter Web
Flutter trên web khá thú vị — nó render vào <canvas> element (CanvasKit mode) hoặc generate DOM element (HTML mode). Điều này nghĩa là:
- CanvasKit: Pixel-perfect rendering, giống mobile. Nhưng download size lớn (~2MB WASM) và không SEO
- HTML mode: Download nhỏ hơn, SEO tốt hơn, nhưng không pixel-perfect
Flutter Web phù hợp nhất cho web app (dashboard, tool, admin panel), không phải website (nơi SEO và content quan trọng).
Flutter Desktop
Flutter desktop app đã production-ready trên Windows, macOS, và Linux. Ví dụ nổi bật:
- Canonical dùng Flutter cho Ubuntu installer
- Google dùng Flutter cho internal tool
- Superlist — task management app (macOS, Windows, mobile)
Khi Nào Flutter Là Lựa Chọn Đúng
Hoàn Hảo ✅
- UI custom, branded — app trông giống hệt trên mọi platform, không "native" theo từng platform
- App nặng animation — transition phức tạp, physics-based interaction, custom painting
- Startup/MVP có vision thiết kế — ship sản phẩm đẹp, độc đáo nhanh
- Multi-platform (ngoài mobile) — target mobile, web, và desktop từ một codebase
- Team sẵn sàng học Dart — đầu tư sẽ được đền đáp bằng productivity
Cân Nhắc ⚠️
- Cần platform look-and-feel native — Flutter có thể mô phỏng iOS/Material, nhưng vẫn là Flutter vẽ, không phải OS
- App có nhiều web content — embed WebView trong Flutter hoạt động nhưng không seamless như Ionic
- Team đã đầu tư nhiều vào React/TypeScript — React Native sẽ là transition mượt hơn
Không Nên ❌
- Wrapper đơn giản cho web app — dùng Ionic/Capacitor
- App phải dùng native UI control — accessibility và platform convention quan trọng hơn consistency
- Team không thể chịu learning curve Dart — nếu time-to-market quan trọng và team biết JavaScript, cân nhắc alternative
Ai Dùng Flutter?
- Google Pay — toàn bộ app rebuild bằng Flutter
- BMW — My BMW app
- Alibaba — Xianyu (chợ đồ cũ, 200M+ user)
- Nubank — ngân hàng số lớn nhất Brazil (80M+ khách hàng)
- eBay Motors — marketplace xe cộ
- PUBG Mobile — companion app
- Toyota — hệ thống infotainment
- Canonical — Ubuntu desktop installer
Trạng Thái Flutter Năm 2026
Flutter tiếp tục phát triển nhanh:
- Impeller giờ là default trên cả iOS và Android — shader jank đã bị loại bỏ
- Dart 3.x — pattern matching, sealed class, record, enhanced enum
- Flutter 3.x — web rendering cải tiến, desktop ổn định hơn, platform view
- pub.dev — 50,000+ package trong Dart/Flutter ecosystem
- Firebase integration — first-class support cho backend service của Google
- Flame — 2D game engine trưởng thành build trên Flutter
Key Takeaway
- Flutter tự vẽ mọi pixel — không WebView, không native component. Toàn quyền kiểm soát rendering.
- Dart được chọn vì lý do kỹ thuật — AOT + JIT compilation, sound null safety, và single-threaded predictability
- Ba cây (Widget → Element → RenderObject) chạy rendering pipeline, cho phép diff hiệu quả và paint tối thiểu
- Impeller thay thế Skia làm rendering engine, loại bỏ shader compilation jank cho 120fps nhất quán
- Platform channel kết nối Dart với native API (camera, Bluetooth, v.v.) qua asynchronous binary message passing
- Flutter target 6 platform từ một codebase — nhưng mobile vẫn là target mạnh nhất
- Trade-off rõ ràng: pixel-perfect consistency và custom UI, đánh đổi bằng việc phải học Dart và mất native platform look-and-feel
Cược của Flutter là sở hữu rendering pipeline đáng giá cái cost phải reimplementing platform UI convention. Với app mà brand consistency và visual polish quan trọng hơn native platform fidelity — cược đó đã thắng lớn.
Series: Cross-Platform Mobile Development
Trước đó: React Native: JavaScript Render Native UI Như Thế Nào
Tiếp theo: Ionic vs React Native vs Flutter: Khi Nào Dùng Cái Nào
📬 Đăng ký nhận bản tin
Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.
Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.
💬 Comments
Đăng nhập để bình luận
Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.