iT邦幫忙

2024 iThome 鐵人賽

DAY 10
0

昨天用了 Cursor 幫我們寫了註冊頁面,各位應該有體驗到AI的強大了吧。今天我們要使用 UI Library 來把我們畫面美化。除了美化以外最大的原因是因為也可以省掉一些自己刻元件的時間。

UI Library

UI Library(使用者介面函式庫)是一組預定義的元件和工具,專門為了讓開發者更快速地設計網頁或應用程式的使用者介面(UI)。它通常包含多種常見的介面元素,例如按鈕、表單、卡片、導航欄等,讓開發者可以直接使用這些已設計好的元件,而不必從頭開始造輪子。

優點

  1. 節省開發時間:提供現成的元件,開發者無需重複造輪子,可以更快完成介面的開發。
  2. 統一的設計語言:UI Library 通常依循一致的設計規範,使應用的不同部分保持視覺一致性,讓使用者體驗更一致。
  3. 跨瀏覽器和平台兼容性:許多 UI Library 已經針對不同的瀏覽器和裝置進行了優化,確保元件在多個平台上正常運作。
  4. 可擴展性:許多 UI Library 提供自定義功能,開發者可以根據需求對預設樣式進行調整,打造符合專案需求的 UI。
  5. 較有設計感:畢竟我們都沒學過設計,通常沒什麼美感,但使用 Libaray 設計基本上會有一定水準,做出來的產品看起來不會太差。至少看起來像設計過的。

缺點

  1. 學習成本:每個 UI Library都會有自己的文件,和使用方法都需要花時間去看文件和學習才能使用。
  2. 彈性較低:如果想要一些特殊的功能,可能 Library會沒有,這時候可能會看要重頭寫還是去修改套件的,但修改套件的其實非常難。

UI Library 是專門為了簡化和加速前端開發的工具,幫助開發者輕鬆打造現代化的應用介面,並確保設計的一致性。整體而言我覺得如果不是太獨特的設計,建議先使用UI套件會比較快。

常見的UI Library

以下為常見的 UI Library,通常就選自己習慣的喜歡的就好了。但如果上班,就只能選和團隊一樣的用。

React UI Library

名稱 簡介 官方連結
Ant Design 豐富的元件和設計規範,適合企業應用和後台系統。 Ant Design
Material UI (MUI) 基於 Google Material Design,適合現代化且一致的介面。 Material UI
Chakra UI 強調易用性與可擴展性,提供簡單的 API 和自定義樣式。 Chakra UI
Blueprint 針對資料密集型應用,提供精細控制與豐富介面元件。 Blueprint
Semantic UI React 基於自然語言設計的元件,簡單易用,適合快速上手。 Semantic UI React
React Bootstrap 基於 Bootstrap,為 React 應用提供靈活、響應式的前端設計框架。 React Bootstrap

React Native UI Library

名稱 簡介 官方連結
NativeBase 跨平台支援,提供跨 iOS 和 Android 的元件,易於定制和擴展。 NativeBase
React Native Elements 提供通用的 React Native 元件,簡單易上手,適合快速構建應用 UI。 React Native Elements
React Native Paper 遵循 Material Design 標準,適合統一設計語言的應用。 React Native Paper
Shoutem UI 多樣的預設樣式和元件,適合打造漂亮且一致的應用介面。 Shoutem UI
Teaset 中國開發者常用的簡潔實用的元件庫,適合快速開發行動應用。 Teaset

開始使用

我們這次專案選用 native-base ,我一開始是想找 Bootstrap 但我發現 RN 好像沒有,就只好找一個看起來比較好用的來用。

native-base 我發現他除了在 RN上可以用,網頁好像也可以用。這樣學起來不虧,等於又多會一個 UI Libarary。所以我就選用這個了。

那就讓我們開始吧!

Setup

起手式一樣,先在 Cursor 的聊天室提出問題請他協助。

我想 使用 native-base 這個套件幫我改寫一下登入頁面

Cursor:
要使用 native-base 套件來改寫你的登入頁面,你需要先安裝 native-base。可以使用以下命令來安裝:

npm install native-base

接下來,這是改寫後的登入頁面代碼:

import React, { useState } from 'react';
import { NativeBaseProvider, Box, Input, Button, Text } from 'native-base';

const Login = () => {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');

    const handleLogin = () => {
        console.log('登入中...', { username, password });
    };

    return (
        <NativeBaseProvider>
            <Box flex={1} justifyContent="center" padding={4}>
                <Text fontSize="2xl" textAlign="center" marginBottom={4}>登入</Text>
                <Input
                    placeholder="用戶名"
                    value={username}
                    onChangeText={setUsername}
                    marginBottom={3}
                />
                <Input
                    placeholder="密碼"
                    type="password"
                    value={password}
                    onChangeText={setPassword}
                    marginBottom={3}
                />
                <Button onPress={handleLogin}>
                    登入
                </Button>
            </Box>
        </NativeBaseProvider>
    );
};

export default Login;

我們可以點擊 Cursor 給你的回應上面的 Run,會自己幫你在終端機下指令。

下完指令後看模擬機會發現錯誤,壞掉了。這時候我們看一下終端機是什麼問題。看起來像是少了 react-native-safe-area-contex 這個套件。通常少了套件,在終端機下指令

yarn add react-native-safe-area-contex

這樣就可以安裝缺少的套件了。

再來我們可以看到,還是錯誤,看起來一樣只是缺少套件而已。不怕再操作一次。

yarn add react-native-svg

誒,終端機沒錯誤但模擬機還是錯誤。怎麼辦。其實真的沒辦法的話,建議還是要看一下官方文件。因為有時候問 GPT會亂回你,反而會花更多時間都解決不了。

看一下官方文件 Existing Project 這邊,一開始就是 安裝相依套件(Install dependencies) 這邊前面的步驟我們做過了,不過要再做一次也可以。

yarn add native-base react-native-svg@12.1.1 react-native-safe-area-context@3.3.2

再來是 Run pod install ,原來是少了安裝 ios的套件。跟著指令做一次。

cd ios/
pod install

接下來就可以看到登入頁面了!看起來好像差不多,但按鈕變得不一樣了。我們就可以開始使用 native-base 的 UI Library了。

註冊頁面

今天我們再來做一個註冊頁面吧。老樣子一樣請 Cursor幫我們做。

提問

我覺得很棒 那我現在想要有註冊頁面,如果再畫面上面放一個註冊頁面的按鈕,註冊的話需要打 使用者名字 信箱 密碼 確認密碼 如果不想註冊了也可以回到註冊頁面

適當的讚美一下 AI也是必須的。

Cursor:
要新增註冊頁面並在登入頁面上放置一個按鈕來切換到註冊頁面,你可以按照以下步驟進行:

  • 創建註冊頁面。
  • 在登入頁面上添加切換到註冊頁面的按鈕。

以下是更新後的代碼:

  1. 註冊頁面 (Register.tsx)
import React, { useState } from 'react';
import { NativeBaseProvider, Box, Input, Button, Text } from 'native-base';

const Register = ({ onBack }) => {
    const [username, setUsername] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [confirmPassword, setConfirmPassword] = useState('');

    const handleRegister = () => {
        console.log('註冊中...', { username, email, password, confirmPassword });
    };

    return (
        <NativeBaseProvider>
            <Box flex={1} justifyContent="center" padding={4}>
                <Text fontSize="2xl" textAlign="center" marginBottom={4}>註冊</Text>
                <Input
                    placeholder="使用者名字"
                    value={username}
                    onChangeText={setUsername}
                    marginBottom={3}
                />
                <Input
                    placeholder="信箱"
                    value={email}
                    onChangeText={setEmail}
                    marginBottom={3}
                />
                <Input
                    placeholder="密碼"
                    type="password"
                    value={password}
                    onChangeText={setPassword}
                    marginBottom={3}
                />
                <Input
                    placeholder="確認密碼"
                    type="password"
                    value={confirmPassword}
                    onChangeText={setConfirmPassword}
                    marginBottom={3}
                />
                <Button onPress={handleRegister}>
                    註冊
                </Button>
                <Button onPress={onBack} marginTop={3}>
                    返回登入
                </Button>
            </Box>
        </NativeBaseProvider>
    );
};

export default Register;
  1. 更新 App.tsx
import React, { useState } from 'react';
import Login from './components/Login';
import Register from './components/Register';
import { NativeBaseProvider } from 'native-base';

const App = () => {
    const [isRegistering, setIsRegistering] = useState(false);

    return (
        <NativeBaseProvider>
            {isRegistering ? (
                <Register onBack={() => setIsRegistering(false)} />
            ) : (
                <Login onRegister={() => setIsRegistering(true)} />
            )}
        </NativeBaseProvider>
    );
};

export default App;
  1. 更新 Login.tsx
import React, { useState } from 'react';
import { NativeBaseProvider, Box, Input, Button, Text } from 'native-base';

const Login = ({ onRegister }) => {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');

    const handleLogin = () => {
        console.log('登入中...', { username, password });
    };

    return (
        <Box flex={1} justifyContent="center" padding={4}>
            <Text fontSize="2xl" textAlign="center" marginBottom={4}>登入</Text>
            <Input
                placeholder="用戶名"
                value={username}
                onChangeText={setUsername}
                marginBottom={3}
            />
            <Input
                placeholder="密碼"
                type="password"
                value={password}
                onChangeText={setPassword}
                marginBottom={3}
            />
            <Button onPress={handleLogin}>
                登入
            </Button>
            <Button onPress={onRegister} marginTop={3}>
                註冊
            </Button>
        </Box>
    );
};

export default Login;

說明

  • 註冊頁面:新增了 Register 組件,包含使用者名字、信箱、密碼和確認密碼的輸入框,並提供返回登入頁面的按鈕。
  • App.tsx:使用 isRegistering 狀態來控制顯示登入頁面或註冊頁面。
  • Login.tsx:新增了 onRegister 屬性,當用戶點擊註冊按鈕時,會切換到註冊頁面。

這樣就完成了登入和註冊頁面的切換功能!


結果

接著看我們的模擬機,登入頁面下面多了一個註冊按鈕。點下去後會到註冊頁面,註冊頁面下面還有返回登入按鈕。確實符合我們的需求。

登入頁面 註冊頁面

下一步

App router 修改:

APP.tsx

 .....
            {isRegistering ? (
                <Register onBack={() => setIsRegistering(false)} />
            ) : (
                <Login onRegister={() => setIsRegistering(true)} />
            )}
....

通常我們再處理不同頁面的切換不會用一個變數的 boolean直來切換,畢竟以後不會只有兩頁切換。通常會使用Router的形式來做切換。

Router 的概念是指在應用程式中負責管理和控制不同 URL 對應的頁面或元件,讓使用者能夠在應用中進行不同路徑的導航。

用 Router 切分頁面

上面提到頁面的切分應該要使用 Router。一樣請 AI 幫我們修改

提問

我想把不同的頁面變成有 router 的概念切換,因為之後會有更多頁面

Curosr:

要在 React Native 中實現路由功能,你可以使用 react-navigation 套件。這樣可以方便地在不同頁面之間進行切換。以下是如何設置和使用 react-navigation 的步驟:

1. 安裝 react-navigation 和相關依賴

首先,安裝 react-navigation 和必要的依賴:

npm install @react-navigation/native @react-navigation/native-stack

npm install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

2. 設置導航容器

在你的 App.tsx 中設置導航容器和堆疊導航:

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import Login from './components/Login';
import Register from './components/Register';
import { NativeBaseProvider } from 'native-base';

const Stack = createNativeStackNavigator();

const App = () => {
    return (
        <NativeBaseProvider>
            <NavigationContainer>
                <Stack.Navigator initialRouteName="Login">
                    <Stack.Screen name="Login" component={Login} />
                    <Stack.Screen name="Register" component={Register} />
                </Stack.Navigator>
            </NavigationContainer>
        </NativeBaseProvider>
    );
};

export default App;

3. 更新 Login 和 Register 組件

在 Login.tsx 和 Register.tsx 中,使用 navigation 來切換頁面。

更新 Login.tsx

import React, { useState } from 'react';
import { NativeBaseProvider, Box, Input, Button, Text } from 'native-base';

const Login = ({ navigation }) => {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');

    const handleLogin = () => {
        console.log('登入中...', { username, password });
    };

    return (
        <Box flex={1} justifyContent="center" padding={4}>
            <Text fontSize="2xl" textAlign="center" marginBottom={4}>登入</Text>
            <Input
                placeholder="用戶名"
                value={username}
                onChangeText={setUsername}
                marginBottom={3}
            />
            <Input
                placeholder="密碼"
                type="password"
                value={password}
                onChangeText={setPassword}
                marginBottom={3}
            />
            <Button onPress={handleLogin}>
                登入
            </Button>
            <Button onPress={() => navigation.navigate('Register')} marginTop={3}>
                註冊
            </Button>
        </Box>
    );
};

export default Login;

更新 Register.tsx

import React, { useState } from 'react';
import { NativeBaseProvider, Box, Input, Button, Text } from 'native-base';
import { Alert } from 'react-native';

const Register = ({ navigation }) => {
    const [username, setUsername] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [confirmPassword, setConfirmPassword] = useState('');
    const [error, setError] = useState('');

    const handleRegister = () => {
        setError('');

        if (!username || !email || !password || !confirmPassword) {
            setError('所有欄位都是必填的');
            return;
        }

        if (password !== confirmPassword) {
            setError('密碼和確認密碼不一致');
            return;
        }

        Alert.alert('註冊成功', '您已成功註冊!', [{ text: '確定', onPress: () => navigation.navigate('Login') }]);
    };

    return (
        <NativeBaseProvider>
            <Box flex={1} justifyContent="center" padding={4}>
                <Text fontSize="2xl" textAlign="center" marginBottom={4}>註冊</Text>
                {error ? <Text color="red.500" textAlign="center" marginBottom={3}>{error}</Text> : null}
                <Input
                    placeholder="使用者名字"
                    value={username}
                    onChangeText={setUsername}
                    marginBottom={3}
                />
                <Input
                    placeholder="信箱"
                    value={email}
                    onChangeText={setEmail}
                    marginBottom={3}
                />
                <Input
                    placeholder="密碼"
                    type="password"
                    value={password}
                    onChangeText={setPassword}
                    marginBottom={3}
                />
                <Input
                    placeholder="確認密碼"
                    type="password"
                    value={confirmPassword}
                    onChangeText={setConfirmPassword}
                    marginBottom={3}
                />
                <Button onPress={handleRegister}>
                    註冊
                </Button>
                <Button onPress={() => navigation.navigate('Login')} marginTop={3}>
                    返回登入
                </Button>
            </Box>
        </NativeBaseProvider>
    );
};

export default Register;

說明

  • 安裝依賴:安裝 react-navigation 和相關的依賴。
  • 設置導航容器:在 App.tsx 中設置 NavigationContainer 和 Stack.Navigator。
  • 使用 navigation:在 Login 和 Register 組件中使用 navigation 來切換頁面。
    這樣就可以實現頁面之間的切換,並且未來可以輕鬆地添加更多頁面!

結果

雖然畫面看起來和上一步一樣,但不一樣的點是因為程式的寫法。在 Stack.Screen 這邊就可以再加上其他頁面加上去。

const App = () => {
    return (
        <NativeBaseProvider>
            <NavigationContainer>
                <Stack.Navigator initialRouteName="Login">
                    <Stack.Screen name="Login" component={Login} />
                    <Stack.Screen name="Register" component={Register} />
                </Stack.Navigator>
            </NavigationContainer>
        </NativeBaseProvider>
    );
};

切換導航頁面的時候可以用 navigation.navigate('Login'),像這樣就是回到登入頁面。

		<Button onPress={() => navigation.navigate('Login')} marginTop={3}>
			返回登入
		</Button>

下一步

做了註冊頁面,登入頁面。我們來做一下首頁吧。登入後的首頁。

首頁

要怎麼做首頁,其實除了用打字的以外還可以直接把圖片貼上去給Cursor請他幫你做出你想要的樣子。我們可以來把之前 Figma的圖把他貼上去請他照著做。

提問

幫我加上一頁 Homepage,希望可以照上圖的設計。先假設登入後會到這頁。然後使用 native-base的套件實作。謝謝

Cursor:

要在你的 React Native 應用中添加一個首頁(Homepage),可以按照以下步驟進行。這個頁面將使用 NativeBase 套件來實現,並假設用戶在登入後會導航到這個頁面。

首先,創建一個新的檔案 Homepage.tsx,然後在這個檔案中實作首頁的設計。接著,更新 App.tsx 以包含這個新的頁面。

1. 創建 Homepage.tsx

components 資料夾中創建一個新的檔案 Homepage.tsx,並添加以下代碼:

import React from 'react';
import { Box, Button, Text } from 'native-base';

const Homepage = ({ navigation }) => {
    return (
        <Box flex={1} padding={4} justifyContent="center" alignItems="center">
            <Button width="100%" marginBottom={4} onPress={() => {/* Handle create report */}}>
                Create a new report
            </Button>
            <Button width="100%" marginBottom={4} onPress={() => {/* Handle report list */}}>
                Report list
            </Button>
            <Button width="100%" marginBottom={4} onPress={() => {/* Handle settings */}}>
                Settings
            </Button>
            <Button width="100%" onPress={() => {/* Handle help & support */}}>
                Help & Support
            </Button>
        </Box>
    );
};

export default Homepage;

2. 更新 App.tsx

接下來,更新 App.tsx 以包含新的首頁組件:

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import Login from './components/Login';
import Register from './components/Register';
import Homepage from './components/Homepage'; // 引入 Homepage
import { NativeBaseProvider } from 'native-base';

const Stack = createNativeStackNavigator();

const App = () => {
    return (
        <NativeBaseProvider>
            <NavigationContainer>
                <Stack.Navigator initialRouteName="Login">
                    <Stack.Screen name="Login" component={Login} />
                    <Stack.Screen name="Register" component={Register} />
                    <Stack.Screen name="Homepage" component={Homepage} /> {/* 添加 Homepage */}
                </Stack.Navigator>
            </NavigationContainer>
        </NativeBaseProvider>
    );
};

export default App;

3. 導航到首頁

Login.tsx 中的 handleLogin 函數中,當用戶成功登入後,導航到首頁:

const handleLogin = () => {
    console.log('登入中...', { username, password });
    // 假設登入成功
    navigation.navigate('Homepage'); // 導航到首頁
};

這樣就完成了首頁的設計和導航。當用戶登入後,將會看到你所提供的設計。


結果

可以看到點擊登入後真的出現我們想要的四個按鈕的畫面,但排版不是我們想要的樣子。我們想要兩個兩個方塊上下排列。只好再和他溝通一次。

下一步

在溝通一次導正到我們想要的結果

首頁排版

提問

我想要排版是 有四個正方形方塊,上面兩個 下面兩個 全部靠上對齊。

Cursor:

要實現四個正方形方塊的排版,上面兩個和下面兩個全部靠上對齊,可以使用 NativeBaseBoxGrid 組件來進行排版。以下是更新後的 Homepage.tsx 代碼:

更新 Homepage.tsx

import React from 'react';
import { Box, Button, Text, VStack, HStack } from 'native-base';

const Homepage = ({ navigation }) => {
    return (
        <Box flex={1} padding={4} justifyContent="flex-start" alignItems="center">
            <VStack space={4} width="100%">
                <HStack space={4} width="100%" justifyContent="space-between">
                    <Button width="48%" onPress={() => {/* Handle create report */}}>
                        Create a new report
                    </Button>
                    <Button width="48%" onPress={() => {/* Handle report list */}}>
                        Report list
                    </Button>
                </HStack>
                <HStack space={4} width="100%" justifyContent="space-between">
                    <Button width="48%" onPress={() => {/* Handle settings */}}>
                        Settings
                    </Button>
                    <Button width="48%" onPress={() => {/* Handle help & support */}}>
                        Help & Support
                    </Button>
                </HStack>
            </VStack>
        </Box>
    );
};

export default Homepage;

說明

  1. VStack: 用於垂直排列的容器,這裡用來包裹兩行的按鈕。
  2. HStack: 用於水平排列的容器,這裡用來包裹每一行的按鈕。
  3. space: 用於設置按鈕之間的間距。
  4. justifyContent="space-between": 確保每行的按鈕之間有均勻的間距。

這樣的排版會使得四個按鈕呈現為兩行,每行兩個,並且全部靠上對齊。


結果

排版對了,但不是我們想要的正方形方塊,也是啦,比較窄的那種。不過沒關係,已經達成我們的需求了。

![[Pasted image 20240924164252.png]]

結語

我們今天使用了 Cursor 幫我們使用 native-base 這個UI庫生成註冊頁面和修改了登入頁面,還有生成了登入後的首頁。

還請他加上了 Router的概念。

雖然我們只是和他提出需求後,套用程式碼檢視成果,發現現問題下一步修改。靠著這樣子的來回我們完成了我們的目標。

明天來整理一下今天用的東西吧。會用以外,也是要會懂使用的東西。因為總會遇到需要自己修改的時候。雖然我們目前還沒修改到一行程式碼。

工商時間

又到了我們最後的工商時間啦~~~。Tom有在經營 Pocast,目前比較偏向職場類的閒聊,未來Podcast預計更新與程式軟體開發相關的主題。有興趣的朋友follow一下。

可以透過以下平台收聽: Apple podcast Spotify KKBOX
Linking Tree


上一篇
[Day09] 魔法的本質:使用GPT幫你變出註冊頁面
下一篇
[Day11] 開始後端 Nest.js
系列文
30天 使用chatGPT輔助學習APP完成接案任務委託30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言