Command Palette

Search for a command to run...

Hướng dẫn tạo Bottom Tabs & Drawer Navigation trong Expo Router

Bottom Tabs & Drawer Navigation trong Expo Router

Ở bài trước, bạn đã nắm được Stack Navigation - "nghệ thuật" mở các trang theo dạng xếp chồng lên nhau.

Tuy nhiên, hãy nhìn vào ứng dụng Facebook, Shopee hay Tiktok trên điện thoại của bạn. Để chuyển đổi giữa "Trang chủ", "Thông báo" và "Tài khoản", bạn không thể cứ dùng Stack để đẩy trang lên rồi lại bấm nút Back mãi được. Bạn cần một thanh Menu cố định luôn xuất hiện trên màn hình!

Trong lập trình Mobile, có 2 loại Menu phổ biến nhất:

  1. Bottom Tabs: Thanh điều hướng nằm dưới đáy màn hình.
  2. Drawer: Menu ẩn vuốt từ cạnh viền (thường gọi là Hamburger menu).

Hôm nay, chúng ta sẽ dùng "sức mạnh" của Expo Router để tạo ra cả hai loại Menu này một cách vô cùng dễ dàng.

1. Bí mật đằng sau thanh Menu: _layout.tsx

Trước khi bắt tay vào code, bạn phải hiểu rõ một quy tắc sống còn của Expo Router: File-based routing.

Bạn có nhớ ở những bài trước đã nhắc đến một file có tên đặc biệt là _layout.tsx không?

  • Trong Expo Router, _layout.tsx đóng vai trò là một khung xương (Wrapper).
  • Thay vì chỉ hiển thị một màn hình duy nhất, file này có nhiệm vụ "ôm" lấy tất cả các màn hình nằm cùng thư mục với nó và gắn thêm một bộ khung chung (như thanh Header dùng chung, hoặc thanh Bottom Tabs dùng chung).

Để tạo Bottom Tabs, chúng ta sẽ sử dụng file _layout.tsx!

2. Dựng nhanh Bottom Tabs trong 5 phút

Bottom Tabs là tiêu chuẩn vàng của thiết kế Mobile hiện đại (UX/UI) vì nó nằm ngay trong tầm với của ngón cái.

Bottom Tabs React Native

Để tạo Tabs gọn gàng, trong thư mục app/, chúng ta thường nhóm các màn hình Tab vào một thư mục có ngoặc đơn, ví dụ: app/(tabs). (Lưu ý: Thư mục có dấu ngoặc đơn () gọi là Route Group, nó giúp gom nhóm file mà KHÔNG làm thay đổi đường dẫn URL).

Cấu trúc dự án lúc này sẽ trông như sau:

📁 app
 └── 📁 (tabs)
      ├── 📄 _layout.tsx   <-- Trái tim của Bottom Tabs
      ├── 📄 index.tsx     <-- Tab Trang chủ (Home)
      └── 📄 profile.tsx   <-- Tab Cá nhân (Profile)

Bây giờ, hãy mở file app/(tabs)/_layout.tsx và nhập đoạn code sau:

import { Tabs } from 'expo-router'
import { Ionicons } from '@expo/vector-icons' // Thư viện icon có sẵn của Expo

export default function TabLayout() {
  return (
    <Tabs screenOptions={{ tabBarActiveTintColor: '#e91e63' }}>
      {/* Định nghĩa Tab Trang Chủ */}
      <Tabs.Screen
        name="index" // Trỏ tới file index.tsx
        options={{
          title: 'Trang Chủ',
          tabBarIcon: ({ color }) => <Ionicons name="home" size={24} color={color} />,
        }}
      />

      {/* Định nghĩa Tab Cá Nhân */}
      <Tabs.Screen
        name="profile" // Trỏ tới file profile.tsx
        options={{
          title: ' Nhân',
          tabBarIcon: ({ color }) => <Ionicons name="person" size={24} color={color} />,
        }}
      />
    </Tabs>
  )
}

Giải mã đoạn code:

  1. Component <Tabs> được lấy thẳng từ expo-router. Nó sẽ tự động biến các file nằm cạnh nó thành một thanh menu dưới đáy.
  2. name="index"name="profile" chính là sự kết nối giữa cấu trúc Tab với các file giao diện của bạn.
  3. tabBarIcon là nơi bạn gắn các icon đẹp mắt. Expo đã tích hợp sẵn thư viện khổng lồ @expo/vector-icons, bạn không cần cài thêm bất cứ thứ gì!

Chỉ với vài dòng code trên, bạn đã có một thanh Bottom Tabs chuẩn xác, mượt mà và tự động tô màu khi được chọn!

3. Tạo Menu vuốt từ cạnh bên (Drawer Navigation)

Drawer Navigation thường được sử dụng khi ứng dụng của bạn có quá nhiều mục (như Cài đặt, Liên hệ, Trợ giúp, Điều khoản...) mà thanh Bottom Tabs (tối đa 5 mục) không thể chứa hết.

Drawer Navigation

Khác với Tabs có sẵn, để dùng Drawer, bạn cần cài đặt thêm 2 thư viện hỗ trợ cử chỉ vuốt cực mạnh của React Native:

Bước 1: Mở Terminal và chạy lệnh cài đặt:

npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated

Bước 2: Cấu trúc code Cấu trúc hoàn toàn tương tự như Tabs. Bạn tạo một thư mục app/(drawer) và thêm file _layout.tsx vào đó:

// app/(drawer)/_layout.tsx
import { GestureHandlerRootView } from 'react-native-gesture-handler'
import { Drawer } from 'expo-router/drawer'
import { Ionicons } from '@expo/vector-icons'

export default function DrawerLayout() {
  return (
    // Bắt buộc phải bọc Drawer trong GestureHandlerRootView để nhận diện thao tác vuốt
    <GestureHandlerRootView style={{ flex: 1 }}>
      <Drawer screenOptions={{ drawerActiveTintColor: '#0984e3' }}>
        <Drawer.Screen
          name="index" // Trỏ tới file index.tsx (Trang chính của Drawer)
          options={{
            drawerLabel: 'Trang Chủ',
            title: 'Trang Chủ',
            drawerIcon: ({ color }) => <Ionicons name="home-outline" size={24} color={color} />,
          }}
        />

        <Drawer.Screen
          name="settings" // Trỏ tới file settings.tsx
          options={{
            drawerLabel: 'Cài Đặt',
            title: 'Hệ Thống Cài Đặt',
            drawerIcon: ({ color }) => <Ionicons name="settings-outline" size={24} color={color} />,
          }}
        />
      </Drawer>
    </GestureHandlerRootView>
  )
}

Lúc này, trên cùng góc trái màn hình của bạn sẽ xuất hiện một icon "3 dấu gạch ngang" (Hamburger). Khi bấm vào hoặc dùng tay vuốt từ mép trái màn hình sang, một bảng Menu tuyệt đẹp sẽ trượt ra!

4. Kiến trúc thực tế: Kết hợp cả 3 loại Navigation

Bạn có thể thắc mắc: "Vậy tôi nên dùng Stack, Tabs hay Drawer?"

Câu trả lời của các ứng dụng hàng đầu (như Facebook) là: Dùng cả 3! Chúng được lồng ghép vào nhau theo một kiến trúc hình cây cực kỳ thông minh:

  • Tầng 1 (Root Stack): Gồm 2 khu vực lớn là [Màn hình Đăng Nhập][Khu vực Đã Đăng Nhập]. (Nếu chưa đăng nhập thì không cho xem Tabs).
  • Tầng 2 (Bên trong Khu vực Đã Đăng Nhập): Chứa thanh Bottom Tabs để chuyển đổi giữa Trang Chủ, Video, Bạn bè, Tài khoản.
  • Tầng 3 (Bên trong Tab Tài khoản): Có thể chứa một Drawer để kéo ra các tùy chọn Cài đặt bảo mật, Trợ giúp, Đăng xuất.
  • Tầng 4 (Bên trong Tab Trang Chủ): Khi bấm vào một bài viết, ứng dụng dùng Stack để mở đè trang Chi Tiết Bài Viết lên trên cùng.

Với Expo Router, việc lồng ghép này cực kỳ đơn giản. Bạn chỉ cần xếp các thư mục lồng vào nhau, hệ thống file-based routing sẽ tự động hiểu và sắp xếp luồng đi cho bạn!

Kết luận: Navigation đã không còn là trở ngại

Chúc mừng bạn! Kết thúc bài học, bạn đã chính thức nắm trong tay bộ 3 "quyền lực" nhất của điều hướng: Stack, Tabs và Drawer. Hiện tại, ứng dụng của bạn đã có một bộ khung hoàn hảo, giao diện lộng lẫy và trải nghiệm chuyển trang mượt mà không thua kém bất kỳ ứng dụng nào trên Store.

Tuy nhiên, mọi dữ liệu từ đầu series đến giờ đều là dữ liệu "chết" (do chúng ta tự gõ tay vào code). Ở thế giới thực, thông tin bài viết, tài khoản hay bình luận đều phải được lấy từ server.

Hãy chuẩn bị tinh thần bước vào giai đoạn tiếp theo, chúng ta sẽ cùng nhau mang sức sống vào ứng dụng thông qua API.

Bài viết liên quan

Hướng dẫn cài đặt môi trường React Native & Expo trong 15 phút

Bạn sợ cài đặt môi trường lập trình mobile phức tạp? Hướng dẫn chi tiết cách dùng Expo Go, Node.js để chạy app React Native ngay lập tức.

Cách làm chủ StyleSheet & Flexbox Layout trong React Native

Nắm vững nghệ thuật tạo kiểu với StyleSheet và làm chủ hệ thống Flexbox trong React Native. Hướng dẫn chi tiết flexDirection, justifyContent, alignItems.

Tìm hiểu 5 Core Components quan trọng nhất trong React Native

Hướng dẫn cách sử dụng các Core Components trong React Native. Nắm vững View, Text, Image, TextInput và ScrollView để xây dựng mọi giao diện mobile.

Cách dùng FlatList trong React Native: Hiển thị danh sách không giật lag

Giải quyết bài toán hiển thị danh sách dữ liệu lớn với FlatList và SectionList. Hướng dẫn tối ưu hóa bộ nhớ và tốc độ cuộn (scroll) mượt mà.