mobile-development について
mobile-developmentは、React NativeやFlutterでモバイル画面を開発する人向けの資料です。コンポーネント、ナビゲーション、レスポンシブレイアウト、プラットフォーム差異、よくある問題を扱います。
主な機能
- 01React Nativeのコンポーネントとリストのパターン
- 02型付きReact Nativeナビゲーション
- 03Flutterのウィジェット構成
- 04画面サイズに応じたレイアウト
- 05プラットフォーム固有のスタイルと動作
- 06モバイル開発チェックリスト
活用シーン
- 01React Native画面の構成
- 02型付きアプリナビゲーションの設定
- 03スマートフォンやタブレット向けのレイアウト調整
- 04モバイルコードのよくある問題の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ rohitg00/ awesome-claude-code-toolkit --skill mobile-developmentインストール後、Claude Code で/mobile-developmentを入力して読み込まれたか確認します。
詳細
モバイル開発
React Native コンポーネントの構造
import { View, Text, FlatList, StyleSheet, Platform } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
interface Product {
id: string;
name: string;
price: number;
image: string;
}
function ProductList({ products }: { products: Product[] }) {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={products}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ProductCard product={item} />}
contentContainerStyle={styles.list}
ItemSeparatorComponent={() => <View style={styles.separator} />}
ListEmptyComponent={<EmptyState message="No products found" />}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
},
list: {
padding: 16,
},
separator: {
height: 12,
},
});
スクロール可能なリストには FlatList を使用してください(.map() を使った ScrollView は使用しないでください)。大規模なリストでは windowSize と maxToRenderPerBatch を設定してください。
React Native のナビゲーション
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
type RootStackParams = {
Tabs: undefined;
ProductDetail: { productId: string };
Cart: undefined;
};
const Stack = createNativeStackNavigator<RootStackParams>();
const Tab = createBottomTabNavigator();
function TabNavigator() {
return (
<Tab.Navigator screenOptions={{ headerShown: false }}>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Search" component={SearchScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Tabs" component={TabNavigator} options={{ headerShown: false }} />
<Stack.Screen name="ProductDetail" component={ProductDetailScreen} />
<Stack.Screen name="Cart" component={CartScreen} options={{ presentation: "modal" }} />
</Stack.Navigator>
</NavigationContainer>
);
}
Flutter ウィジェットのパターン
class ProductCard extends StatelessWidget {
final Product product;
final VoidCallback onTap;
const ProductCard({
super.key,
required this.product,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Card(
elevation: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(8)),
child: Image.network(
product.imageUrl,
height: 200,
width: double.infinity,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => const Icon(Icons.broken_image, size: 64),
),
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(product.name, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 4),
Text("\$${product.price.toStringAsFixed(2)}",
style: Theme.of(context).textTheme.bodyLarge),
],
),
),
],
),
),
);
}
}
レスポンシブレイアウト
import { useWindowDimensions } from "react-native";
function useResponsive() {
const { width } = useWindowDimensions();
return {
isPhone: width < 768,
isTablet: width >= 768 && width < 1024,
isDesktop: width >= 1024,
columns: width < 768 ? 1 : width < 1024 ? 2 : 3,
};
}
function ProductGrid({ products }: { products: Product[] }) {
const { columns } = useResponsive();
return (
<FlatList
data={products}
numColumns={columns}
key={columns}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ flex: 1, maxWidth: `${100 / columns}%`, padding: 8 }}>
<ProductCard product={item} />
</View>
)}
/>
);
}
プラットフォーム固有のコード
import { Platform } from "react-native";
const styles = StyleSheet.create({
shadow: Platform.select({
ios: {
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
android: {
elevation: 4,
},
default: {},
}),
});
アンチパターン
- 動的なリストに
.map()を使ったScrollViewを使用する(FlatListまたはSectionListを使用してください) - コンポーネントの近くに配置せず、すべての状態をグローバルストアに保存する
- セーフエリア(ノッチ、ステータスバー、ホームインジケーター)に対応しない
- レンダリングのたびにインラインスタイルを使う(
StyleSheet.createで定義してください) - 重い計算で JS スレッドをブロックする(
InteractionManagerを使用してください) - プラットフォーム固有の UX の慣習を無視する(iOS の戻るスワイプ、Android の戻るボタン)
チェックリスト
- スクロール可能なすべてのリストで
keyExtractorを指定したFlatListを使用している - ナビゲーションのルートパラメーターを TypeScript で型付けしている
-
SafeAreaViewでセーフエリアのインセットに対応している - スタイルをインラインオブジェクトではなく
StyleSheet.createで定義している - レスポンシブレイアウトがスマートフォン、タブレット、デスクトップに適応している
-
Platform.selectでプラットフォーム固有のスタイルに対応している - 画像をキャッシュし、エラー状態と読み込み状態を表示している
- 重い計算を JS スレッドからオフロードしている
コメント
Markdown 対応