🦔
ネイティブアプリ開発日記(8/100)支出・収入の切替
前回はこちら
- 前回作成した家計簿アプリの機能
- 画面遷移
- 画面下部のタブバーで遷移できる
- 支出登録機能
- 年月日を押下すると、カレンダーが表示されて選択できる
- 金額を押下すると、テンキーが表示されて入力できる
- カテゴリを押下すると、リストが表示されて選択できる
- 支出参照機能
- 年月毎の集計を参照できる
- 一覧を参照できる
- 支出編集機能
- 支出(1件毎)を編集できる
- 支出(1件毎)を削除できる
- 画面遷移
- 今回作成する機能
- 支出の他に収入を管理できる
入力画面はこちら。セグメントコントロールで支出・収入を切替可能。

セグメントで「収入」を選択したのがこちら。

カテゴリは支出・収入で分ける。

レポート画面も支出・収入で切替可能。

実装面の学び1. 入力画面での支出・収入の切替
- 支出・収入の選択を状態として定義
- const [type, setType] = useState('expense');
- セグメントコントロールでの選択
- <SegmentedControl values={['支出', '収入']} onChange={(event) => { setType(・・・); }
- 入力欄のカテゴリも支出・収入で個別に管理
- const categories = type === 'expense' ? expenseCategories : incomeCategories;
InputScreen.js
import SegmentedControl from '@react-native-segmented-control/segmented-control';
import { expenseCategories, incomeCategories } from '../parameters/categories';
// 入力変数の初期値
const [type, setType] = useState('expense');
const [category, setCategory] = useState('-');
// 支出・収入の切替
const categories = type === 'expense' ? expenseCategories : incomeCategories;
const typeText = type === 'expense' ? '支出' : '収入';
return (
<View style={styles.container}>
<SegmentedControl
values={['支出', '収入']}
selectedIndex={type === 'expense' ? 0 : 1 }
onChange={(event) => {
const index = event.nativeEvent.selectedSegmentIndex;
setType(index === 0 ? 'expense' : 'income');
setCategory('-');
}}
/>
<Picker
selectedValue={category}
onValueChange={(itemValue) => setCategory(itemValue)}
>
{categories.map((cat) => (
<Picker.Item key={cat.value} label={cat.label} value={cat.value} />
))}
</Picker>
</View>
);
実装面の学び2. レポート画面での支出・収入の切替
- 支出・収入の選択を状態として定義
- const [type, setType] = useState('expense');
- 画面に表示するデータを支出・収入で切替
- const filtered = data.filter((item) => item.type === type);
SummaryScreen.js
import SegmentedControl from '@react-native-segmented-control/segmented-control';
import { loadExpense } from '../utils/storage';
import { calculateMonthlyTotals } from '../utils/aggregate';
const isFocused = useIsFocused();
const [transactions, setTransactions] = useState([]);
const [monthlyTotals, setMonthlyTotals] = useState([]);
const [type, setType] = useState('expense');
const typeText = type === 'expense' ? '支出' : '収入';
useEffect(() => {
const fetchData = async () => {
const data = await loadExpense();
const filtered = data.filter((item) => item.type === type);
setTransactions(filtered);
setMonthlyTotals(calculateMonthlyTotals(filtered));
};
if (isFocused) {
fetchData();
}
}, [isFocused, type]);
return (
<View>
<SegmentedControl
values={['支出', '収入']}
selectedIndex={type === 'expense' ? 0 : 1 }
onChange={(event) => {
const index = event.nativeEvent.selectedSegmentIndex;
setType(index === 0 ? 'expense' : 'income');
}}
/>
<Text>月ごとの合計{typeText}</Text>
{Object.entries(monthlyTotals).map(([month, {expense, income}]) => (
<Text key={month}>
{month}: {type === 'expense' ? `¥${expense}` : `¥${income}`}
</Text>
))}
<Text>{typeText}一覧</Text>
<FlatList
data={transactions}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View>
<Text>{item.date} - {item.category}: ¥{item.amount}</Text>
</View>
)}
/>
</View>
)
Discussion