Приложение-калькулятор - это простое приложение, которое всегда доступно на каждом устройстве Android, iOS и настольном компьютере.
В этой статье мы создадим калькулятор с помощью React Native и Expo. Почему мы используем эти инструменты?
React Native - это фреймворк на основе JavaScript, который можно использовать для разработки мобильных приложений сразу на двух операционных системах - Android и iOS. React Native был впервые запущен в 2015 году компанией Facebook и имеет открытый исходный код.
Expo - это набор инструментов, библиотек и сервисов, которые вы можете использовать для упрощения кода React Native. Таким образом, вы можете запускать приложения React Native на эмуляторе Expo.
Прежде чем мы начнем создавать калькулятор, вам сначала нужно установить Node.js, React Native и Expo на свой компьютер.
Оглавление
Предварительные условия
Установите Node.js - как установить его, можно посмотреть здесь. Установите React Native - документацию по установке можно посмотреть здесь. Установите Expo - документацию по установке можно посмотреть здесь.
Шаг 1: Создание нового проекта
Первым шагом будет создание нового проекта. Используйте Expo CLI для создания кодовой базы React Native с помощью следующей команды:
$ expo init calculator-app
Затем у вас будет выбор, какой проект вы хотите запустить. Здесь мы выбираем пустой вариант и используем JavaScript, как показано ниже:
выберите шаблон проекта экспо
После этого процесс продолжится загрузкой всех зависимостей.
Шаг 2: Создание компонента кнопки
При разработке приложений с использованием React Native обязательно разбивайте компоненты пользовательского интерфейса на более мелкие компоненты, чтобы созданный вами код можно было использовать повторно.
Сначала создайте новую папку “components” для хранения кода компонентов. Первый компонент, который мы создадим, будет кнопкой, поэтому создайте новый файл Button.js. Вот исходный код компонента Button:
import { Dimensions, StyleSheet, Text, TouchableOpacity } from "react-native";
export default ({ onPress, text, size, theme }) => {
const buttonStyles = [styles.button];
const textStyles = [styles.text];
if (size === "double") {
buttonStyles.push(styles.buttonDouble);
}
if (theme === "secondary") {
buttonStyles.push(styles.buttonSecondary);
textStyles.push(styles.textSecondary);
} else if (theme === "accent") {
buttonStyles.push(styles.buttonAccent);
}
return (
<TouchableOpacity onPress={onPress} style={buttonStyles}>
<Text style={textStyles}>{text}</Text>
</TouchableOpacity>
);
};
Пояснение к коду:
В строке 3 есть четыре реквизита, которые нам нужны для создания компонента Button: onPress, text, size и theme. Каждый из реквизитов имеет функцию onPress для обработки действий на кнопках. Компонент кнопки, который мы создали, имеет 2 типа тем, secondary и accent, и 1 размер, double. Компонент кнопки использует компонент React Native по умолчанию, TouchableOpacity.
После того как мы сделаем код компонента, не забудьте добавить стилизацию для этого компонента кнопки. Вот код для стилизации компонента:
// set dimmenstion
const screen = Dimensions.get("window");
const buttonWidth = screen.width / 4;
const styles = StyleSheet.create({
button: {
backgroundColor: "#333333",
flex: 1,
height: Math.floor(buttonWidth - 10),
alignItems: "center",
justifyContent: "center",
borderRadius: Math.floor(buttonWidth),
margin: 5,
},
text: {
color: "#fff",
fontSize: 24,
},
textSecondary: {
color: "#060606",
},
buttonDouble: {
width: screen.width / 2 - 10,
flex: 0,
alignItems: "flex-start",
paddingLeft: 40,
},
buttonSecondary: {
backgroundColor: "#a6a6a6",
},
buttonAccent: {
backgroundColor: "#ffc107",
},
});
Итак, полный код нашего компонента кнопки выглядит следующим образом:
import { Dimensions, StyleSheet, Text, TouchableOpacity } from "react-native";
export default ({ onPress, text, size, theme }) => {
const buttonStyles = [styles.button];
const textStyles = [styles.text];
if (size === "double") {
buttonStyles.push(styles.buttonDouble);
}
if (theme === "secondary") {
buttonStyles.push(styles.buttonSecondary);
textStyles.push(styles.textSecondary);
} else if (theme === "accent") {
buttonStyles.push(styles.buttonAccent);
}
return (
<TouchableOpacity onPress={onPress} style={buttonStyles}>
<Text style={textStyles}>{text}</Text>
</TouchableOpacity>
);
};
// set dimmenstion
const screen = Dimensions.get("window");
const buttonWidth = screen.width / 4;
const styles = StyleSheet.create({
button: {
backgroundColor: "#333333",
flex: 1,
height: Math.floor(buttonWidth - 10),
alignItems: "center",
justifyContent: "center",
borderRadius: Math.floor(buttonWidth),
margin: 5,
},
text: {
color: "#fff",
fontSize: 24,
},
textSecondary: {
color: "#060606",
},
buttonDouble: {
width: screen.width / 2 - 10,
flex: 0,
alignItems: "flex-start",
paddingLeft: 40,
},
buttonSecondary: {
backgroundColor: "#a6a6a6",
},
buttonAccent: {
backgroundColor: "#ffc107",
},
});
Шаг 3: Создание компонента Row
Следующим компонентом, который мы создадим, будет компонент Row. Этот компонент полезен для создания строк, когда мы хотим обработать макеты.
Вот код для компонента Row и его код стилизации:
import { StyleSheet, View } from "react-native";
const Row = ({ children }) => {
return <View style={styles.container}>{children}</View>;
};
// create styles of Row
const styles = StyleSheet.create({
container: {
flexDirection: "row",
},
});
export default Row;
Вот что происходит:
В компоненте row есть 1 параметр, который нам нужен: children.Компонент row использует компонент View по умолчанию из React Native.flexDirection: “row” в этом стиле используется для того, чтобы сделать макет рядом.Шаг 4: Создание логики калькулятора
Создайте новую папку util и новый файл calculator.js. Здесь мы создадим логику функций в приложении калькулятора, которую позже реализуем в файле App.js. Вот полный код:
export const initialState = {
currentValue: "0",
operator: null,
previousValue: null,
};
export const handleNumber = (value, state) => {
if (state.currentValue === "0") {
return { currentValue: `${value}` };
}
return {
currentValue: `${state.currentValue}${value}`,
};
};
const handleEqual = (state) => {
const { currentValue, previousValue, operator } = state;
const current = parseFloat(currentValue);
const previous = parseFloat(previousValue);
const resetState = { operator: null, previousValue: null };
switch (operator) {
case "+":
return {
currentValue: `${previous + current}`,
...resetState,
};
case "-":
return {
currentValue: `${previous - current}`,
...resetState,
};
case "*":
return {
currentValue: `${previous * current}`,
...resetState,
};
case "/":
return {
currentValue: `${previous / current}`,
...resetState,
};
default:
return state;
}
};
// calculator function
const calculator = (type, value, state) => {
switch (type) {
case "number":
return handleNumber(value, state);
case "clear":
return initialState;
case "posneg":
return {
currentValue: `${parseFloat(state.currentValue) * -1}`,
};
case "percentage":
return {
currentValue: `${parseFloat(state.currentValue) * 0.01}`,
};
case "operator":
return {
operator: value,
previousValue: state.currentValue,
currentValue: "0",
};
case "equal":
return handleEqual(state);
default:
return state;
}
};
export default calculator;
И вот что происходит:
initialState используется, чтобы дать значение по умолчанию нашему приложению калькулятора.Функция handleNumber служит для возврата значения калькулятора и имеет 2 реквизита - value и state.Функция handle Equal служит для обработки заданного значения каждого математического оператора и возвращает его значение.Функция calculator проверяет каждый заданный оператор. Например, если число вызывает функцию handleNumber, то если она чиста, то вернет значение состояния по умолчанию из initiaState, и так далее.Шаг 5: Рефакторинг файла App.js
После того как мы создали все компоненты и логический процесс, следующим шагом будет внесение изменений в код в файле App.js. Вот полный код:
import React, { Component } from "react";
import { SafeAreaView, StyleSheet, Text, View } from "react-native";
import Button from "./components/Button";
import Row from "./components/Row";
import calculator, { initialState } from "./util/calculator";
// create class component of App
export default class App extends Component {
state = initialState;
// handle tap method
HandleTap = (type, value) => {
this.setState((state) => calculator(type, value, state));
};
// render method
render() {
return (
<View style={styles.container}>
{/* Status bae here */}
<SafeAreaView>
<Text style={styles.value}>
{parseFloat(this.state.currentValue).toLocaleString()}
</Text>
{/* Do create componentRow */}
<Row>
<Button
text="C"
theme="secondary"
onPress={() => this.HandleTap("clear")}
/>
<Button
text="+/-"
theme="secondary"
onPress={() => this.HandleTap("posneg")}
/>
<Button
text="%"
theme="secondary"
onPress={() => this.HandleTap("percentage")}
/>
<Button
text="/"
theme="accent"
onPress={() => this.HandleTap("operator", "/")}
/>
</Row>
{/* Number */}
<Row>
<Button text="7" onPress={() => this.HandleTap("number", 7)} />
<Button text="8" onPress={() => this.HandleTap("number", 8)} />
<Button text="9" onPress={() => this.HandleTap("number", 9)} />
<Button
text="X"
theme="accent"
onPress={() => this.HandleTap("operator", "*")}
/>
</Row>
<Row>
<Button text="5" onPress={() => this.HandleTap("number", 5)} />
<Button text="6" onPress={() => this.HandleTap("number", 6)} />
<Button text="7" onPress={() => this.HandleTap("number", 7)} />
<Button
text="-"
theme="accent"
onPress={() => this.HandleTap("operator", "-")}
/>
</Row>
<Row>
<Button text="1" onPress={() => this.HandleTap("number", 1)} />
<Button text="2" onPress={() => this.HandleTap("number", 2)} />
<Button text="3" onPress={() => this.HandleTap("number", 3)} />
<Button
text="+"
theme="accent"
onPress={() => this.HandleTap("operator", "+")}
/>
</Row>
<Row>
<Button text="0" onPress={() => this.HandleTap("number", 0)} />
<Button text="." onPress={() => this.HandleTap("number", ".")} />
<Button
text="="
theme="primary"
onPress={() => this.HandleTap("equal", "=")}
/>
</Row>
</SafeAreaView>
</View>
);
}
}
// create styles of app
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#202020",
justifyContent: "flex-end",
},
value: {
color: "#fff",
fontSize: 42,
textAlign: "right",
marginRight: 20,
marginBottom: 10,
},
});
Несколько кратких заметок:
handleTap - это созданная нами функция, которая предназначена для предоставления значений состояния и вызова utils/calculator.Здесь мы вызываем два компонента, Button и Row, для оформления внешнего вида калькулятора, таких как его числа, математические операции и процесс вычисления.Шаг 6: Запуск приложения
На этом шаге мы попытаемся запустить приложение калькулятора на устройстве или можем использовать эмулятор. Здесь я использую симулятор iPhone из MacOS. Выполните приведенную ниже команду, чтобы запустить программу:
$ yarn ios
Здесь запущенный процесс использует Expo, как показано ниже:

Если процесс компиляции завершен, то отображение приложения калькулятора, которое мы запрограммировали, будет выглядеть следующим образом:
Заключение
Этого достаточно для данной статьи. Вы узнали о стилизации, компонентах, реквизитах и состояниях в React Native и создали функциональное приложение-калькулятор.
Если вам нужен полный исходный код, вы можете посетить мой репозиторий GitHub здесь: https://github.com/bangadam/calculator-app.
Спасибо за прочтение!