- Logcatで確認すべきポイント
- Platform declaration clash: The following declarations have the same JVM signature (setPasswordToggle(Z)V): fun <set-passwordToggle>(<set-?>: Boolean): Unit defined in com.〜 fun setPasswordToggle(newValue: Boolean): Unit defined in com.〜
- Lint警告の@Composable関数にmodifierの引数がないと出る【This @Composable function emits content but doesn’t have a modifier parameter.See https://slackhq.github.io/compose-lints/rules/#when-should-i-expose-modifier-parameters for more information.】
Logcatで確認すべきポイント
Android Studio の Logcat フィルタを
package:com.名前.アプリ名
または
show only selected application
にすると、自分のアプリに関係するログだけ見られます。
実際の開発では、
Finsky
GooglePlayServices
GmsCore
SystemUI
SurfaceFlinger
あたりのエラーは大量に出るので、自分のアプリが正常動作しているなら気にしないことが多いです。
Platform declaration clash: The following declarations have the same JVM signature (setPasswordToggle(Z)V): fun <set-passwordToggle>(<set-?>: Boolean): Unit defined in com.〜 fun setPasswordToggle(newValue: Boolean): Unit defined in com.〜
このエラーは Kotlin のプロパティ setter と、自分で定義した関数の名前が JVM レベルで衝突している ことを意味します。
あなたのコードでは、
var passwordToggle: Boolean by mutableStateOf(false)
private set
このプロパティに対して Kotlin は内部的に
getPasswordToggle()
setPasswordToggle(Boolean)
という getter/setter メソッドを生成します。
private set を付けていても JVM 上には setter が生成されます。
一方で、あなた自身が
fun setPasswordToggle(newValue: Boolean) {
...
}
を定義しています。
すると JVM から見ると両方とも
setPasswordToggle(boolean)
になってしまい、どちらを呼べばよいか分からなくなるため、
Platform declaration clash
が発生します。
解決方法
関数名を変更するのが一般的です。
例えば
fun onPasswordToggleChanged(newValue: Boolean) {
...
}
あるいは
fun updatePasswordToggle(newValue: Boolean) {
...
}
など。
Compose の ViewModel では
fun onPasswordToggleChanged(...)
fun onBioAuthenticationToggleChanged(...)
fun onPasswordChanged(...)
のような命名がよく使われます。
例えば
fun onPasswordToggleChanged(newValue: Boolean) {
...
}
fun onBioAuthenticationToggleChanged(newValue: Boolean) {
...
}
に変更するとエラーは解消されます。
ちなみに同じ問題は今後も発生し得ます。
例えば次もアウトです。
var password: String = ""
fun setPassword(value: String) { ... }
なぜなら password プロパティの setter が内部的に setPassword(String) になるからです。
Kotlin では
var xxx
fun setXxx(...)
の組み合わせは基本的に避ける、と覚えておくとよいです。
Lint警告の@Composable関数にmodifierの引数がないと出る【This @Composable function emits content but doesn’t have a modifier parameter.See https://slackhq.github.io/compose-lints/rules/#when-should-i-expose-modifier-parameters for more information.】
はい、その警告は 「とりあえずアノテーションで消す」のはあまり推奨されません。
Compose界隈では、
@Composable
fun MyWidget(
modifier: Modifier = Modifier
) {
...
}
を付けるのが基本スタイルになっています。
例えば
@Composable
fun LoadingOverlay() {
Box(
modifier = Modifier.fillMaxSize()
) {
...
}
}
より
@Composable
fun LoadingOverlay(
modifier: Modifier = Modifier
) {
Box(
modifier = modifier.fillMaxSize()
) {
...
}
}
の方が好まれます。
呼び出し側で
LoadingOverlay(
modifier = Modifier.zIndex(100f)
)
や
LoadingOverlay(
modifier = Modifier.padding(16.dp)
)
ができるためです。
例外もある
すべての Composable に Modifier が必要というわけではありません。
例えば
@Composable
fun AppRoot()
はアプリのルートなので Modifier を付けても意味がありません。
また
@Composable
fun TableMemoApp()
みたいなトップレベルの画面構築関数も不要です。
その場合は
@Suppress("ModifierMissing")
やプロジェクトで使っているLintルールの suppress を付けるのは普通です。
判断基準
私は次の基準で考えています。
Modifierを付けるべき
- 再利用するUI部品
- Button風コンポーネント
- Card風コンポーネント
- Dialogの中身
- Overlay
- Row/Column/Boxを返すUI部品
例:
UserCard()
SearchBar()
LoadingOverlay()
SettingsSection()
Modifier不要
- AppRoot
- NavigationHost
- MainScreen
- LoginFlow
- App全体のCoordinator
例:
AppRoot()
MainNavigation()
AuthenticationGate()
SwiftUIで考えると、
struct UserCard: View
には外から .padding() や .frame() を付けたいので Modifier 相当が欲しい。
一方で
@main
struct MyApp: App
に .padding() は付けない。
Composeでもほぼ同じ感覚です。
なので、今回の LoadingOverlay() のような再利用UIなら modifier: Modifier = Modifier を追加し、AppRoot() のようなアプリ全体のエントリーポイントなら suppress で問題ありません。