diff --git a/Samples/Flix/FlixHilt/build.gradle b/Samples/Flix/FlixHilt/build.gradle
new file mode 100644
index 00000000..e227c93e
--- /dev/null
+++ b/Samples/Flix/FlixHilt/build.gradle
@@ -0,0 +1,88 @@
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+
+buildscript {
+ ext {
+ versions = [
+ 'compose': [
+ 'compilerExt': '1.5.14',
+ 'ui' : '1.6.7',
+ ]
+ ]
+ }
+}
+
+plugins {
+ id 'com.android.library'
+ alias(libs.plugins.google.devtools.ksp)
+ id 'com.google.dagger.hilt.android'
+ id 'dev.testify'
+ alias(libs.plugins.compose.compiler)
+}
+
+android {
+ namespace 'dev.testify.samples.flix.hilt'
+ compileSdk 36
+
+ defaultConfig {
+ minSdkVersion 26
+ targetSdkVersion 36
+
+ // Hilt tests need HiltTestApplication. This setting is module-wide, which is why the
+ // example lives here rather than in :FlixSample — see HiltTestRunner.
+ testInstrumentationRunner "dev.testify.samples.flix.hilt.HiltTestRunner"
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_25
+ targetCompatibility JavaVersion.VERSION_25
+ }
+ kotlin {
+ compilerOptions {
+ jvmTarget.set(JvmTarget.JVM_25)
+ }
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion "${versions.compose.compilerExt}"
+ }
+}
+
+dependencies {
+ def composeBom = platform('androidx.compose:compose-bom:2025.08.01')
+ implementation composeBom
+
+ implementation "androidx.appcompat:appcompat:1.7.1"
+ implementation 'androidx.compose.material3:material3:1.3.2'
+ implementation "androidx.hilt:hilt-navigation-compose:1.2.0"
+ implementation "com.google.dagger:hilt-android:2.60.1"
+ ksp "com.google.dagger:hilt-compiler:2.60.1"
+
+ // HiltComposableTestActivity lives in the `debug` source set, which cannot see the
+ // `androidTest` dependencies below. It extends ComposableTestActivity, which implements an
+ // interface from the core library, so both artifacts are needed to compile it.
+ //
+ // `compileOnly` is enough: at run time the Testify classes come from the test APK, as they do
+ // for ComposableTestActivity itself. `debugImplementation` would instead add Testify's own test
+ // dependencies to the module and clash with the versions `androidTest` resolves.
+ debugCompileOnly project(":Library")
+ debugCompileOnly project(path: ":ComposeExtensions")
+
+ androidTestImplementation composeBom
+ androidTestImplementation project(":Library")
+ androidTestImplementation project(path: ":ComposeExtensions")
+ androidTestImplementation 'androidx.test.ext:junit:1.3.0'
+ androidTestImplementation "androidx.test:core-ktx:1.4.0"
+ androidTestImplementation "androidx.test:rules:1.7.0"
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:${versions.compose.ui}"
+
+ androidTestImplementation "com.google.dagger:hilt-android-testing:2.60.1"
+ kspAndroidTest "com.google.dagger:hilt-compiler:2.60.1"
+}
+
+testify {
+ applicationPackageId "dev.testify.samples.flix.hilt.test"
+ testPackageId "dev.testify.samples.flix.hilt.test"
+ autoImplementLibrary false
+}
diff --git a/Samples/Flix/FlixHilt/src/androidTest/assets/screenshots/37-1080x2220@440dp-en_US/HiltComposableScreenshotTest_default.png b/Samples/Flix/FlixHilt/src/androidTest/assets/screenshots/37-1080x2220@440dp-en_US/HiltComposableScreenshotTest_default.png
new file mode 100644
index 00000000..cddfe0bc
Binary files /dev/null and b/Samples/Flix/FlixHilt/src/androidTest/assets/screenshots/37-1080x2220@440dp-en_US/HiltComposableScreenshotTest_default.png differ
diff --git a/Samples/Flix/FlixHilt/src/androidTest/java/dev/testify/samples/flix/hilt/HiltComposableScreenshotTest.kt b/Samples/Flix/FlixHilt/src/androidTest/java/dev/testify/samples/flix/hilt/HiltComposableScreenshotTest.kt
new file mode 100644
index 00000000..905a5e6c
--- /dev/null
+++ b/Samples/Flix/FlixHilt/src/androidTest/java/dev/testify/samples/flix/hilt/HiltComposableScreenshotTest.kt
@@ -0,0 +1,87 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 ndtp
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package dev.testify.samples.flix.hilt
+
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.test.core.app.launchActivity
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import dev.testify.annotation.ScreenshotInstrumentation
+import dev.testify.compose.scenario.ComposableScreenshotScenarioRule
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Demonstrates screenshot testing a composable that resolves its dependencies through Hilt.
+ *
+ * The composable under test calls `hiltViewModel()`, which needs an `@AndroidEntryPoint` host. The
+ * three pieces that make that work:
+ *
+ * 1. [HiltComposableTestActivity] in the `debug` source set, an `@AndroidEntryPoint` subclass of
+ * `ComposableTestActivity`, declared in `src/debug/AndroidManifest.xml`.
+ * 2. [HiltTestRunner] as the module's `testInstrumentationRunner`.
+ * 3. [HiltAndroidRule] ordered before the Testify rule, so the component is ready before the
+ * activity launches.
+ *
+ * `ComposableScreenshotRule` cannot be used here: it hardcodes `ComposableTestActivity` as its
+ * activity. `ComposableScreenshotScenarioRule` lets the test choose the host.
+ *
+ * @see Testing composables that use Hilt
+ */
+@HiltAndroidTest
+class HiltComposableScreenshotTest {
+
+ @get:Rule(order = 0)
+ val hiltRule = HiltAndroidRule(this)
+
+ @get:Rule(order = 1)
+ val rule = ComposableScreenshotScenarioRule()
+
+ /**
+ * Resolves [SampleViewModel] through Hilt and renders a value that comes from it. If the
+ * injection fails the test throws rather than capturing, so a passing baseline is itself the
+ * assertion that Hilt resolved through [HiltComposableTestActivity].
+ */
+ @Composable
+ private fun HiltBackedContent() {
+ val viewModel = hiltViewModel()
+ Text(text = viewModel.label)
+ }
+
+ @ScreenshotInstrumentation
+ @Test
+ fun default() {
+ launchActivity().use { scenario ->
+ rule
+ .withScenario(scenario)
+ .setCompose {
+ HiltBackedContent()
+ }
+ .assertSame()
+ }
+ }
+}
diff --git a/Samples/Flix/FlixHilt/src/androidTest/java/dev/testify/samples/flix/hilt/HiltTestRunner.kt b/Samples/Flix/FlixHilt/src/androidTest/java/dev/testify/samples/flix/hilt/HiltTestRunner.kt
new file mode 100644
index 00000000..77147d21
--- /dev/null
+++ b/Samples/Flix/FlixHilt/src/androidTest/java/dev/testify/samples/flix/hilt/HiltTestRunner.kt
@@ -0,0 +1,45 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 ndtp
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package dev.testify.samples.flix.hilt
+
+import android.app.Application
+import android.content.Context
+import androidx.test.runner.AndroidJUnitRunner
+import dagger.hilt.android.testing.HiltTestApplication
+
+/**
+ * Substitutes [HiltTestApplication] for the app's own [Application] so Hilt can build its test
+ * component.
+ *
+ * `testInstrumentationRunner` is module-wide, which is why this module exists. Pointing the `Flix`
+ * sample at this runner would replace its `FlixApplication`, and with it the Coil `ImageLoader` that
+ * `FlixApplication` supplies through `ImageLoaderFactory` — so its image-backed screenshot tests
+ * would capture before their images had drawn. Keeping the Hilt example in its own module leaves
+ * that suite alone.
+ */
+class HiltTestRunner : AndroidJUnitRunner() {
+ override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application =
+ super.newApplication(cl, HiltTestApplication::class.java.name, context)
+}
diff --git a/Samples/Flix/FlixHilt/src/debug/AndroidManifest.xml b/Samples/Flix/FlixHilt/src/debug/AndroidManifest.xml
new file mode 100644
index 00000000..951eabc8
--- /dev/null
+++ b/Samples/Flix/FlixHilt/src/debug/AndroidManifest.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/Samples/Flix/FlixHilt/src/debug/java/dev/testify/samples/flix/hilt/HiltComposableTestActivity.kt b/Samples/Flix/FlixHilt/src/debug/java/dev/testify/samples/flix/hilt/HiltComposableTestActivity.kt
new file mode 100644
index 00000000..72d2391d
--- /dev/null
+++ b/Samples/Flix/FlixHilt/src/debug/java/dev/testify/samples/flix/hilt/HiltComposableTestActivity.kt
@@ -0,0 +1,59 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 ndtp
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package dev.testify.samples.flix.hilt
+
+import dagger.hilt.android.AndroidEntryPoint
+import dev.testify.ComposableTestActivity
+
+/**
+ * A Hilt-aware host for composables under test.
+ *
+ * Hilt injects only into activities annotated with [AndroidEntryPoint]. Testify's
+ * [ComposableTestActivity] is not, so a composable that resolves a dependency — through
+ * `hiltViewModel()`, for example — fails inside it with:
+ *
+ * ```
+ * java.lang.IllegalStateException: Given component holder class dev.testify.ComposableTestActivity
+ * does not implement interface dagger.hilt.internal.GeneratedComponent or interface
+ * dagger.hilt.internal.GeneratedComponentManager
+ * ```
+ *
+ * Subclassing it here adds the annotation without changing anything else, so
+ * `ComposableScreenshotScenarioRule` can host Hilt-backed composables.
+ *
+ * This lives in the `debug` source set, with its manifest entry in `src/debug/AndroidManifest.xml`
+ * and Testify on the debug compile classpath as `debugCompileOnly`. That is the arrangement an
+ * application module needs — a test-only activity has no business in a shipping build — and it is
+ * what the [Hilt recipe](https://testify.dev/docs/recipes/hilt) describes, so this sample mirrors
+ * the recipe step for step.
+ *
+ * It is not the only arrangement that works. In a library module the test APK *is* the application,
+ * so the activity can equally live in `androidTest` with an `androidTest` manifest and no
+ * `compileOnly` at all. `debug` is used here to match the recipe.
+ *
+ * @see HiltComposableScreenshotTest
+ */
+@AndroidEntryPoint
+class HiltComposableTestActivity : ComposableTestActivity()
diff --git a/Samples/Flix/FlixHilt/src/main/java/dev/testify/samples/flix/hilt/SampleViewModel.kt b/Samples/Flix/FlixHilt/src/main/java/dev/testify/samples/flix/hilt/SampleViewModel.kt
new file mode 100644
index 00000000..8b3e71a9
--- /dev/null
+++ b/Samples/Flix/FlixHilt/src/main/java/dev/testify/samples/flix/hilt/SampleViewModel.kt
@@ -0,0 +1,41 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 ndtp
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package dev.testify.samples.flix.hilt
+
+import androidx.lifecycle.ViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+
+/**
+ * A view model with nothing in it, so the test has something for Hilt to resolve.
+ *
+ * The point of [HiltComposableScreenshotTest] is that `hiltViewModel()` resolves at all inside a
+ * screenshot test. Giving this no dependencies and no mutable state keeps the rendered output
+ * deterministic, so a failure means the injection broke rather than the data changed.
+ */
+@HiltViewModel
+class SampleViewModel @Inject constructor() : ViewModel() {
+ val label: String = "injected by Hilt"
+}
diff --git a/bitrise.yml b/bitrise.yml
index 491cfde3..c8e2013e 100644
--- a/bitrise.yml
+++ b/bitrise.yml
@@ -431,6 +431,22 @@ workflows:
- test_package: dev.testify.samples.flix.library.test
- test_runner: androidx.test.runner.AndroidJUnitRunner
- verbose: true
+ - android-build@1:
+ inputs:
+ - variant: DebugAndroidTest
+ - module: FlixHilt
+ - git::https://github.com/ndtp/bitrise-step-android-testify.git@main:
+ timeout: 1800
+ title: Run Screenshot Tests
+ inputs:
+ - adb_command: "./gradlew FlixHilt:screenshotTest"
+ - module: ":FlixHilt"
+ - app_apk: "./Samples/Flix/FlixHilt/build/outputs/apk/androidTest/debug/FlixHilt-debug-androidTest.apk"
+ - app_package: dev.testify.samples.flix.hilt.test
+ - test_apk: "./Samples/Flix/FlixHilt/build/outputs/apk/androidTest/debug/FlixHilt-debug-androidTest.apk"
+ - test_package: dev.testify.samples.flix.hilt.test
+ - test_runner: dev.testify.samples.flix.hilt.HiltTestRunner
+ - verbose: true
- custom-test-results-export@1.0:
is_always_run: true
timeout: 120
diff --git a/settings.gradle b/settings.gradle
index 4e3ba1ae..1d6b79a7 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -19,6 +19,7 @@ dependencyResolutionManagement {
include ':Accessibility'
include ':ComposeExtensions'
+include ':FlixHilt'
include ':FlixLibrary'
include ':FlixSample'
include ':FullscreenCaptureMethod'
@@ -32,6 +33,7 @@ includeBuild("./Plugins/Gradle") { name = "Plugin" }
project(':Accessibility').projectDir = new File("./Ext/Accessibility")
project(':ComposeExtensions').projectDir = new File("./Ext/Compose")
+project(':FlixHilt').projectDir = new File("./Samples/Flix/FlixHilt")
project(':FlixLibrary').projectDir = new File("./Samples/Flix/FlixLibrary")
project(':FlixSample').projectDir = new File("./Samples/Flix")
project(':FullscreenCaptureMethod').projectDir = new File("./Ext/Fullscreen")