chore(skills): 引入 agent skills 与锁文件
- superpowers(14 个):流程纪律(brainstorming/TDD/debug/review 等) - mobile-android-design:Compose + Material 3 设计实现 - android-clean-architecture:分层/UseCase/Repository/Hilt/Room - compose-multiplatform-patterns / kotlin-coroutines-flows:KMP 与协程 - skills-lock.json:可复现锁文件;陈旧副本 agent/ 加入 .gitignore
This commit is contained in:
337
.agents/skills/mobile-android-design/references/details.md
Normal file
337
.agents/skills/mobile-android-design/references/details.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# mobile-android-design — detailed sections
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. Material Design 3 Principles
|
||||
|
||||
**Personalization**: Dynamic color adapts UI to user's wallpaper
|
||||
**Accessibility**: Tonal palettes ensure sufficient color contrast
|
||||
**Large Screens**: Responsive layouts for tablets and foldables
|
||||
|
||||
**Material Components:**
|
||||
|
||||
- Cards, Buttons, FABs, Chips
|
||||
- Navigation (rail, drawer, bottom nav)
|
||||
- Text fields, Dialogs, Sheets
|
||||
- Lists, Menus, Progress indicators
|
||||
|
||||
### 2. Jetpack Compose Layout System
|
||||
|
||||
**Column and Row:**
|
||||
|
||||
```kotlin
|
||||
// Vertical arrangement with alignment
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
Text(
|
||||
text = "Title",
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Text(
|
||||
text = "Subtitle",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
// Horizontal arrangement with weight
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Default.Star, contentDescription = null)
|
||||
Text("Featured")
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
TextButton(onClick = {}) {
|
||||
Text("View All")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Lazy Lists and Grids:**
|
||||
|
||||
```kotlin
|
||||
// Lazy column with sticky headers
|
||||
LazyColumn {
|
||||
items.groupBy { it.category }.forEach { (category, categoryItems) ->
|
||||
stickyHeader {
|
||||
Text(
|
||||
text = category,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.padding(16.dp),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
}
|
||||
items(categoryItems) { item ->
|
||||
ItemRow(item = item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adaptive grid
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(minSize = 150.dp),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(items) { item ->
|
||||
ItemCard(item = item)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Navigation Patterns
|
||||
|
||||
**Bottom Navigation:**
|
||||
|
||||
```kotlin
|
||||
@Composable
|
||||
fun MainScreen() {
|
||||
val navController = rememberNavController()
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
NavigationBar {
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentDestination = navBackStackEntry?.destination
|
||||
|
||||
NavigationDestination.entries.forEach { destination ->
|
||||
NavigationBarItem(
|
||||
icon = { Icon(destination.icon, contentDescription = null) },
|
||||
label = { Text(destination.label) },
|
||||
selected = currentDestination?.hierarchy?.any {
|
||||
it.route == destination.route
|
||||
} == true,
|
||||
onClick = {
|
||||
navController.navigate(destination.route) {
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = NavigationDestination.Home.route,
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
composable(NavigationDestination.Home.route) { HomeScreen() }
|
||||
composable(NavigationDestination.Search.route) { SearchScreen() }
|
||||
composable(NavigationDestination.Profile.route) { ProfileScreen() }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Navigation Drawer:**
|
||||
|
||||
```kotlin
|
||||
@Composable
|
||||
fun DrawerNavigation() {
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
ModalNavigationDrawer(
|
||||
drawerState = drawerState,
|
||||
drawerContent = {
|
||||
ModalDrawerSheet {
|
||||
Spacer(Modifier.height(12.dp))
|
||||
Text(
|
||||
"App Name",
|
||||
modifier = Modifier.padding(16.dp),
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
HorizontalDivider()
|
||||
|
||||
NavigationDrawerItem(
|
||||
icon = { Icon(Icons.Default.Home, null) },
|
||||
label = { Text("Home") },
|
||||
selected = true,
|
||||
onClick = { scope.launch { drawerState.close() } }
|
||||
)
|
||||
NavigationDrawerItem(
|
||||
icon = { Icon(Icons.Default.Settings, null) },
|
||||
label = { Text("Settings") },
|
||||
selected = false,
|
||||
onClick = { }
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Home") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { scope.launch { drawerState.open() } }) {
|
||||
Icon(Icons.Default.Menu, contentDescription = "Menu")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Content(modifier = Modifier.padding(innerPadding))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Material 3 Theming
|
||||
|
||||
**Color Scheme:**
|
||||
|
||||
```kotlin
|
||||
// Dynamic color (Android 12+)
|
||||
val dynamicColorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context)
|
||||
else dynamicLightColorScheme(context)
|
||||
} else {
|
||||
if (darkTheme) DarkColorScheme else LightColorScheme
|
||||
}
|
||||
|
||||
// Custom color scheme
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Color(0xFF6750A4),
|
||||
onPrimary = Color.White,
|
||||
primaryContainer = Color(0xFFEADDFF),
|
||||
onPrimaryContainer = Color(0xFF21005D),
|
||||
secondary = Color(0xFF625B71),
|
||||
onSecondary = Color.White,
|
||||
tertiary = Color(0xFF7D5260),
|
||||
onTertiary = Color.White,
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
)
|
||||
```
|
||||
|
||||
**Typography:**
|
||||
|
||||
```kotlin
|
||||
val AppTypography = Typography(
|
||||
displayLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 57.sp,
|
||||
lineHeight = 64.sp
|
||||
),
|
||||
headlineMedium = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 28.sp,
|
||||
lineHeight = 36.sp
|
||||
),
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp
|
||||
),
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp
|
||||
),
|
||||
labelMedium = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
### 5. Component Examples
|
||||
|
||||
**Cards:**
|
||||
|
||||
```kotlin
|
||||
@Composable
|
||||
fun FeatureCard(
|
||||
title: String,
|
||||
description: String,
|
||||
imageUrl: String,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
Column {
|
||||
AsyncImage(
|
||||
model = imageUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(180.dp),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = description,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Buttons:**
|
||||
|
||||
```kotlin
|
||||
// Filled button (primary action)
|
||||
Button(onClick = { }) {
|
||||
Text("Continue")
|
||||
}
|
||||
|
||||
// Filled tonal button (secondary action)
|
||||
FilledTonalButton(onClick = { }) {
|
||||
Icon(Icons.Default.Add, null)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text("Add Item")
|
||||
}
|
||||
|
||||
// Outlined button
|
||||
OutlinedButton(onClick = { }) {
|
||||
Text("Cancel")
|
||||
}
|
||||
|
||||
// Text button
|
||||
TextButton(onClick = { }) {
|
||||
Text("Learn More")
|
||||
}
|
||||
|
||||
// FAB
|
||||
FloatingActionButton(
|
||||
onClick = { },
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
) {
|
||||
Icon(Icons.Default.Add, contentDescription = "Add")
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user