Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Now in Android アプリ解説 / Now in Android App

Now in Android アプリ解説 / Now in Android App

Yuki Anzai

June 05, 2022
Tweet

More Decks by Yuki Anzai

Other Decks in Technology

Transcript

  1. plugins { `kotlin-dsl` } group = "com.google.samples.apps.nowinandroid.buildlogic" java { sourceCompatibility

    = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } dependencies { implementation(libs.android.gradlePlugin) implementation(libs.kotlin.gradlePlugin) implementation(libs.spotless.gradlePlugin) } gradlePlugin { plugins { … } } ʁ implementation("com.android.tools.build:gradle:7.2.1") ͷΑ͏ͳจࣈྻࢦఆͰ͸ͳ͍ MJCTBESPJEHSBEMF1MVHJO͸Ͳ͜Λࢦ͍ͯ͠Δʁ
  2. [versions] accompanist = "0.24.8-beta" androidDesugarJdkLibs = "1.1.5" androidGradlePlugin = "7.2.1"

    androidxActivity = "1.4.0" androidxAppCompat = "1.3.0" androidxCompose = "1.2.0-beta02" … [libraries] accompanist-flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanist" } android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } … [plugins] protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
  3. [versions] accompanist = "0.24.8-beta" androidDesugarJdkLibs = "1.1.5" androidGradlePlugin = "7.2.1"

    androidxActivity = "1.4.0" androidxAppCompat = "1.3.0" androidxCompose = "1.2.0-beta02" … [libraries] accompanist-flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanist" } android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } … [plugins] protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
  4. … dependencies { implementation(libs.android.gradlePlugin) … } dependencyResolutionManagement { … versionCatalogs

    { create("libs") { from(files("...")) } } } … … [libraries] … android-gradlePlugin = { … } …
  5. … gradlePlugin { plugins { register("androidApplicationCompose") { id = "nowinandroid.android.application.compose"

    implementationClass = "AndroidApplicationComposeConventionPlugin" } register("androidApplication") { id = "nowinandroid.android.application" implementationClass = "AndroidApplicationConventionPlugin" } … } }
  6. class AndroidApplicationConventionPlugin : Plugin<Project> { override fun apply(target: Project) {

    with(target) { with(pluginManager) { apply("com.android.application") apply("org.jetbrains.kotlin.android") } extensions.configure<BaseAppModuleExtension> { configureKotlinAndroid(this) defaultConfig.targetSdk = 32 } } } } ͜͜Ͱࢦఆ͞Ε͍ͯΔ BOESPJECMPDLͰͷઃఆͷڞ௨Խ
  7. internal fun Project.configureKotlinAndroid( commonExtension: CommonExtension<*, *, *, *>, ) {

    commonExtension.apply { compileSdk = 32 defaultConfig { minSdk = 21 } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 isCoreLibraryDesugaringEnabled = true } kotlinOptions { … } } … }
  8. BQQOJBDBUBMPH app-nia-catalog core-ui core-model data class Author( val id: String,

    val name: String, val imageUrl: String, val twitter: String, val mediumPage: String, val bio: String, )
  9. BQQOJBDBUBMPH app-nia-catalog core-ui core-model @Composable fun NiaFilledButton( onClick: () ->

    Unit, modifier: Modifier = Modifier, enabled: Boolean = true, small: Boolean = false, … content: @Composable RowScope.() -> Unit ) { Button( … ) }
  10. BQQ

  11. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢
  12. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢
  13. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢ w *0༻ͷ$PSPVUJOF%JTQBUDIFS w TFBMFEJOUFSGBDF3FTVMUPVU5
  14. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢ 1SPUPEBUBTUPSFͰGPMMPXͨ͠ 5PQJDͱ"VUIPSͷJEΛอଘ
  15. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢ 3FUPSpUΛ࢖ͬͯωοτϫʔΫ͔Β σʔλΛऔಘ
  16. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢ SPPNʹ5PQJD΍"VUIPSͳͲͷ σʔλΛอଘ
  17. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢ 5PQJD "VUIPS /FXT ͷ3FQPTJUPSZ࣮૷
  18. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢ 8PSL.BOBHFSΛ࢖ͬͯΞϓϦ ىಈ࣌ʹσʔλΛಉظ
  19. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢ /BWJHBUJPOͷભҠઌΛදݱ͢Δ JOUFSGBDF
  20. App core-ui core-navigation feature-author sync feature-interests feature-foryou feature-topic core-model core-common

    core-data core- datastore core- database core- network ˎUFTU༻ͷNPEVMF͸ আ͍͍ͯ·͢
  21. 5PQJD/BWJHBUJPOLU object TopicDestination : NiaNavigationDestination { override val route =

    "topic_route" override val destination = "topic_destination" const val topicIdArg = "topicId" } fun NavGraphBuilder.topicGraph( onBackClick: () -> Unit ) { composable( route = "${TopicDestination.route}/{${TopicDestination.topicIdArg}}", arguments = listOf( navArgument(TopicDestination.topicIdArg) { type = NavType.StringType } ) ) { TopicRoute(onBackClick = onBackClick) } } /BW(SBQIʹ͜ͷGFBUVSFͷը໘Λ ભҠઌͱͯ͠௥Ճ͢ΔͨΊͷ֦ுؔ਺
  22. @Composable fun NiaNavHost( … ) { NavHost( navController = navController,

    startDestination = startDestination, modifier = modifier, ) { … interestsGraph( …, nestedGraphs = { topicGraph(onBackClick = { navController.popBackStack() }) authorGraph(onBackClick = { navController.popBackStack() }) } ) } }
  23. .BUFSJBMXJUI+FUQBDL$PNQPTF w EZOBNJDDPMPSTDIFNFʢ"OESPJEҎ߱ʣରԠ @Composable fun NiaTheme( darkTheme: Boolean = isSystemInDarkTheme(),

    dynamicColor: Boolean = false, androidTheme: Boolean = false, content: @Composable() () -> Unit ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(co } androidTheme && darkTheme -> DarkAndroidColorScheme androidTheme -> LightAndroidColorScheme darkTheme -> DarkDefaultColorScheme else -> LightDefaultColorScheme }
  24. *DPOΛ·ͱΊͨPCKFDU object NiaIcons { val AccountCircle = Icons.Outlined.AccountCircle val Add

    = Icons.Rounded.Add val ArrowBack = Icons.Rounded.ArrowBack val ArrowDropDown = Icons.Rounded.ArrowDropDown val ArrowDropUp = Icons.Rounded.ArrowDropUp val Bookmark = R.drawable.ic_bookmark … } Icon( painter = painterResource(id = NiaIcons.Bookmark), contentDescription = null ) Icon(imageVector = NiaIcons.Add, contentDescription = null)
  25. 8JOEPX*OTFUTͷѻ͍ w 8JOEPX*OTFUTTBGF%SBXJOHΛओʹ࢖༻ NiaNavRail( …, modifier = Modifier.safeDrawingPadding() ) NavigationBar(

    modifier = Modifier.windowInsetsPadding( WindowInsets.safeDrawing.only( WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom ) ), … ) {
  26. 8JOEPX4J[FDMBTT w ը໘෯Ͱ#PUUPN#BSͱ/BW3BJMΛग़͠෼͚ Scaffold( … bottomBar = { if (windowSizeClass.widthSizeClass

    == WindowWidthSizeClass.Compact) { NiaBottomBar( … ) } } ) { padding -> Row(…) { if (windowSizeClass.widthSizeClass != WindowWidthSizeClass.Compact) { NiaNavRail( … ) } … } BOESPJEYDPNQPTFNBUFSJBMNBUFSJBMXJOEPXTJ[FDMBTTBMQIB
  27. 8JOEPX4J[FDMBTT w ը໘෯Ͱ#PUUPN#BSͱ/BW3BJMΛग़͠෼͚ w هࣄҰཡͷ$PMVNO਺ val numberOfColumns = when (windowSizeClass.widthSizeClass)

    { WindowWidthSizeClass.Compact, WindowWidthSizeClass.Medium -> 1 else -> floor(maxWidth / 300.dp).toInt().coerceAtLeast(1) } BOESPJEYDPNQPTFNBUFSJBMNBUFSJBMXJOEPXTJ[FDMBTTBMQIB
  28. /BWJHBUJPO 4BWFE4UBUF)BOEMF composable( route = "${TopicDestination.route}/{${TopicDestination.topicIdArg}}", arguments = listOf( navArgument(TopicDestination.topicIdArg)

    { type = NavType.StringType } ) ) { TopicRoute(onBackClick = onBackClick) } @Composable fun TopicRoute( onBackClick: () -> Unit, modifier: Modifier = Modifier, viewModel: TopicViewModel = hiltViewModel(), ) { … 5PQJD%FTUJOBUJPOUPQJD*E"SH ͱ͍͏໊લͰ 4USJOH ͷBSHVNFOU /BW#BDL4UBDL&OUSZͷBSHVNFOUT͕ "CTUSBDU4BWFE4UBUF7JFX.PEFM'BDUPSZ ͷEFGBVMU"SHTʹ౉͞ΕΔ
  29. /BWJHBUJPO 4BWFE4UBUF)BOEMF @HiltViewModel class TopicViewModel @Inject constructor( savedStateHandle: SavedStateHandle, …

    ) : ViewModel() { private val topicId: String = checkNotNull(savedStateHandle[TopicDestination.topicIdArg]) … } 5PQJD%FTUJOBUJPOUPQJD*E"SH ͱ͍͏໊લͰ 4USJOH ͷBSHVNFOU ͕4BWFE4UBUF)BOEMFʹೖ͍ͬͯΔ
  30. CZTBWFE4UBUF)BOEMFTBWFBCMF\^ TextField( value = viewModel.text, onValueChange = { viewModel.text =

    it } ) @HiltViewModel class SampleViewModel @Inject constructor( savedStateHandle: SavedStateHandle ) : ViewModel() { var text by mutableStateOf("") } 1SPDFTTLJMM࣌ʹ5FYU'JFMEͷ ೖྗจࣈΛࣦͬͯ͠·͏ @HiltViewModel class SampleViewModel @Inject constructor( savedStateHandle: SavedStateHandle ) : ViewModel() { var text by savedStateHandle.saveable { mutableStateOf("") } } 1SPDFTTLJMM࣌΋ೖྗจࣈ ͕อ࣋͞ΕΔ 😫 👍