Skip to main content

mobile

Mobile development with React Native, Flutter, and native patterns

Quellinformationen

Repository
miles990/claude-software-skills
Letzte Quellaktivität
15. Januar 2026 um 07:58
Erkannte Sprache von SKILL.md
Englisch
Sterne
25
Forks
6

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
5 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
mobile
description
Mobile development with React Native, Flutter, and native patterns
domain
development-stacks
version
1.0.0
tags
["react-native","flutter","ios","android","expo","mobile-ui"]
triggers
{"keywords":{"primary":["mobile","react native","flutter","ios","android","app","expo"],"secondary":["navigation","push notification","deep link","gesture","native module"]},"context_boost":["app store","play store","cross-platform","hybrid"],"context_penalty":["web","backend","desktop","server"],"priority":"high"}
# Mobile Development ## Overview Cross-platform and native mobile development patterns, frameworks, and best practices. --- ## React Native ### Component Patterns ```tsx // Functional component with hooks import React, { useState, useCallback } from 'react'; import { View, Text, FlatList, TouchableOpacity, StyleSheet, RefreshControl, } from 'react-native'; interface User { id: string; name: string; email: string; } interface UserListProps { users: User[]; onSelect: (user: User) => void; onRefresh: () => Promise<void>; } export function UserList({ users, onSelect, onRefresh }: UserListProps) { const [refreshing, setRefreshing] = useState(false); const handleRefresh = useCallback(async () => { setRefreshing(true); await onRefresh(); setRefreshing(false); }, [onRefresh]); const renderItem = useCallback(({ item }: { item: User }) => ( <TouchableOpacity style={styles.item} onPress={() => onSelect(item)} activeOpacity={0.7} > <Text style={styles.name}>{item.name}</Text> <Text style={styles.email}>{item.email}</Text> </TouchableOpacity> ), [onSelect]); const keyExtractor = useCallback((item: User) => item.id, []); return ( <FlatList data={users} renderItem={renderItem} keyExtractor={keyExtractor} refreshControl={ <RefreshControl refreshing={refreshing} onRefresh={handleRefresh} /> } ItemSeparatorComponent={() => <View style={styles.separator} />} ListEmptyComponent={ <Text style={styles.empty}>No users found</Text> } /> ); } const styles = StyleSheet.create({ item: { padding: 16, backgroundColor: '#fff', }, name: { fontSize: 16, fontWeight: '600', color: '#1a1a1a', }, email: { fontSize: 14, color: '#666', marginTop: 4, }, separator: { height: 1, backgroundColor: '#e0e0e0', }, empty: { textAlign: 'center', padding: 32, color: '#999', }, }); ``` ### Navigation ```tsx // React Navigation setup import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; // Type-safe navigation type RootStackParamList = { Home: undefined; Profile: { userId: string }; Settings: undefined; }; type TabParamList = { Feed: undefined; Search: undefined; Notifications: undefined; Account: undefined; }; const Stack = createNativeStackNavigator<RootStackParamList>(); const Tab = createBottomTabNavigator<TabParamList>(); function TabNavigator() { return ( <Tab.Navigator screenOptions={({ route }) => ({ tabBarIcon: ({ focused, color, size }) => { const iconName = { Feed: focused ? 'home' : 'home-outline', Search: focused ? 'search' : 'search-outline', Notifications: focused ? 'bell' : 'bell-outline', Account: focused ? 'person' : 'person-outline', }[route.name]; return <Icon name={iconName} size={size} color={color} />; }, tabBarActiveTintColor: '#007AFF', tabBarInactiveTintColor: '#8E8E93', })} > <Tab.Screen name="Feed" component={FeedScreen} /> <Tab.Screen name="Search" component={SearchScreen} /> <Tab.Screen name="Notifications" component={NotificationsScreen} /> <Tab.Screen name="Account" component={AccountScreen} /> </Tab.Navigator> ); } function App() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={TabNavigator} options={{ headerShown: false }} /> <Stack.Screen name="Profile" component={ProfileScreen} options={{ title: 'User Profile' }} /> <Stack.Screen name="Settings" component={SettingsScreen} /> </Stack.Navigator> </NavigationContainer> ); } // Using navigation in components import { useNavigation, useRoute } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; type ProfileScreenNavigationProp = NativeStackNavigationProp< RootStackParamList, 'Profile' >; function ProfileButton({ userId }: { userId: string }) { const navigation = useNavigation<ProfileScreenNavigationProp>(); return ( <Button title="View Profile" onPress={() => navigation.navigate('Profile', { userId })} /> ); } ``` ### State Management ```tsx // Zustand for React Native import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; import AsyncStorage from '@react-native-async-storage/async-storage'; interface AuthState { user: User | null; token: string | null; login: (email: string, password: string) => Promise<void>; logout: () => void; } const useAuthStore = create<AuthState>()( persist( (set) => ({ user: null, token: null, login: async (email, password) => { const response = await api.login(email, password); set({ user: response.user, token: response.token }); }, logout: () => { set({ user: null, token: null }); }, }), { name: 'auth-storage', storage: createJSONStorage(() => AsyncStorage), } ) ); // React Query for data fetching import { useQuery, useMutation, QueryClient } from '@tanstack/react-query'; const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 2, staleTime: 5 * 60 * 1000, }, }, }); function usePosts() { return useQuery({ queryKey: ['posts'], queryFn: () => api.getPosts(), }); } function useCreatePost() { return useMutation({ mutationFn: (newPost: CreatePostInput) => api.createPost(newPost), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['posts'] }); }, }); } ``` ### Platform-Specific Code ```tsx import { Platform, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ container: { paddingTop: Platform.OS === 'ios' ? 44 : 0, ...Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, }, android: { elevation: 4, }, }), }, }); // Platform-specific files // Button.ios.tsx // Button.android.tsx // Import as: import { Button } from './Button'; ``` --- ## Expo ### Expo Router ```tsx // app/_layout.tsx import { Stack } from 'expo-router'; export default function RootLayout() { return ( <Stack> <Stack.Screen name="(tabs)" options={{ headerShown: false }} /> <Stack.Screen name="modal" options={{ presentation: 'modal' }} /> </Stack> ); } // app/(tabs)/_layout.tsx import { Tabs } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; export default function TabLayout() { return ( <Tabs> <Tabs.Screen name="index" options={{ title: 'Home', tabBarIcon: ({ color }) => ( <Ionicons name="home" size={24} color={color} /> ), }} /> <Tabs.Screen name="profile" options={{ title: 'Profile', tabBarIcon: ({ color }) => ( <Ionicons name="person" size={24} color={color} /> ), }} /> </Tabs> ); } // app/(tabs)/index.tsx import { View, Text } from 'react-native'; import { Link } from 'expo-router'; export default function HomeScreen() { return ( <View> <Text>Home Screen</Text> <Link href="/profile">Go to Profile</Link> <Link href="/modal">Open Modal</Link> </View> ); } ``` ### Native APIs ```tsx import * as Camera from 'expo-camera'; import * as ImagePicker from 'expo-image-picker'; import * as Location from 'expo-location'; import * as Notifications from 'expo-notifications'; // Camera async function takePhoto() { const { status } = await Camera.requestCameraPermissionsAsync(); if (status !== 'granted') { Alert.alert('Permission required', 'Camera access is needed'); return; } const result = await ImagePicker.launchCameraAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, quality: 0.8, }); if (!result.canceled) { return result.assets[0].uri; } } // Location async function getCurrentLocation() { const { status } = await Location.requestForegroundPermissionsAsync(); if (status !== 'granted') { throw new Error('Location permission denied'); } const location = await Location.getCurrentPositionAsync({}); return { latitude: location.coords.latitude, longitude: location.coords.longitude, }; } // Push Notifications async function registerForPushNotifications() { const { status } = await Notifications.requestPermissionsAsync(); if (status !== 'granted') { return null; } const token = await Notifications.getExpoPushTokenAsync(); return token.data; } Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: true, }), }); ``` --- ## Flutter ### Widget Patterns ```dart // Stateless widget class UserCard extends StatelessWidget { final User user; final VoidCallback onTap; const UserCard({ super.key, required this.user, required this.onTap, }); @override Widget build(BuildContext context) { return Card( child: ListTile( leading: CircleAvatar( backgroundImage: NetworkImage(user.avatarUrl), ), title: Text(user.name), subtitle: Text(user.email), trailing: const Icon(Icons.chevron_right), onTap: onTap, ), ); } } // Stateful widget with hooks (flutter_hooks) class CounterPage extends HookWidget { @override Widget build(BuildContext context) { final count = useState(0); final controller = useAnimationController(duration: Duration(seconds: 1)); return Scaffold( body: Center( child: Text('Count: ${count.value}'), ), floatingActionButton: FloatingActionButton( onPressed: () => count.value++, child: const Icon(Icons.add), ), ); } } ```
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen