Khoá học đang trong giai đoạn preview

Nhập mật khẩu để truy cập nội dung khoá học.

Nâng cao Bài 5/8 · 25 phút đọc

Mobile App không cần học Swift/Kotlin

Mục tiêu

Chuyển đổi web app sang mobile app bằng Expo/React Native, chạy thử trên điện thoại thật.

Sau bài này, bạn sẽ:

  • ✅ Hiểu 3 con đường làm mobile app và chọn được đường phù hợp
  • ✅ Setup Expo project và chạy trên điện thoại thật qua Expo Go
  • ✅ Biết sự khác biệt giữa React (web) và React Native (mobile)
  • ✅ Dùng AI chuyển đổi code web sang mobile

Nội dung chính

5.1 — Lựa chọn đường đi Mobile

┌─────────────────────────────────────────────────────────────┐
│                   3 CON ĐƯỜNG LÀM MOBILE APP                │
│                                                             │
│  🌐 PWA (Progressive Web App)                               │
│     Web app thêm vào Home Screen điện thoại                │
│     Ưu: Không cần sửa code, nhanh nhất                     │
│     Nhược: Không lên App Store, ít tính năng native        │
│                                                             │
│  📱 Expo / React Native          ← CHỌN CÁI NÀY           │
│     Viết code React → chạy trên iOS và Android             │
│     Ưu: App thật, lên App Store được, dùng lại tư duy React│
│     Nhược: Cần learn thêm component mobile                 │
│                                                             │
│  🔄 Capacitor / Ionic                                       │
│     Bọc web app vào "vỏ" native                            │
│     Ưu: Giữ nguyên code web, chạy như app                  │
│     Nhược: Performance kém hơn native                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Mẹo: Nếu bạn chỉ cần “app trên điện thoại” cho demo hoặc portfolio, PWA là đủ — mất 5 phút setup. Nhưng nếu muốn lên App Store hoặc dùng camera/GPS/push notification → chọn Expo.

5.2 — Setup Expo (chi tiết từng bước)

Bước 1: Kiểm tra Node.js

node --version
→ Cần phiên bản 18 trở lên. Nếu chưa có, tải tại nodejs.org

Bước 2: Tạo project Expo

npx create-expo-app FocusFlow-Mobile --template blank
cd FocusFlow-Mobile

Bước 3: Cài Expo Go trên điện thoại

  • iPhone: Tìm “Expo Go” trên App Store → Tải miễn phí
  • Android: Tìm “Expo Go” trên Google Play → Tải miễn phí

Bước 4: Chạy project

npx expo start

Màn hình terminal sẽ hiện QR code. Quét QR code:

  • iPhone: Mở camera → quét QR → bấm link Expo Go
  • Android: Mở app Expo Go → Scan QR Code

Bước 5: Thấy app trên điện thoại! Mỗi khi bạn save code, app tự động reload trên điện thoại (hot reload).

Screenshot: Terminal hiện QR code + Expo Go app chạy trên điện thoại

Mẹo: Đảm bảo điện thoại và máy tính cùng 1 mạng WiFi. Nếu không cùng WiFi, Expo Go sẽ không quét được QR. Nếu dùng WiFi công ty có firewall, thử chuyển sang dùng npx expo start --tunnel (cần cài @expo/ngrok).

Cấu trúc thư mục Expo:

FocusFlow-Mobile/
├── app/                  ← Các trang (Expo Router)
│   ├── _layout.jsx       ← Layout chung
│   ├── index.jsx          ← Trang chính
│   └── settings.jsx       ← Trang cài đặt
├── components/            ← Components dùng chung
├── lib/                   ← Supabase, helpers
├── assets/                ← Hình ảnh, fonts
├── app.json               ← Config app (tên, icon, splash)
└── package.json

5.3 — Khác biệt Web vs Mobile

Web (React)Mobile (React Native)
<div><View>
<p>, <span><Text>
<img><Image>
<button><TouchableOpacity> hoặc <Pressable>
CSS fileStyleSheet.create()
onClickonPress
Scroll tự do<ScrollView> hoặc <FlatList>
<input><TextInput>
localStorageAsyncStorage
CSS flexbox (row default)Flexbox (column default)

Mẹo: React Native dùng flexbox giống web NHƯNG mặc định là flexDirection: 'column' (xếp dọc), trong khi web mặc định row (xếp ngang). Đây là khác biệt gây nhầm lẫn nhiều nhất.

Prompt chuyển đổi web → mobile (chi tiết):

Tôi có web app React, cần chuyển sang React Native cho Expo.
File web cần chuyển: @src/pages/Home.jsx

Hãy chuyển đổi theo quy tắc sau:
1. Thay HTML tags bằng React Native components:
   - div → View
   - p, span, h1, h2 → Text (dùng style để phân biệt)
   - img → Image (dùng source={{ uri: url }})
   - button → Pressable hoặc TouchableOpacity
   - input → TextInput
   - a (link) → Pressable + Linking.openURL()

2. Chuyển CSS sang StyleSheet:
   - Tạo const styles = StyleSheet.create({...}) ở cuối file
   - Không dùng className, dùng style={styles.xxx}
   - Đổi kebab-case sang camelCase (font-size → fontSize)
   - Đơn vị: không ghi px (fontSize: 16 thay vì '16px')

3. Giữ nguyên logic:
   - State, hooks, useEffect → giữ nguyên
   - Supabase connection → giữ nguyên
   - API calls → giữ nguyên

4. Thêm các component mobile-specific:
   - SafeAreaView bao ngoài cùng (tránh notch iPhone)
   - StatusBar configuration
   - KeyboardAvoidingView cho form (tránh keyboard che input)

5. Navigation:
   - Dùng Expo Router (file-based routing)
   - Tạo tab navigation nếu app có 3+ trang chính

Đảm bảo chạy được trên cả iPhone và Android.
Screenshot: So sánh giao diện web app (trái) vs mobile app (phải) sau khi chuyển đổi

5.4 — Cài đặt thư viện phổ biến cho Expo

# Navigation (điều hướng giữa các trang)
npx expo install expo-router

# Icons
npx expo install @expo/vector-icons

# Storage (thay localStorage)
npx expo install @react-native-async-storage/async-storage

# Supabase
npm install @supabase/supabase-js

# Image picker (chụp ảnh / chọn từ thư viện)
npx expo install expo-image-picker

# Push notification
npx expo install expo-notifications

Mẹo: Luôn dùng npx expo install thay vì npm install cho thư viện Expo. npx expo install tự chọn phiên bản tương thích với Expo SDK hiện tại, tránh lỗi version mismatch.

Lỗi thường gặp

Vấn đề: Expo Go không quét được QR code. → Giải pháp: (1) Kiểm tra điện thoại và máy tính cùng WiFi. (2) Thử npx expo start --tunnel (cần internet). (3) Trên Android, mở Expo Go → nhập URL thủ công (hiện ở terminal, dạng exp://192.168.x.x:8081). (4) Tắt VPN nếu đang bật.

Vấn đề: App crash ngay khi mở — “Something went wrong”. → Giải pháp: Xem terminal trên máy tính — lỗi chi tiết hiện ở đó. Copy lỗi → paste cho AI. Nguyên nhân phổ biến nhất: import thư viện web (ví dụ react-router-dom) thay vì thư viện mobile (expo-router).

Vấn đề: Text không hiển thị — “Text strings must be rendered within a <Text> component”. → Giải pháp: Trong React Native, mọi text PHẢI nằm trong <Text>. Không được viết text trực tiếp trong <View>. Sai: <View>Hello</View>. Đúng: <View><Text>Hello</Text></View>. Prompt AI: “Tìm tất cả text nằm ngoài component Text và sửa.”

Vấn đề: StyleSheet không hoạt động như CSS — layout bị sai. → Giải pháp: Nhớ: React Native flexbox mặc định flexDirection: 'column'. Nếu muốn xếp ngang, thêm flexDirection: 'row'. Không có float, position: fixed, hay z-index như web. Prompt AI: “Layout bị sai, tôi cần [mô tả layout muốn]. Sửa StyleSheet cho đúng.”

Vấn đề: Hot reload không hoạt động — sửa code nhưng app không update. → Giải pháp: (1) Shake điện thoại → menu Expo hiện ra → bấm “Reload”. (2) Trong terminal bấm r để reload. (3) Nếu vẫn không được, dừng server (Ctrl+C) → chạy lại npx expo start --clear (xóa cache).

Bài tập thực hành

Nhiệm vụ:

Các bước thực hiện:

  1. Tạo project Expo mới (~5 phút)
  2. Chạy app trên điện thoại bằng Expo Go — chụp screenshot (~5 phút)
  3. Dùng AI chuyển 1-2 trang chính từ web app sang mobile (~15 phút)
  4. Kết nối Supabase — dùng lại database cũ (~5 phút)
  5. Test các chức năng chính trên điện thoại thật (~5 phút)
  6. Commit code (~2 phút)

Tiêu chí hoàn thành:

  • App chạy được trên điện thoại thật qua Expo Go
  • Ít nhất 1 trang hiển thị dữ liệu từ Supabase
  • Navigation hoạt động giữa các trang
  • Không crash khi sử dụng bình thường

Gợi ý nếu bị stuck: Nếu chuyển đổi web → mobile quá khó, bắt đầu từ 0 trên Expo. Prompt AI: “Tạo trang chính cho app [tên] bằng React Native. Hiển thị danh sách [items] từ Supabase. Dùng FlatList, có pull-to-refresh.” Viết mới nhanh hơn chuyển đổi khi mới bắt đầu.

Thời gian: 40 phút Deliverable: Video quay màn hình điện thoại chạy app (15-30 giây)

Kiểm tra kiến thức

1. Trong React Native, thẻ HTML <div> được thay bằng component nào?

2. React Native flexbox mặc định khác web ở điểm nào?

3. Tại sao nên dùng 'npx expo install' thay vì 'npm install' cho thư viện Expo?

4. Lỗi 'Text strings must be rendered within a <Text> component' xảy ra khi nào?

5. Để chạy Expo trên điện thoại thật, điều kiện bắt buộc là gì?

Tham gia Bình Dân AI

Hoàn toàn miễn phí. Bạn sẽ nhận được:

Vào group Zalo ngay

Miễn phí mãi mãi. Không spam. Rời group bất cứ lúc nào.