This guide covers how to build an Android APK file for the Twin Style mobile app.
Twin Style uses a hybrid app architecture with Capacitor:
Because Twin Style uses Next.js API routes, the mobile app requires a running server to function. The app cannot work completely offline as a standalone static app.
Two deployment models:
http://localhost:3000)https://your-domain.com)cd app
npm install
# Add the Android platform
npx cap add android
This creates an android/ directory with the native Android project.
# Copy web assets to Android project
npx cap sync android
For debug builds (unsigned, for testing):
npm run build:android
Or manually:
cd android
./gradlew assembleDebug
cd ..
For release builds (requires signing):
npm run build:android:release
Or manually:
cd android
./gradlew assembleRelease
cd ..
Debug APK location:
app/android/app/build/outputs/apk/debug/app-debug.apk
Release APK location:
app/android/app/build/outputs/apk/release/app-release-unsigned.apk
The repository includes an automated GitHub Actions workflow that builds APKs in the cloud.
debug or releaseThe workflow automatically runs when:
main branchmainapp/twin-style-debug-apk or twin-style-release-apk# Start emulator from Android Studio or:
emulator -avd YOUR_AVD_NAME
# Install APK
adb install app/android/app/build/outputs/apk/debug/app-debug.apk
The app needs to connect to a running Twin Style server. You have two options:
For testing, connect to your local development server:
cd app
npm run dev
# On macOS/Linux
ifconfig | grep "inet "
# On Windows
ipconfig
# In app directory
export CAPACITOR_SERVER_URL="http://YOUR_LOCAL_IP:3000"
npx cap sync android
Note: Your mobile device must be on the same Wi-Fi network as your computer.
For production use, deploy your Next.js app to a server first:
https://twinstyle.yourdomain.com)capacitor.config.ts:
server: {
url: 'https://twinstyle.yourdomain.com',
cleartext: false,
}
For production distribution (Google Play Store), you need to sign your APK:
keytool -genkey -v -keystore my-release-key.keystore \
-alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
android/app/build.gradle:
android {
signingConfigs {
release {
storeFile file("path/to/my-release-key.keystore")
storePassword "your-keystore-password"
keyAlias "my-key-alias"
keyPassword "your-key-password"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
cd android
./gradlew assembleRelease
⚠️ Security Note: Never commit keystores or passwords to Git. Use environment variables or secure secret management.
Solution:
ANDROID_HOME environment variable:
export ANDROID_HOME=$HOME/Android/Sdk
export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools
Solution:
./gradlew --refresh-dependenciesrm -rf ~/.gradle/caches/Solution:
Solutions:
npm run dev)localhostcapacitor.config.ts is correctCommon solutions:
# Clean build
cd android
./gradlew clean
# Update Gradle wrapper
./gradlew wrapper --gradle-version=8.14.3
# Sync project with Gradle files
./gradlew --refresh-dependencies
Solution: Capacitor 8.x requires Node.js ≥ 22.0.0. Update Node:
# Using nvm
nvm install 22
nvm use 22
# Or download from nodejs.org
By default, Gradle builds a universal APK. To build for specific architectures:
Edit android/app/build.gradle:
android {
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk true
}
}
}
This creates:
app-armeabi-v7a-release.apk (32-bit ARM)app-arm64-v8a-release.apk (64-bit ARM)app-x86-release.apk (32-bit x86)app-x86_64-release.apk (64-bit x86)app-universal-release.apk (all architectures)Most modern devices use arm64-v8a.