12
3

More than 3 years have passed since last update.

Jetpack Composeで頻出する"ambient"とは

Last updated at Posted at 2020-08-30

普通にアプリの中でも使われる概念で、Jetpack Composeのサンプルアプリのコードや、内部のコードを少しでも読むとすぐに出てくる概念で、知らないと混乱すると思うので、理解しておいたほうが良さそうです。

以下の動画でも登場しました。以下の動画を見てもらってもいいですし、このQiitaでサラッと確認したい人は以下をどうぞ。
https://youtu.be/DDd6IOlH3io?t=445

アプリの中で共有したい色などを渡していきたいときはどうすればよいでしょうか? (この場合通常はMaterialThemeを使うことで解決できますが、このように共有したいものはアプリを作る中で出てきます。 参考: https://github.com/android/compose-samples/search?q=ambient&unscoped_q=ambient )
通常Composeのデータフローは上から下に流れていくので、一応、以下のように関数で渡していくことができます。

class NewsAppPalette(
  val primary = ...,
  val onPrimary = ...,
)

@Composable
fun NewsApp() {
  ArticleList(
    articles = ...,
      // ** 引数でcolorを渡していっている **
    colorPalette = NewsAppPalette(
      primary = ...
      onPrimary = ...
    ),
  )
}

@Composable
fun ArticleList(articles: List<Article>, colorPalette: NewsAppPalette) {
  Row() {
       articles.forEach { article ->
           Article(article, colors)
       }
  }
}

fun ArticleItem(article: Article, colorPalette: NewsAppPalette) {
  Text(
    text = article.title
    textColor = colorPalette.onPrimary
  )
}

image.png

が、こういうふうに渡していくのはけっこう大変ですよね。。??

Jetpack Composeではこれの代わりにambientを提供します。
サービスロケーターのように名前をつけたオブジェクトを作ってそれを使えるようにします。
これはJetpack Composeの MaterialTheme で使われているのと同じ仕組みです。
提供されているComposeのスコープ以下でその変数が利用可能になります。

ambientの利用方法

ambientの宣言
ambientでデフォルトで配布されるものをブロックの中に書きます。 ambientOf<NewsAppPalette>()のように 書かなくても宣言できます。

val NewsAppColors = ambientOf<NewsAppPalette> {
  LightColorPalette
}

ambientの変数の配布。
注意が必要なのが、このブロックの中とそこから呼び出されるComposable functionの中でのみ利用可能になるということです。

  Prividers(NewsAppColors privides LightColorPalette) {
    // この中でambientの要素を使える
  }

ambientの変数の使い方

NewsAppColors.current

コードから理解するambientの利用方法

コード的には以下になります。このように引数で渡していかなくても、利用することができています。

val NewsAppColors = ambientOf<NewsAppPalette> {
  LightColorPalette
}


@Composable
fun NewsApp() {
  // ** ここでambientをProvideする **
  Prividers(NewsAppColors privides LightColorPalette) {
    ArticleList(
      // ** 引数でcolorを渡していかなくて良くなっている **
      articles = ...
    )
  }
}

@Composable
fun ArticleList(articles: List<Article>) {
...
           Article(article)
...
}


fun ArticleItem(article: Article) {
  Text(
    text = article.title
    textColor = NewsAppColors.current.colors.onPrimary
  )
}

(配布されているところの色を変えてわかりやすくしています。)

image.png

配布される値の変更

Provideをもう一度することで、そのスコープ内でProvideする値を変えることができます。

(配布されているところの色を変えてわかりやすくしています。)
image.png

@Composable
fun NewsApp() {
  // ** ここでambientをProvideする **
  Prividers(NewsAppColors privides LightColorPalette) {
    ArticleList(
      articles = ...
    )
  }
}

@Composable
fun ArticleList(articles: List<Article>) {
...
  // ** ここでまた別のDarkColorPaletteをProvideする **
  Prividers(NewsAppColors privides DarkColorPalette) {
    Row() {
       articles.forEach { article ->
           Article(article, colors)
       }
    }
...
}


fun ArticleItem(article: Article) {
  Text(
    text = article.title
    textColor = NewsAppColors.current.colors.onPrimary
  )
}

少しだけ効率的に利用する

以下のように NewsAppColors.current で利用させるより、そのAmbientへのアクセスをラップオブジェクトを作ってあげるパターンがJetpack Compose内ではよく利用されるようです。

object NewsAppTheme {
  @Composable
  val colors: NewsAppPalette
     get() = NewsAppColors.current
  ...
}


fun ArticleItem(article: Article) {
  Text(
    text = article.title
    textColor = NewsAppTheme.colors.onPrimary
  )
}

ambientが提供されていないときにエラーにする

サンプルでは以下のようなコードもありました。エラーがわかりやすくなりますね。
https://github.com/android/compose-samples/blob/1b05455b191ad32dd1f9401771a55a6d6c1abc2a/Owl/app/src/main/java/com/example/owl/ui/utils/Navigation.kt#L107

internal val BackDispatcherAmbient = staticAmbientOf<OnBackPressedDispatcher> {
    error("No Back Dispatcher provided")
}

まとめ

これがわかるとかなりの部分のUI周辺のJetpack Composeのコードが読めるようになるので、内部への理解も深まります!
ちょっとはずれますが、 redditでは tree-local value (ツリーローカルな値) という説明になっており、しっくりきます。

12
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
3