State は宣言的に、ユーザーの操作は命令的に

 

🤔 ViewModelで「Stateの生成」と「ユーザー操作」を分けて考える

Jetpack Composeでは、UIを宣言的に書くことが当たり前になりました。

「UIをどう更新するか」を細かく指示するのではなく、

「このStateなら、UIはこう見える」

という関係をコードで表現します。

この考え方は、ViewModelにもそのまま応用できます。

私はViewModelを書くとき、次のシンプルなルールで考えると整理しやすいと思っています。

・ Stateの生成は宣言的に。
・ ユーザーの意図への反応は命令的に。

この2つを分けるだけで、ViewModelはかなり読みやすくなります。

 

🤔 State は宣言的に生成する

たとえば、Repositoryがユーザー一覧をFlowとして公開しているとします。


Repository
    │
    │ users: Flow<List<User>>
    ▼
   map
    │
    ▼
 UiState
    │
    ▼
 Compose

この場合、ViewModelではFlowからUiStateをそのまま導出できます。


class UserViewModel(
    repository: UserRepository
) : ViewModel() {

    val uiState: StateFlow<UiState> =
        repository.users
            .map { users ->
                UiState.Success(users)
            }
            .stateIn(
                viewModelScope,
                SharingStarted.WhileSubscribed(5_000),
                UiState.Loading
            )
}

ここには init がありません。

load() もありません。

ViewModel がやっているのは、

uiStaterepository.users から導出される

という関係を定義することだけです。

Repository から新しい値が流れてくれば、それに応じて uiState も変わります。

ここには「最初にこれを実行して、そのあとこれを実行する」という手順がありません。

あるのは、


users → UiState

という関係だけです。

これが「宣言的な State 生成」です。

 

🤔 命令的なコードにも、もちろん役割がある

だからといって、命令的なコードをなくす必要はありません。

たとえば「更新」ボタンを考えてみます。


fun refresh() {
    viewModelScope.launch {
        repository.refresh()
    }
}

これは自然なコードです。

ユーザーが「更新したい」と明示的に操作したからです。

処理の流れも明確です。


ユーザーがRefreshをタップ
        ↓
     refresh()
        ↓
Repositoryが更新処理を実行
        ↓
     Stateが変化

これは命令的な処理です。

そして、こういう場所こそ命令的なコードが適しています。

同じことは、たとえば次のような操作にも当てはまります。


fun retry()
fun deleteUser(id: String)
fun submit()

これらはすべて、

「ユーザーがこうしてほしい」

という意図を表しています。

そのため、命令として表現するのが自然です。

 

🤔 問題は、この2つを混ぜてしまうこと

少し気になるのは、State を生成するためだけに命令的なコードを使うケースです。

たとえば、こんな ViewModel です。


class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    val uiState: StateFlow<UiState> = ...

    init {
        load()
    }

    private fun load() {
        viewModelScope.launch {
            repository.loadUsers()
        }
    }
}

このコード自体が間違っているわけではありません。

ただ、ここで一度考えてみたいことがあります。

なぜ load() が必要なのでしょうか?

すでに uiState を宣言的に定義しているのに、その State を作るために別の命令を実行しなければなりません。

構造としては、こうなっています。



ViewModelの生成
      ↓
    init
      ↓
    load()
      ↓
 Stateが生成・更新

つまり、State を得るために「いつ load() を呼ぶか」という別の問題が発生します。

もし Repository がデータを Flow として公開できるのであれば、この命令自体が不要になるかもしれません。

 

🤔 LaunchedEffect に移しても、本質は変わらない

「それなら load() を Compose 側から呼べばいい」と考えることもできます。


@Composable
fun UserScreen(
    viewModel: UserViewModel
) {
    LaunchedEffect(Unit) {
        viewModel.load()
    }

    // UI
}

UI そのものは宣言的です。

しかし、ここでは別の命令が追加されています。

「この画面がCompositionに入ったらload()を呼ぶ」

つまり、


Screen enters Composition
          ↓
   LaunchedEffect
          ↓
       load()
          ↓
      State changes

という流れです。

もちろん、これは常に悪いわけではありません。

明示的な副作用が必要なケースでは、LaunchedEffect は適切な選択です。

ただし、

「Flowとして表現できるStateを最初に作るためだけ」

に LaunchedEffect を使っているのであれば、一度設計を見直してみる価値があります。

 

🤔 ViewModel の init も同じ視点で考える

ここで、ViewModel の init についても考えてみます。


init {
    load()
}

これは、

「ViewModelが生成されたら、この処理を開始する」

という命令です。

処理の順番は明確です。


ViewModelが生成される
        ↓
       init
        ↓
      load()
        ↓
    Stateが変化

一方、State を宣言的なパイプラインとして表現できるなら、


Repository State
       ↓
      map
       ↓
     UiState

と直接書けます。

ここでは「いつ State を作るか」をコードで指示していません。

State がどのように導出されるかだけを記述しています。

この違いは小さく見えますが、ViewModel の責務を考えるうえで重要です。

 

🤔 すべてを宣言的にする必要はない

ここが一番重要なポイントかもしれません。

この考え方は、

「ViewModelの処理を全部Flowにしよう」

という話ではありません。

たとえばユーザーが削除ボタンを押した場合、


fun deleteUser(id: String) {
    viewModelScope.launch {
        repository.deleteUser(id)
    }
}

で何も問題ありません。

ユーザーが「このユーザーを削除したい」と意図を示したので、その意図を命令的な処理として表現するのは自然です。

重要なのは、

・ すべてを宣言的にすること

ではありません。

むしろ、

・ Stateは宣言的に。
・ ユーザーの意図は命令的に。

という境界を持つことです。

境界があると、ViewModel のコードを読むときにも、

・ Stateを作っているコードなのか
・ ユーザーの操作に反応しているコードなのか

を簡単に区別できます。

 

🤔 init { load() } を疑ってみる

この考え方をすると、これまで何となく書いていたコードも少し違って見えてきます。


init {
    load()
}

あるいは、


LaunchedEffect(Unit) {
    viewModel.load()
}

を見たとき、

「初期ロードだから、とりあえずこれを書く」

ではなく、

「そもそも、この load() は必要なのだろうか?」

と考えられるようになります。

もちろん、必要な場合もあります。

外部 API を明示的に叩く必要があったり、ユーザーの操作によって処理を開始する必要があったりするなら、命令的な処理は適切です。

ただ、State そのものが Flow から自然に導出できるのであれば、State を作るためだけの load() は必要ないかもしれません。

 

🧑🏻‍💻 まとめ

宣言的プログラミングと命令的プログラミングのどちらかを選ぶ必要はありません。

ViewModelの中でも、それぞれに適した場所があります。


State Generation
      ↓
  Declarative

Flow → map → UiState


User Intent
      ↓
  Imperative

大切なのは、この2つを混ぜないことです。

State の生成は宣言的に。
ユーザーの意図への反応は命令的に。

この境界を意識すると、init { load() } LaunchedEffect { viewModel.load() } を「お決まりの初期化パターン」として使うのではなく、

本当にこの load() は必要なのか?

と考えられるようになります。

そして、ときには初期化問題を解決する最善の方法は、load() を別の場所に移動することではありません。

そもそも load() が必要な設計なのかを見直すことです。

 

🧑🏻‍💻 参考記事


iPhone・Android・PC 間でデータを共有するなら、Google Keep が便利

 

🧑🏻‍💻 iPhone・Android・PC でデータを送りたい

スマートフォンとPCの間で、ちょっとしたデータを送りたいことがあります。

例えば、


iPhone → Android
Android → iPhone
iPhone → Windows PC
Android → Mac
PC → iPhone
PC → Android

といったケースです。

同じOSなら、AirDropやQuick Share など便利な方法があります。

しかし、iPhone・Android・Windows・Mac が混在すると、少し面倒です。

そこで便利なのが、Google Keep です。

 

🧑🏻‍💻 Google KeepならOSを気にしなくていい

Google Keep は、Android だけのアプリではありません。

スマートフォンだけでなく、PCのブラウザからも利用できます。


          Google Keep
               │
   ┌───────────┼───────────┐
   │           │           │
Android      iPhone      PC / Mac
   │           │           │
   └───────────┼───────────┘
               │
         Google Account

つまり、Google Keepを共有場所にすることで、OSの違いをあまり意識せずに済みます。

 

🧑🏻‍💻 Googleアカウントがあればいい

Google Keep の大きなメリットは、Google アカウントを使えることです。

例えば PC で調べた URL をスマートフォンに送りたいとします。


PC
 │
 │ URLをKeepに保存
 ▼
Google Keep
 │
 │ 同期
 ▼
iPhone / Android

スマートフォン側でGoogle Keepを開けば、そのURLを確認できます。

逆に、


iPhone
   │
   │ テキストを保存
   ▼
Google Keep
   │
   │ 同期
   ▼
Windows PC

のように、スマートフォンからPCへ送ることもできます。

 

🧑🏻‍💻 「自分の端末間」で使うのが簡単

実は、Google Keep の便利さは自分の複数端末をつなぐ用途で特に分かりやすいです。

例えば、


       ┌──── iPhone
       │
       ├──── Android
Google ┤
Keep   ├──── Mac
       │
       └──── Windows PC

すべて同じ Google アカウントでログインしておけば、Keep のメモを各端末から確認できます。

「自分の iPhone から自分の Mac へ URL を送りたい」といった用途なら、かなり手軽です。

 

🧑🏻‍💻 他の人とも共有できる

Google Keep は、自分の端末間だけでなく他の人との共有にも使えます。

共有相手の Google アカウントを指定すれば、同じメモを共同編集できます。


      Google Keep
      /         \
     /           \
自分の Google    相手の Google
  Account         Account
     │               │
  Android          iPhone
     │               │
    PC              PC

そのため、

・ 自分の Android → 家族の iPhone
・ 自分の Mac → 同僚の Windows
・ 自分の iPhone → 自分の PC

のような使い方もできます。

 

🧑🏻‍💻 「Google アカウントは今どき持っている」がポイント

iPhone ユーザーでも Google アカウントを持っている人は珍しくありません。

Android ユーザーなら、Google アカウントを利用しているケースはさらに多いでしょう。

PC でもブラウザから Google Keep を利用できます。

そのため、特別なアプリや専用の転送ケーブルを用意しなくても、OSの違いを超えて情報を共有できます。

 

🧑🏻‍💻 まとめ

iPhone・Android・Windows・Mac が混在する環境では、データ共有の方法に悩みがちです。

Google Keepなら、

・ Android
・ iPhone / iPad
・ Windows
・ Mac
・ Chromebook

などから同じデータにアクセスできます。

特に、

・ URL
・ テキスト
・ メモ
・ チェックリスト
・ 小さな画像

のようなちょっとしたデータを別の端末へ渡す用途に向いています。

「iPhone か Android か」ではなく、「Google Keep を共有場所にする」と考えると、端末間のデータ共有がかなりシンプルになります。


【Jetpack Compose】スクロール位置を正しく復元する

スクロール位置の復元は、一見するととても簡単そうに見えます。


val gridState = rememberLazyStaggeredGridState( 
    initialFirstVisibleItemIndex = savedIndex, 
    initialFirstVisibleItemScrollOffset = savedOffset
)

しかし、この方法が正しく動作するのは、すでにアイテムのレイアウトが完了している場合だけです。

データを非同期で読み込む画面では、LazyVerticalStaggeredGrid は最初はアイテム数が 0 の状態で生成されることが多くあります。

そのため、指定した初期スクロール位置は反映されません。

この問題を解決するため、多くの開発者は LaunchedEffect の中で scrollToItem() を呼び出します。


LaunchedEffect(Unit) { 
    gridState.scrollToItem(savedIndex, savedOffset)
}

しかし、これにも問題があります。

LaunchedEffect はコンポーズ直後に実行されるため、レイアウトがまだ完了していないタイミングで scrollToItem() が呼ばれてしまう可能性があります。

 

🧑🏻‍💻 グリッドの準備が完了するまで待つ

レイアウトの完了タイミングを推測するのではなく、目的のアイテムが実際にレイアウトされるまで待機するのが確実です。


LaunchedEffect(Unit) { 
    val savedPosition = viewModel.savedScrollPosition

    // 目的のアイテムがレイアウトされるまで待機 
    snapshotFlow { gridState.layoutInfo.totalItemsCount }
        .first { it > savedPosition.index }
 
    gridState.scrollToItem(savedPosition.index, savedPosition.offset) 
}

このコードでは、layoutInfo.totalItemsCount が保存していたインデックスより大きくなるまで処理を一時停止します。

つまり、復元したいアイテムが実際にレイアウトされたことを確認してから scrollToItem() を実行するため、タイミングに依存せず、安定してスクロール位置を復元できます。

 

💡 追記: タブを切り替えても rememberSaveable を機能させる

複数の NavBackStack を使う場合、rememberDecoratedNavEntries を外出しにして、NavDisplay(entries) を使うと rememberSaveable がタブ切り替え時にも機能する。

上に書いたような「ViewModel 内に保持させて UI の re-compose 時に読み込んで」のような処理はいらない。

 

🧑🏻‍💻 参考


2026 FIFAワールドカップを無料で視聴できる国まとめ

2026 FIFAワールドカップは史上最多となる48カ国が参加し、全104試合が開催されます。

日本では有料配信や放送形態がまだ確定していない部分もありますが、海外に目を向けると、多くの国で無料視聴が予定されています。

今回は、2026 FIFAワールドカップを無料で視聴できる主要な国と放送局をまとめました。

 

🤔 無料で視聴できる主な国

イギリス

BBCとITVが全104試合を分担して放送予定です。

両局とも無料放送で知られており、イギリス国内ではワールドカップのほぼ全試合を追加料金なしで楽しめます。

オーストラリア

SBSが大会全試合を無料配信する予定です。

インターネット経由でも視聴できるため、近年のスポーツ中継では非常に人気の高い選択肢となっています。

ブラジル

ブラジルではCazéTVが注目されています。

YouTubeを活用したスポーツ配信で急成長しており、2026年大会でも全試合の無料配信が予定されています。

ヨーロッパ各国

オランダのNOS、ベルギーのVRT・RTBF、スイスのSRF・RTS・RSI、アイルランドのRTÉなど、多くの公共放送局が無料中継を実施する見込みです。

ヨーロッパではサッカー人気が非常に高く、ワールドカップを無料で視聴できる国が数多く存在します。

中国

中国ではCCTVが無料放送を行う予定です。

テレビだけでなくオンライン配信にも対応する可能性があります。

 

🤔 海外の無料配信を見る方法

海外放送局の多くは配信地域を制限しています。

そのため、日本から直接アクセスしても視聴できない場合があります。

一般的には以下のような仕組みになっています。


視聴者
   ↓
インターネット
   ↓
放送局の配信サーバー
   ↓
地域判定
   ├─ 対象国 → 視聴可能
   └─ 対象外 → 視聴不可

配信条件や権利契約は大会直前に変更されることもあるため、最新情報の確認が重要です。

 

🤔 まとめ

2026 FIFAワールドカップでは、多くの国で無料放送・無料配信が予定されています。

特にイギリスのBBC・ITV、オーストラリアのSBS、ブラジルのCazéTVは、全試合を無料で視聴できる有力な選択肢として注目されています。

大会が近づくにつれて放映権情報は更新されるため、各放送局やFIFAの公式発表を定期的に確認しておきましょう。

 

🤔 参考リンク

- Tom's Guide — How to watch World Cup 2026: live stream every game for FREE
https://www.tomsguide.com/entertainment/sports/watch-world-cup-2026-free-live-streams

- FIFA World Cup 2026
https://fifa-2026.com/fifa-2026-broadcasting-and-streaming

- 2026 FIFA World Cup broadcasting rights
https://www.fifaworldcupnews.com/fifa-world-cup-2026-broadcasting-rights/


Mac Book の 日本語 JIS キーボードを 英語 US キーボードになるべく違和感なく変える方法

日本語 JIS キーボードが馴染めない場合とか

ストレスすぎますよね。

逆もしかり。

様々なキーボードマッピングカスタマイズアプリなどありますが、

強引にレイアウトを変更したとて、

そもそもキーそれぞれ一つ一つのサイズが違う。

この方法がベストだと思われます。

 

🧑🏻‍💻 Magic Keyboard を手に入れる

少し高いですが。

👉 Magic Keyboard - 英語(US) - Apple(日本)

これを買うなりして手に入れるのが一番操作感は一番近いです。

日本語 JIS と 英語 US。

指紋センサー付きとなし。

Mac Bookのキーボード。

すべてサイズ同じです。

上に乗せればそれが一番良い感じです。

以上です。

知らなかったわ。