ساخت اپلیکیشن ساده آب و هوا با React Native و Expo – از صفر تا صد
React Native یک فریمورک عالی برای توسعه اپلیکیشنهای موبایل چند پلتفرمی برای گوشیهای مبتنی بر iOS و اندروید است. در این مقاله قصد داریم با همدیگر فرایند ساخت یک اپلیکیشن «کوچک» آب و هوا را با استفاده از React Native و Expo از طریق واکشی دادهها به صورت آنی مرور کنیم. اگر تاکنون هرگز با React Native کار نکردهاید، میتوانید از این راهنمای گام به گام به عنوان آغاز مسیر خود برای تبدیل شدن به یک توسعهدهنده اپلیکیشنهای موبایل استفاده و یک پروژه جالب به رزومه خود اضافه کنید.
مقدمه
اگر تجربه کار با React.js را داشته باشید، در یادگیری این راهنما هیچ مشکلی نخواهید داشت. اگر در زمینه جاوا اسکریپت یا اکوسیستم React.js یک تازهکار محسوب میشوید، پیشنهاد میکنیم پیش از ادامه مطالعه این مقاله، برای این که بتوانید مفاهیم مقدماتی این راهنما را درک کنید، سری به این آموزشها نیز بزنید:
- آموزش جاوا اسکریپت — مجموعه مقالات جامع وبلاگ فرادرس
- آموزش ریاکت (React) — مجموعه مقالات مجله فرادرس
توجه داشته باشید که React Native یک فریمورک اپلیکیشن موبایل هیبرید نیست. این فریمورک از پلی بین جاوا اسکریپت و API-های native پلتفرم مقصد استفاده میکند. برای کسب اطلاعات بیشتر در این زمینه پیشنهاد میکنیم سری به مستندات رسمی (+) React Native بزنید.
ما در این راهنما از Expo (+) استفاده خواهیم کرد که به عنوان «سریعترین روش برای ساخت اپلیکیشن» توصیف شده است. Expo یک مجموعه اوپنسورس از ابزارها و سرویسهایی است که به طور خاص زمانی که تازه وارد دنیای React Native شدهاید، بسیار به کار شما میآید. ابزار توسعهای که در این نوشته برای Expo استفاده میکنیم، Expo XDE (+) نام دارد.
فهرست پیشنیازها
- آشنایی با روش نوشتن کدهای جاوا اسکریپت
- آشنایی با React
- نصب Node.js روی سیستم محلی
- برخی دستورهای npm ساده
همه موارد مورد نیاز اینها هستند. بنابراین در ادامه فرایند توسعه اپلیکیشن خود را آغاز میکنیم.
سرآغاز
Expo XDE را پس از نصب کردن باز کنید و روی Create New Project کلیک کنید:
نام اپلیکیشن خود را وارد کرده و روی Create کلیک کنید. نام اپلیکیشن باید با حروف کوچک لاتین باشد. دلیل این مسئله آن است که رابط کاربری Expo XDE از کاراکترهای حروف بزرگ پشتیبانی نمیکند.
Expo EDE
Expo در پسزمینه از ابزار مدیریت پکیج React Native برای شبیهسازی اپلیکیشن و بارگذاری وابستگیها از فایل package.json اپلیکیشن استفاده میکند. مزیت استفاده از Expo EDE این است که لازم نیست پنجرههای ترمینال چندگانه را باز کنید و میتوانید اپلیکیشن را همزمان با توسعه روی دستگاه واقعی تست کنید. زمانی که این مرحله پایان یافت، کد منبع اپلیکیشن ایجاد میشود و میتوانیم آن را در یک شبیهساز روی سیستم محلی آغاز کنیم تا پیشنمایشی از اپلیکیشن پیشفرض به دست بیاوریم.
اگر از Mac استفاده میکنید، باید مطمئن شوید که Xcode روی سیستمتان نصب شده است. اگر از ویندوز استفاده میکنید، دستورالعملهای نصب اندروید استودیو برای اجرای شبیهساز را پیگیری کنید.
اگر میخواهید از مرحله شبیهسازی اپلیکیشن عبور کنید و آن را بدون ایجاد هر گونه فایل apk. یا ipa. روی دستگاه واقعی تست کنید، میتوانید کلاینت Expo را نصب کرده و کد QR ایجاد شده از سوی Expo XDE را اسکن کنید.
زمانی که کد منبع کار بستهبندی را انجام داد، یک پیام موفقیت در ترمینال Expo XDE نمایش مییابد:
در این زمان میتوانید ببینید که اپلیکیشن پیشفرض روی دستگاه مربوطه اجرا شده است:
پیام نمایش یافته در این صفحه کد سادهای است که از سوی App.js در ریشه اپلیکیشن ما رندر شده است:
1import React from 'react';
2import { StyleSheet, Text, View } from 'react-native';
3
4export default class App extends React.Component {
5 render() {
6 return (
7 <View style={styles.container}>
8 <Text>Minimalist Weather App</Text>
9 </View>
10 );
11 }
12}
13
14const styles = StyleSheet.create({
15 container: {
16 flex: 1,
17 backgroundColor: '#fff',
18 alignItems: 'center',
19 justifyContent: 'center'
20 }
21});
<Text> را به صورت زیر تغییر دهید:
1<Text>Minimalist Weather App</Text>
در این مرحله مشاهده میکنید که خروجی رندر میشود و اپلیکیشن به صورت خودکار و آنی بارگذاری مجدد میشود. برای مشاهده تغییرات نیازی به رفرش کردن وجود دارد.
بدین ترتیب مرحله نخست آغاز کار را انجام دادهایم. در مرحله بعدی یک پروتوتایپ استاتیک از اپلیکیشن خود میسازیم تا ببینیم چه سر و شکلی خواهد داشت.
پروتوتایپ
در این مرحله نخستین صفحه اپلیکیشن خود را توسعه میدهیم که یک صفحه بارگذاری است. بدین منظور در فایل App.js یک «حالت محلی» (local state) تعریف کنید:
1import React from 'react';
2import { StyleSheet, Text, View } from 'react-native';
3
4export default class App extends React.Component {
5 state = {
6 isLoading: false
7 };
8
9 render() {
10 const { isLoading } = this.state;
11 return (
12 <View style={styles.container}>
13 {isLoading ? null : (
14 <View>
15 <Text>Minimalist Weather App</Text>
16 </View>
17 )}
18 </View>
19 );
20 }
21}
22
23const styles = StyleSheet.create({
24 container: {
25 flex: 1,
26 backgroundColor: '#fff',
27 alignItems: 'center',
28 justifyContent: 'center'
29 }
30});
کد فوق اعلام میکند که وقتی حالت محلی شیء isLoading نادرست است، نام اپلیکیشن را باید نشان دهیم. این همان چیزی است که قصد داریم رندر کنیم. در ادامه و زمانی که دادهها را با موفقیت واکشی کردیم، به جای نمایش دادن نام اپلیکیشن، وضعیت آب و هوا را در این صفحه نمایش خواهیم داد. در حال حاضر، این پیام را بررسی میکنیم و بدین منظور نخست باید به این سؤال پاسخ دهیم که اگر اپلیکیشن ما در حالت بارگذاری باشد چه خواهد شد؟ متن پیام را به این صفحه اضافه میکنیم تا به کاربر نشان دهیم اپلیکیشن در حال واکشی کردن دادهها است.
1import React from 'react';
2import { StyleSheet, Text, View, Animated } from 'react-native';
3
4export default class App extends React.Component {
5 state = {
6 isLoading: true
7 };
8
9 render() {
10 const { isLoading } = this.state;
11 return (
12 <View style={styles.container}>
13 {isLoading ? (
14 <Text>Fetching The Weather</Text>
15 ) : (
16 <View>
17 <Text>Minimalist Weather App</Text>
18 </View>
19 )}
20 </View>
21 );
22 }
23}
24
25const styles = StyleSheet.create({
26 container: {
27 flex: 1,
28 backgroundColor: '#fff',
29 alignItems: 'center',
30 justifyContent: 'center'
31 }
32});
هنگامی که اپلیکیشن ما کار بارگذاری دادهها از API را به پایان ببرد، حالت isLoading را به صورت False تنظیم میکنیم:
صفحه نخست
ما یک کامپوننت «آب و هوا» (Weather) در مسیر components/Weather.js./ تعریف میکنیم. کد قالب برای هر صفحه شرایط جوی، یکسان خواهد بود و به دو view تقسیم میشود که یکی هدر و دیگری بدنه است. ویوی هدر آیکون شرایط جوی و دما را نشان میدهد و ویوی بدنه متن مرتبط با شرایط جوی را نمایش خواهد داد.
در Weather.js، شروع به تعریف کردن دو کانتینر درون کانتینر اصلی میکنیم که یکی headerContainer و دیگری bodyContainer است. توجه داشته باشید که کامپوننت weather به صورت یک کلاس و نه یک تابع تعریف میشود تا props را دریافت کند و از این رو میتواند یک حالت را مدیریت کند.
1import React from 'react';
2import { View, Text, Stylesheet } from 'react-native';
3
4const Weather = () => {
5 return (
6 <View style={styles.container}>
7 <View style={styles.headerContainer} />
8 <View style={styles.bodyContainer} />
9 </View>
10 );
11};
12
13const styles = StyleSheet({
14 container: {
15 flex: 1
16 },
17 headerContainer: {},
18 bodyContainer: {}
19});
20
21export default Weather;
ما از MaterialCommunityIcons استفاده خواهیم کرد که به همراه Expo به عنوان یک کتابخانه فرعی از کتابخانه humongous به نام vector-icons عرضه میشود:
1import React from 'react';
2import { View, Text, StyleSheet } from 'react-native';
3import { MaterialCommunityIcons } from '@expo/vector-icons';
4
5const Weather = () => {
6 return (
7 <View style={styles.weatherContainer}>
8 <View style={styles.headerContainer}>
9 <MaterialCommunityIcons size={48} name="weather-sunny" color={'#fff'} />
10 <Text style={styles.tempText}>Temperature˚</Text>
11 </View>
12 <View style={styles.bodyContainer}>
13 <Text style={styles.title}>So Sunny</Text>
14 <Text style={styles.subtitle}>It hurts my eyes!</Text>
15 </View>
16 </View>
17 );
18};
19
20const styles = StyleSheet.create({
21 weatherContainer: {
22 flex: 1,
23 backgroundColor: '#f7b733'
24 },
25 headerContainer: {
26 flex: 1,
27 alignItems: 'center',
28 justifyContent: 'center'
29 },
30 tempText: {
31 fontSize: 48,
32 color: '#fff'
33 },
34 bodyContainer: {
35 flex: 2,
36 alignItems: 'flex-start',
37 justifyContent: 'flex-end',
38 paddingLeft: 25,
39 marginBottom: 40
40 },
41 title: {
42 fontSize: 48,
43 color: '#fff'
44 },
45 subtitle: {
46 fontSize: 24,
47 color: '#fff'
48 }
49});
50
51export default Weather;
بدین ترتیب اپلیکیشن ما پس از اتمام مرحله پروتوتایپ به صورت زیر در خواهد آمد:
واکشی کردن دادهها
برای واکشی دادهها به صورت آنی از API نقشه Open Weather (+) استفاده میکنیم، چون کاملاً مفید و سازگار است. برای ارتباط با این API باید یک کلید API داشته باشیم، یعنی باید یک حساب کاربری در سایت ایجاد کنیم تا کلید API را دریافت کنیم. توجه داشته باشید که فعال شدن کلید API مربوط به Open Weather دستکم 10 دقیقه طول میکشد.
در وبسایت به بخش API section بروید. چنان که میبینید نیازهای ما بر اساس دادههای جوی حاضر تأمین میشود. ما قصد داریم کلید API را در فایل utils/WeatherAPIKey.js/. ذخیره کنیم.
1export const API_KEY = 'YOUR_API_KEY HERE';
طرز کار API مربوط به Open Weather چنین است که باید مختصات طول و عرض جغرافیایی را از روی مکان دستگاه به آن ارائه کنیم. سپس دادهها را از سرور آن به صورت یک شیء JSON واکشی میکنیم. زمان کار با سرور به دو چیز نیاز داریم که یکی دما و دیگری شرایط جوی است. ما باید این دو مقدار را در حالت محلی App.js ذخیره کنیم.
1import React from 'react';
2import { StyleSheet, Text, View, Animated } from 'react-native';
3
4import { API_KEY } from './utils/WeatherAPIKey';
5
6import Weather from './components/Weather';
7
8export default class App extends React.Component {
9 state = {
10 isLoading: false,
11 temperature: 0,
12 weatherCondition: null,
13 error: null
14 };
15
16 componentDidMount() {
17 navigator.geolocation.getCurrentPosition(
18 position => {
19 this.fetchWeather(position.coords.latitude, position.coords.longitude);
20 },
21 error => {
22 this.setState({
23 error: 'Error Gettig Weather Condtions'
24 });
25 }
26 );
27 }
28
29 fetchWeather(lat = 25, lon = 25) {
30 fetch(
31 `http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&APPID=${API_KEY}&units=metric`
32 )
33 .then(res => res.json())
34 .then(json => {
35 console.log(json);
36 });
37 }
38
39 render() {
40 const { isLoading } = this.state;
41 return (
42 <View style={styles.container}>
43 {isLoading ? <Text>Fetching The Weather</Text> : <Weather />}
44 </View>
45 );
46 }
47}
48
49const styles = StyleSheet.create({
50 container: {
51 flex: 1,
52 backgroundColor: '#fff'
53 }
54});
ایمپورت کلید API
در این مرحله کار خود را ایمپورت کردن کلید API که قبلاً تعریف کردیم آغاز میکنیم و سپس حالت خود را با مقادیر temperature ،weatherCondition و error بهروزرسانی میکنیم. بدین منظور از ()componentDidMount استفاده میکنیم که یک متد چرخه عمر است که به رندر مجدد اپلیکیشن پس از واکشی دادهها از سوی API کمک میکند. این متد همچنین در زمینه بهروزرسانی حالت به ما کمک خواهد کرد. ما از یک API به نام navigator نیز برای دریافت موقعیت دستگاه استفاده میکنیم. این همان جایی است که API جاوا اسکریپت از طریق یک پل با API نیتیو همکاری میکند. ما مقادیر طول و عرض جغرافیایی را به تابع سفارشی خود با نام fetchWeather ارسال میکنیم که API مربوط به Open Weather Map در آن فراخوانی میشود.
نتیجه کار در قالب JSON است و اگر آن را در کنسول لاگ کنید، میتوانید نتیجه را به صورت یک شیء JSON در ترمینال Expo مشاهده کنید که مقادیر زیادی در خود دارد. ما تنها به مقدار دما و شرایط جوی نیاز داریم و سپس حالت محلی خود را با مقادیر جدیدی که به دست آوردهایم بهروزرسانی میکنیم. units=metric& در انتهای فراخوانی API موجب میشود که دما از مقیاس کلوین به سلسیوس تبدیل شود.
1.then(json => {
2 // console.log(json);
3 this.setState({
4 temperature: json.main.temp,
5 weatherCondition: json.weather[0].main,
6 isLoading: false
7 });
اینک تنها کاری که باید انجام دهیم، ارسال این دو مقدار حالت محلی به صورت props به کامپوننت weather است و سپس آن را طوری بهروزرسانی میکنیم که آن prop-ها را دریافت کند. ابتدا در فایل App.js تغییرات زیر را ایجاد کنید:
1<Weather weather={weatherCondition} temperature={temperature} />
فایل weather.js را نیز به صورت زیر بهروزرسانی کنید:
1const Weather = ({ weather, temperature }) => {
2 return (
3 <View style={styles.weatherContainer}>
4 <View style={styles.headerContainer}>
5 <MaterialCommunityIcons size={48} name="weather-sunny" color={'#fff'} />
6 <Text style={styles.tempText}>{temperature}˚</Text>
7 </View>
8 <View style={styles.bodyContainer}>
9 <Text style={styles.title}>{weather}</Text>
10 <Text style={styles.subtitle}>It hurts my eyes!</Text>
11 </View>
12 </View>
13 );
14};
از آنجا که بخش دشوار کار که واکشی دادهها به صورت همزمان بود را انجام دادیم، اینک باید کاری کنیم که کامپوننت weather ما بر اساس مقادیری که دریافت میکند رفتاری دینامیک داشته باشد. این رفتار دینامیک با withweatherCondition مرتبط خواهد بود.
رفتار دینامیک
با استفاده از withweatherCondition میتوانیم تغییراتی را در پسزمینه، عنوان، عنوان فرعی آیکون جوی خود تعریف کنیم. کار خود را با تعریف از پیش آماده شرایط جوی در یک فایل به نام utils/WeatherConditions.js./ آغاز میکنیم:
1export const weatherConditions = {
2 Rain: {
3 color: '#005BEA',
4 title: 'Raining',
5 subtitle: 'Get a cup of coffee',
6 icon: 'weather-rainy'
7 },
8 Clear: {
9 color: '#f7b733',
10 title: 'So Sunny',
11 subtitle: 'It is hurting my eyes',
12 icon: 'weather-sunny'
13 },
14 Thunderstorm: {
15 color: '#616161',
16 title: 'A Storm is coming',
17 subtitle: 'Because Gods are angry',
18 icon: 'weather-lightning'
19 },
20 Clouds: {
21 color: '#1F1C2C',
22 title: 'Clouds',
23 subtitle: 'Everywhere',
24 icon: 'weather-cloudy'
25 },
26
27 Snow: {
28 color: '#00d2ff',
29 title: 'Snow',
30 subtitle: 'Get out and build a snowman for me',
31 icon: 'weather-snowy'
32 },
33 Drizzle: {
34 color: '#076585',
35 title: 'Drizzle',
36 subtitle: 'Partially raining...',
37 icon: 'weather-hail'
38 },
39 Haze: {
40 color: '#66A6FF',
41 title: 'Haze',
42 subtitle: 'Another name for Partial Raining',
43 icon: 'weather-hail'
44 },
45 Mist: {
46 color: '#3CD3AD',
47 title: 'Mist',
48 subtitle: "Don't roam in forests!",
49 icon: 'weather-fog'
50 }
51};
تعریف PropTypes
این شرایط جوی به وسیله API مربوط به Open Weather ارائه میشوند. سپس این فایل را در weather.js ایمپورت میکنیم. همچنین PropTypes را برای دو prop که از App.js دریافت میکنیم تعریف خواهیم کرد. به کد زیر توجه کنید:
1import React from 'react';
2import { View, Text, StyleSheet } from 'react-native';
3import { MaterialCommunityIcons } from '@expo/vector-icons';
4import PropTypes from 'prop-types';
5import { weatherConditions } from '../utils/WeatherConditions';
6
7const Weather = ({ weather, temperature }) => {
8 return (
9 <View
10 style={[
11 styles.weatherContainer,
12 { backgroundColor: weatherConditions[weather].color }
13 ]}
14 >
15 <View style={styles.headerContainer}>
16 <MaterialCommunityIcons
17 size={72}
18 name={weatherConditions[weather].icon}
19 color={'#fff'}
20 />
21 <Text style={styles.tempText}>{temperature}˚</Text>
22 </View>
23 <View style={styles.bodyContainer}>
24 <Text style={styles.title}>{weatherConditions[weather].title}</Text>
25 <Text style={styles.subtitle}>
26 {weatherConditions[weather].subtitle}
27 </Text>
28 </View>
29 </View>
30 );
31};
32
33Weather.propTypes = {
34 temperature: PropTypes.number.isRequired,
35 weather: PropTypes.string
36};
37
38const styles = StyleSheet.create({
39 weatherContainer: {
40 flex: 1
41 },
42 headerContainer: {
43 flex: 1,
44 flexDirection: 'row',
45 alignItems: 'center',
46 justifyContent: 'space-around'
47 },
48 tempText: {
49 fontSize: 72,
50 color: '#fff'
51 },
52 bodyContainer: {
53 flex: 2,
54 alignItems: 'flex-start',
55 justifyContent: 'flex-end',
56 paddingLeft: 25,
57 marginBottom: 40
58 },
59 title: {
60 fontSize: 60,
61 color: '#fff'
62 },
63 subtitle: {
64 fontSize: 24,
65 color: '#fff'
66 }
67});
68
69export default Weather;
بخش غالب کد منبع یکسان است. ما اینک برخی موارد را از طریق دسترسی به prop-های شرایط جوی به صورت دینامیک تغییر میدهیم که شامل پسزمینه، آیکون، نام شرایط جوی و عنوان فرعی آن میشود. همچنین میتوانید تغییراتی بنا به دلخواه خود در استایل اپلیکیشن ایجاد کنید تا ظاهری مینیمالتر یا زیباتر بیابد.
نکته: پیش از آن که اپلیکیشن را روی دستگاه واقعی خود اجرا کنید، باید مطمئن شوید که دسترسی اینترنت و مکان روی دستگاه برای این اپلیکیشن فعال شده است. ما در این مقاله در مورد دسترسیهای اپلیکیشن صحبت نکردیم، چون تا حدودی خارج از حیطه این مطلب به حساب میآید. برای مشاهده کد کامل این پروژه میتوانید به این ریپوی گیتهاب (+) مراجعه کنید.
اگر این نوشته برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- آموزش مقدماتی فریمورک React Native برای طراحی نرمافزارهای اندروید و iOS با زبان جاوا اسکریپت
- مجموعه آموزشهای برنامهنویسی
- ساخت یک اپلیکیشن چند پلتفرمی موبایل با React Native — به زبان ساده
- یازده کتابخانه کامپوننت کاربردی React Native برای سال 2۰1۹ — راهنمای جامع
- ۶ روش آسان برای سرعت بخشیدن به اپلیکیشن های React Native
==