目次

07.駅選択画面の作成

駅を選択する画面を:feature:depart:stationモジュールに作成します。

共通UIモジュールの作成と同様に、トップディレクトリで:feature:depart:stationを作成し、javaディレクトリ名の変更、および、サンプルテストクラスの削除を行います。

FeatureConfigurePluginの適用

:feature:depart:stationモジュールのbuild.gradle.ktsに作成したビルドプラグインを適用します。

plugins {
  alias(libs.plugins.android.library)
  alias(libs.plugins.kotlin.android)
  id("build.logic.feature.configure")
}

android {
  namespace = "jp.co.progress_llc.portal.feature.depart.station"
  compileSdk {
    version = release(36)
  }

  defaultConfig {
    minSdk = 28

    testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    consumerProguardFiles("consumer-rules.pro")
  }

  buildTypes {
    release {
      isMinifyEnabled = false
      proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
    }
  }
  compileOptions {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
  }
  kotlinOptions {
    jvmTarget = "11"
  }
}

dependencies {

  implementation(libs.androidx.core.ktx)
  implementation(libs.androidx.appcompat)
  implementation(libs.material)
  testImplementation(libs.junit)
  androidTestImplementation(libs.androidx.junit)
  androidTestImplementation(libs.androidx.espresso.core)
  implementation(project(":core:ui"))
  implementation(project(":feature:depart:ui"))
}

2行目~3行目、9行目~15行目、17行目~32行目、37行目~39行目
ビルドプラグインで定義しているので削除します。
3行目
/build-logic/build.gradle.ktsで定義したビルドプラグインのidを指定します。
16行目、40行目~42行目
テストを行うときまで削除します。
43行目
共通UIモジュールを使用します。
44行目
共有ViewModelを使用するため発車予定画面の共通UIモジュールを使用します。

適用後、『Sync Now』で内容をプロジェクトに反映させます。

プレビュー用ViewModelの作成

:feature:depart:stationdebugソースセットを追加し、viewmodelディレクトリ(パッケージ)を作成します。
viewmodelディレクトリ(パッケージ)の直下にプレビュー用のDI(Hilt)を使用しないViewModel実装をDepartRouteEditViewModelPreviewで作成します。

package jp.co.progress_llc.portal.feature.depart.station.viewmodel

import jp.co.progress_llc.portal.feature.depart.ui.viewmodel.DepartRouteEditViewModelContract

class DepartRouteEditViewModelPreview : DepartRouteEditViewModelContract {
  override var stationType = "発駅"
  override var stationCode = ""
  override var stationName = ""
}

6行目~8行目
インターフェースの各変数への初期化を実装しています。

表示画面の作成

:feature:depart:stationpresentationディレクトリ(パッケージ)を追加し、直下に暫定的に共有ViewModelのみDepartStationScreen.ktを作成します。

package jp.co.progress_llc.portal.feature.depart.station.presentation

import androidx.compose.runtime.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import jp.co.progress_llc.portal.core.ui.theme.AppTheme
import jp.co.progress_llc.portal.core.ui.components.AppTitleBox
import jp.co.progress_llc.portal.core.ui.components.CustomOutlinedTextField
import jp.co.progress_llc.portal.feature.depart.ui.viewmodel.DepartRouteEditViewModelContract
import jp.co.progress_llc.portal.feature.depart.ui.viewmodel.DepartRouteEditViewModel
import jp.co.progress_llc.portal.feature.depart.station.viewmodel.DepartRouteEditViewModelPreview

@Composable
fun DepartStationScreen(
  departRouteEditViewModel:
    DepartRouteEditViewModelContract = hiltViewModel<DepartRouteEditViewModel>()
) {
  val stationType = departRouteEditViewModel.stationType
  var searchText by remember { mutableStateOf("") }

  Scaffold(
    topBar = { AppTitleBox() }
  ) { innerPadding ->
    Box(
      modifier = Modifier
        .padding(innerPadding)
        .fillMaxSize()
    ) {
      Column(
        modifier = Modifier
          .fillMaxSize()
          .background(MaterialTheme.colorScheme.background)
          .padding(8.dp),
        verticalArrangement = Arrangement.spacedBy(2.dp)
      ) {
        Text(
          text = stationType + "選択",
          style = MaterialTheme.typography.titleLarge,
        )
        Row(
          modifier = Modifier
            .fillMaxWidth()
            .padding(top = 10.dp)
            .height(32.dp)
            .padding(horizontal = 8.dp),
          verticalAlignment = Alignment.CenterVertically
        ) {
          CustomOutlinedTextField(
            value = searchText,
            onValueChange = { value: String -> searchText = value },
            label = { Text(stationType, style = MaterialTheme.typography.bodySmall) },
            placeholder = "検索する" + stationType + "名を入力してください",
            modifier = Modifier.weight(1f),
            textStyle = MaterialTheme.typography.bodySmall.copy(
              color = MaterialTheme.colorScheme.primary
            ),
            contentPadding = PaddingValues(start = 12.dp, end = 8.dp, top = 2.dp),
            shape = RoundedCornerShape(6.dp),
            singleLine = true,
            height = 32.dp
          )
          Spacer(modifier = Modifier.width(8.dp))
          Button(
            onClick = { },
            modifier = Modifier.height(32.dp),
            contentPadding = PaddingValues(horizontal = 12.dp),
            shape = RoundedCornerShape(6.dp)
          ) {
            Text(
              text = "検索",
              style = MaterialTheme.typography.bodySmall
            )
          }
        }
      }
    }
  }
}

@Preview(showBackground = true)
@Composable
fun DepartStationScreenPreview() {
  AppTheme {
    DepartStationScreen(
      departRouteEditViewModel = DepartRouteEditViewModelPreview()
    )
  }
}

20行目
Jetpack Compose関数であることを宣言します。
22行目~23行目
共有ViewModelのインタフェースを引数にしています。
既定でDI(Hilt)で共有ViewModelを注入しています。
25行目
共有ViewModelからデータを取得しています。
26行目
検索する駅名を初期化しています。
71行目
検索ボタンが押されても何もしないようにしています。
87行目~95行目
Android Studioで画面のプレビューを表示するための関数を定義しています。
なくても構いません。
プレビュー用のViewModelを渡しています。