Fragment間のデータ受け渡しどれでやる?activityModel | setFragmentResult | BackStackEntry

画面遷移が得意なiOSでも画面間のデータの受け渡しは千差万別でした。いわんやAndroidもfragmentは歴史が浅いのか四苦八苦しました。

調べたところ、3つほど方法がみつかりました。

いったいどれを使うのが良いのか検討してみました。かなり独断と偏見なので、詳細は今後調べる必要があるかと思います。

ActivityModels

kotlinの開発案件で使われているソースを見かけます。

ViewModelを共有できるので便利ですが、その名の通りActivityを取得した共有であることに注意が必要です。

ViewModelを共有するので、任意のデータを共有するだけでなく、ロジックも共有したい場合に便利そうです。

Fragment KTXと呼ばれる拡張モジュールによって簡単に提供されてるので、次のように宣言するだけで共有されるためわかりやすいです。

class Fragment : Fragment() {
    private val viewModel : ViewModel by activityViewModels()
}

setFragmentResult

FragmentManagerによるFragment間のデータの受け渡しを提供する機能のようです。

あるフラグメントの結果データを使いたい場合に便利かと思います。

ただ複数データの場合Bundleのため、変換実装が必要になることが想像しえます。

こちらもFragment KTXと呼ばれる拡張モジュールによって提供されています。

// 受け側Fragment
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setResultListener("Result") { key, bundle ->
        val resultValue = bundle.getString("key")
    }
}

// 送り側Fragment
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    button.setOnClickListener {
        setResult("Result", bundleOf("key" to "result value"))
    }
}

BackStackEntry

NavControllerに提供されるデータ受け渡しの機能のため、NavControllerを使っている場合はこちらを使うことになるかと思います。

currentBackStackEntryとpreviousBackStackEntryを使います。

使い方はsetFragmentResultsと似てますが、Mapで返却できたのでBundleよりは扱いやすいかなと感じました。

// 受け側Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<String>("result")
        ?.observe(viewLifecycleOwner) {
    }
}

// 送り側Fragment
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    button.setOnClickListener {
findNavController().previousBackStackEntry?.savedStateHandle?.set("result", "ABC")
findNavController().popBackStack()
    }
}

データを1回だけ受け取る場合は受け取り後にremoveする必要があります。

結論

Fragment間でロジックを共有する場合はactivityModelsを利用して、データの受け取りだけであればNavControllerを使っていればBackStackEntry、使っていなければFragmentManagerのsetFragmentResultを使えばよいのかなといまいまは考えております。

何かご助言いただけれる場合はご連絡いただけますと幸いです。