Android Apps

How to Make a Flashlight App in Android Studio

The best first Android project. Complete working code for a torch app, with runtime permissions, lifecycle handling and the device quirks that break it.

A flashlight app running on an Android phone

A flashlight app is the right first Android project. It is small enough to finish in an evening, but it still teaches the four things every Android app needs: layouts, permissions, hardware access and lifecycle.

All the code is here. Copy it, run it, then change it.

What you need

  • Android Studio — any recent version
  • A real phone. The emulator has no flash, so the app will look broken there. Use a phone with USB debugging on.
  • No prior experience. Everything is explained.

Step 1: Create the project

File → New → New Project → Empty Views Activity.

  • Name: Flashlight
  • Package name: com.yourname.flashlight — must be unique on the Play Store
  • Language: Java or Kotlin, both are below
  • Minimum SDK: API 24 (Android 7.0), which covers almost every phone in use

The first build downloads a lot through Gradle. Leave it alone until it finishes.

Step 2: Ask for the camera permission

The torch is part of the camera hardware, so the app needs camera permission even though it never takes a photo.

Open AndroidManifest.xml and add these two lines above <application>:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.flash" android:required="true" />

uses-feature with required="true" tells the Play Store to hide the app from devices with no flash. Without it, people on tablets install it, it does nothing, and you get one-star reviews.

Step 3: Build the layout

Replace res/layout/activity_main.xml with this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:background="#111827">

    <TextView
        android:id="@+id/statusText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Torch is off"
        android:textColor="#9CA3AF"
        android:textSize="18sp"
        android:layout_marginBottom="32dp" />

    <Button
        android:id="@+id/toggleButton"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:text="TURN ON"
        android:textSize="20sp"
        android:textStyle="bold" />

</LinearLayout>

One big button in the middle. That is the whole interface, and for this app that is correct — a torch should be one tap, not a menu.

Step 4: The code

This is the part that matters. We use CameraManager.setTorchMode(), which is the modern API and works on Android 6.0 and above.

Java

package com.yourname.flashlight;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraManager;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity {

    private CameraManager cameraManager;
    private String cameraId;
    private boolean isOn = false;

    private Button toggleButton;
    private TextView statusText;

    private static final int CAMERA_REQUEST = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        toggleButton = findViewById(R.id.toggleButton);
        statusText   = findViewById(R.id.statusText);

        cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

        // Find the camera that actually has a flash — usually the back one,
        // but never assume it is camera 0.
        try {
            for (String id : cameraManager.getCameraIdList()) {
                Boolean hasFlash = cameraManager.getCameraCharacteristics(id)
                        .get(android.hardware.camera2.CameraCharacteristics.FLASH_INFO_AVAILABLE);
                if (Boolean.TRUE.equals(hasFlash)) { cameraId = id; break; }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

        if (cameraId == null) {
            statusText.setText("This device has no flash");
            toggleButton.setEnabled(false);
            return;
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST);
        }

        toggleButton.setOnClickListener(v -> toggleTorch());
    }

    private void toggleTorch() {
        try {
            isOn = !isOn;
            cameraManager.setTorchMode(cameraId, isOn);
            toggleButton.setText(isOn ? "TURN OFF" : "TURN ON");
            statusText.setText(isOn ? "Torch is on" : "Torch is off");
        } catch (CameraAccessException e) {
            Toast.makeText(this, "Could not access the torch", Toast.LENGTH_SHORT).show();
            isOn = false;
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Leaving the torch on in the background drains the battery and
        // can overheat the LED. Always switch it off.
        if (isOn) { toggleTorch(); }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == CAMERA_REQUEST
                && (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED)) {
            Toast.makeText(this, "Camera permission is needed for the torch",
                    Toast.LENGTH_LONG).show();
            toggleButton.setEnabled(false);
        }
    }
}

Kotlin

package com.yourname.flashlight

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

class MainActivity : AppCompatActivity() {

    private lateinit var cameraManager: CameraManager
    private var cameraId: String? = null
    private var isOn = false

    private lateinit var toggleButton: Button
    private lateinit var statusText: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        toggleButton = findViewById(R.id.toggleButton)
        statusText   = findViewById(R.id.statusText)
        cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager

        cameraId = cameraManager.cameraIdList.firstOrNull { id ->
            cameraManager.getCameraCharacteristics(id)
                .get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true
        }

        if (cameraId == null) {
            statusText.text = "This device has no flash"
            toggleButton.isEnabled = false
            return
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 100)
        }

        toggleButton.setOnClickListener { toggleTorch() }
    }

    private fun toggleTorch() {
        try {
            isOn = !isOn
            cameraManager.setTorchMode(cameraId!!, isOn)
            toggleButton.text = if (isOn) "TURN OFF" else "TURN ON"
            statusText.text   = if (isOn) "Torch is on" else "Torch is off"
        } catch (e: Exception) {
            Toast.makeText(this, "Could not access the torch", Toast.LENGTH_SHORT).show()
            isOn = false
        }
    }

    override fun onPause() {
        super.onPause()
        if (isOn) toggleTorch()
    }
}

Three things in that code worth understanding

These are the parts that separate a working app from a tutorial copy.

Never assume camera 0 has the flash

Most tutorials hardcode cameraId = "0". On phones with several rear cameras, or where the front camera is listed first, that crashes or silently does nothing. The loop above asks each camera whether it has a flash and uses the one that does.

Turn the torch off in onPause()

If the user switches apps with the torch on, it stays on. The battery drains, the LED gets hot, and the user blames your app. Three lines in onPause() prevent it.

Handle a refused permission

If the user says no to the camera prompt, the app must not crash. It should say what it needs and disable the button. Reviewers on the Play Store test exactly this.

Step 5: Run it

Connect a real phone with USB debugging on and press Run. Allow the camera permission when asked, then press the button.

Test all four of these before you call it finished:

  1. Torch turns on and off
  2. Press home with it on — it should switch off
  3. Refuse the permission — the app should explain, not crash
  4. Rotate the phone — it should not crash

Common problems

Nothing happens when I press the button

You are probably on the emulator, which has no flash. Use a real device.

The app crashes on launch

Usually a hardcoded camera ID. Use the loop above. Check Logcat in Android Studio for the exact line.

"CameraAccessException"

Another app is holding the camera. Close the camera app and try again. This also happens if your own app opened the camera and never released it.

The torch stays on after closing the app

The onPause() handler is missing.

It works on my phone but not on another

Different camera layouts. The detection loop handles most of this. A few older Samsung and Xiaomi models need the legacy Camera.Parameters API, but those are now rare.

Ideas to take it further

Once it works, these are good next steps and each teaches something new:

  • SOS mode — flash a repeating pattern with a Handler
  • Screen light — a white full-screen mode for devices with no flash
  • A widget — toggle from the home screen without opening the app
  • Shake to toggle — introduces the accelerometer
  • Strobe with adjustable speed — a SeekBar plus a background thread

Publishing it

Two honest notes before you put this on the Play Store.

First, the store is full of torch apps, and most are older than yours with thousands of reviews. Do not expect downloads from a plain one.

Second, keep the permissions minimal. Torch apps have a bad reputation because many ask for contacts, location and storage they do not need. Google reviews camera-permission apps carefully. Asking only for what you use gets you approved faster.

Where to go next

Questions people ask

Why does my flashlight app not work on the emulator?
The Android emulator has no flash hardware, so setTorchMode does nothing. You need a real phone with USB debugging enabled. This is the most common reason a correct torch app appears broken.
Do I need camera permission for a flashlight app?
Yes. The torch LED is part of the camera hardware, so Android requires the CAMERA permission even though your app never takes a photo. Add it to the manifest and request it at runtime.
Why does my app crash when I open it?
Usually a hardcoded camera ID. Many tutorials use "0", which fails on phones where the front camera is listed first or there are several rear cameras. Loop through getCameraIdList and pick the one where FLASH_INFO_AVAILABLE is true.
Why does the torch stay on after I close the app?
You are not switching it off in onPause. Without that, the LED keeps burning in the background, drains the battery and can overheat. Call your toggle method from onPause whenever the torch is on.
Can I make a flashlight app in Kotlin?
Yes, and the Kotlin version is shorter. Both complete versions are in this guide. Kotlin is now the default for new Android projects, but Java works identically for this app.
What is CameraAccessException and how do I fix it?
It means another app is holding the camera, so yours cannot control the torch. Close the camera app and try again. It also happens if your own code opened the camera and never released it.
How do I make an SOS or strobe mode?
Use a Handler with postDelayed to toggle the torch on and off in a repeating pattern. For SOS, use the Morse timing: three short, three long, three short. Remember to cancel the Handler in onPause.
Can I publish a flashlight app on the Play Store?
Yes, though the store is already full of them and a plain one is unlikely to get downloads. If you do publish, keep permissions minimal — torch apps that ask for contacts or location get flagged in review and distrusted by users.
What minimum SDK should I use?
API 24, Android 7.0. That covers almost every phone in use and supports setTorchMode, which is far simpler than the old Camera.Parameters approach. Going lower means writing legacy code for very few extra devices.
It works on my phone but not on my friend's. Why?
Different camera hardware layouts. The detection loop in this guide handles nearly all of them. A small number of older Samsung and Xiaomi devices need the legacy Camera API, but those are now rare enough to ignore.

Written by Habib Baloch

I build Android apps, websites and Ubuntu servers, and write down exactly how I did it.

Ask me about this guide →