iOS to Flutter
iOS to Flutter

Cần làm gì khi chuyển từ iOS Native sang Flutter?

Việc chuyển từ lập trình iOS thuần (native) sang đa nền tảng với Flutter đem lại cho bạn một góc nhìn mới về phát triển ứng dụng mobile. Flutter mang đến nhiều lợi thế về tốc độ phát triển, khả năng chạy đa nền tảng (iOS, Android, web, desktop), và cộng đồng hỗ trợ đông đảo. Tuy nhiên, để tận dụng tốt những lợi ích này, bạn cũng cần thay đổi hoặc giữ lại một số tư duy từ iOS native. Bài viết này sẽ giúp bạn nhận diện những gì nên giữ, những gì nên bỏ, và so sánh cách Flutter xây dựng UI so với iOS native.

1. Hiểu về Flutter

Flutter là một framework UI mã nguồn mở được phát triển bởi Google. Nó cho phép lập trình viên xây dựng các ứng dụng cho cả iOS và Android từ một mã nguồn duy nhất. Điều này có nghĩa là thay vì phát triển hai ứng dụng riêng biệt cho mỗi nền tảng, bạn có thể viết mã một lần duy nhất và triển khai cho cả hai hệ điều hành. Flutter sử dụng Dart, một ngôn ngữ lập trình do Google phát triển, với cú pháp dễ tiếp cận và tối ưu hóa cho việc xây dựng ứng dụng di động.

2. Lập trình với Dart

  • Khi chuyển từ iOS Native sang Flutter, việc học ngôn ngữ Dart là bước đầu tiên quan trọng. Dart có cú pháp khá giống với các ngôn ngữ lập trình phổ biến như JavaScript và Java, giúp những lập trình viên có kinh nghiệm với các ngôn ngữ này dễ dàng làm quen với Dart. Dart không chỉ giúp xây dựng các ứng dụng di động, mà còn có thể sử dụng cho các ứng dụng web và desktop.
  • Mở rộng đầu óc, sẵn sàng tiếp nhận những cái mới, những cái tương đồng nhưng không giống hoàn toàn.
  • Không nên mang toàn bộ những kiến thức, sự hiểu biết về cú pháp của Swift mà áp dụng vào ngôn ngữ Dart vì mỗi ngôn ngữ được phát triển theo một khía cạnh riêng mà chúng ta không thể áp dụng qua lại lẫn nhau, hay đơn giản bạn hiểu là khi tiếp cận với một thứ mới thì tạm hãy quên những cái cũ đi sẽ giúp bạn đi nhanh hơn.

Ví dụ mẫu:

enum Requester {
    case signIn(email: String, password: String)
    case signUp(email: String, password: String)
    case logut
}
demo.swift
enum Requester {
  signIn, signUp, signOut
}
demo.dart

Bạn để ý rằng với Swift ta có thể truyền thêm Parameters vào các cases, còn với Dart thì không thể.

3. Cơ chế xây dựng UI

Với iOS sử dụng UIKit / UIView

  • Imperative nghĩa là bạn tự “ra lệnh” cho hệ thống về việc tạo view, thêm subview, thiết lập frame hoặc constraint để layout.
  • Mỗi UIView và UIViewController trong UIKit đều liên kết với một CALayer (Core Animation Layer) bên dưới.
  • Hệ thống iOS sử dụng Core Animation và Core Graphics để vẽ các Layer này lên màn hình.
    Về cơ bản, bạn sắp xếp một loạt các view chồng lên nhau, iOS tổng hợp (composite) tất cả layer lại để xuất ra buffer hiển thị lên màn hình.
  • Khi bạn thay đổi thuộc tính UI (như thay đổi frame, thay đổi background color…), UIKit/UIView sẽ báo cho Core Animation biết rằng layer cần được vẽ lại (re-draw). Sau đó, Core Animation tối ưu quá trình vẽ để cập nhật giao diện.
  • Auto Layout: Bạn thường mô tả các constraint (ràng buộc) giữa các view (ví dụ: “view A cách top 20pt, cách leading 10pt, v.v.”). Khi kích thước màn hình thay đổi (xoay ngang/dọc), iOS tự tính lại frame của các view dựa trên constraint.
CALayer
CALayer

Với Flutter tất cả là Wiget

Flutter cũng sử dụng mô hình declarative. Bạn xây dựng UI qua các hàm (hoặc class) trả về một widget tree (cây widget), ví dụ:

Widget build(BuildContext context) {
  return Column(
    children: [
      Text('Hello'),
      Text('Flutter'),
    ],
  );
}

ui.dart
  • Khi dữ liệu (state) thay đổi, Flutter sẽ gọi phương thức build() của widget (hoặc các widget con), tạo một cây widget mới, so sánh với cây cũ, sau đó cập nhật UI cần thiết.

Flutter có cách hoạt động đặc thù so với các framework UI khác. Nó không dựa trên các thành phần gốc (native components) của hệ điều hành, mà tự vẽ (render) mọi thứ qua “engine” của chính mình (Skia). Quá trình này được chia thành nhiều tầng (layers) và được tổ chức chủ yếu thông qua ba “cây” (trees):

Widget Tree (Cây widget):

Đây là nơi chúng ta lập trình trực tiếp, khai báo các widget (như Text, Container, Row…).
Mỗi widget mô tả giao diện và logic biểu diễn của nó (ví dụ: màu sắc, kích cỡ, hình dạng…).

Element Tree (Cây element):

Mỗi widget khi chạy sẽ sinh ra một element (hiểu nôm na là “phiên bản” cụ thể của widget đó) để gắn vào layout.
Element giữ vai trò kết nối giữa widget và RenderObject.

Render Tree (Cây render):

Thực hiện nhiệm vụ tính toán vị trí (layout) và vẽ (paint) thực tế cho widget.
Trong Render Tree, mỗi RenderObject sẽ quyết định chiều rộng, chiều cao của chính nó và cách nó “vẽ” (render) trên màn hình.

Widget Tree
Widget Tree

4. Lời khuyên khi bắt đầu với Flutter

Học Dart cơ bản
  • Biết cách dùng async/await, Future, Stream.
Hiểu widget cốt lõi
  • Text, Row, Column, Container, Expanded…
  • Cách “lồng” widget để tạo layout phức tạp.

Quản lý trạng thái (State) là một khái niệm quan trọng trong phát triển ứng dụng, đặc biệt là khi xây dựng ứng dụng đa nền tảng. Có rất nhiều cách để tiếp cận với việc quản lý State khi làm việc với Flutter. Dưới đây là một vài phương thức bạn có thể tiếp cận.

  • Provider
  • Riverpod
  • BLoC

Trên đây đều là các quản lý State phổ biến được cộng đồng phát triển với Flutter. Với cá nhân tôi thì tôi rất thích Reactive Programming nên tôi sử dụng RxDart kết hợp với mô hình MVVM quen thuộc.

5. Kết luận

Kế thừa kinh nghiệm iOS về tối ưu, tổ chức code, UX tốt.
Chủ động quên đi thói quen dùng Storyboard, suy nghĩ “chỉ cho iOS”.
Học từ đầu cách Flutter quản lý widget, state, và code đa nền tảng.

Flutter mang đến cơ hội phát triển một ứng dụng cho nhiều hệ điều hành chỉ bằng một codebase. Hiểu rõ những điểm giống và khác nhau giữa iOS và Flutter sẽ giúp bạn “lên tay” nhanh chóng, đồng thời giữ được chất lượng ứng dụng như trên iOS native. Chúc bạn thành công!