Native module:React native, showing black screen when integrating camera

I am using react-native-cli version 0.74.0 and Andorid device for debugging linux os for writing code.

I am trying to make a Camera app using a native module.
You will ask if there is already a camera package so why I am doing this?
I have some customisation including face recognition.

Below is the code I implemented. Let me tell you, before I implemented cameraType props camera was working fine.

MainActivity.kt

package com.real_time_face_detection

import android.os.Bundle
import android.content.pm.PackageManager
import android.widget.FrameLayout
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultReactActivityDelegate
import android.util.Log
import android.widget.Toast

class MainActivity : ReactActivity() {

    private var cameraView: CameraView? = null

    private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
        if (isGranted) {
            Log.d("FaceRecognition", "Camera permission granted log from MainActivity.kt")
            showToast("Camera permission granted")
            initializeCameraView()
        } else {
            showToast("Camera permission denied")
            Log.d("FaceRecognition", "Camera permission denied log from MainActivity.kt")
        }
    }

    private fun showToast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
            Log.d("FaceRecognition", "Camera permission already granted log from MainActivity.kt")
            initializeCameraView()
            Log.d("FaceRecognition", "initialized Camera ---- log from MainActivity.kt")
        } else {
            Log.d("FaceRecognition", "Requesting camera permission log from MainActivity.kt")
            requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
            Log.d("FaceRecognition", "Requesting camera permission done ------- log from MainActivity.kt")
        }
    }

    private fun initializeCameraView() {
        if (cameraView == null) {
            cameraView = CameraView(this, this) // Pass both context and lifecycle owner
            Log.d("FaceRecognition", "Initializing CameraView log from MainActivity.kt")
            addContentView(cameraView, FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT))
            Log.d("FaceRecognition", "Initializing CameraView done---- log from MainActivity.kt")
        }
    }

    override fun getMainComponentName(): String = "real_time_face_detection"

    /**
     * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
     * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
     */
    override fun createReactActivityDelegate(): ReactActivityDelegate =
        DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)

    private val fabricEnabled: Boolean
        get() = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
}

FaceDetectionPackage.kt

package com.real_time_face_detection

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class FaceDetectionPackage : ReactPackage {
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        return listOf(FaceDetectionModule(reactContext))
    }

    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return listOf(CameraViewManager())
    }
}

FaceDetectionModule.kt

package com.real_time_face_detection

import android.util.Log
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.module.annotations.ReactModule
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.Face
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions

@ReactModule(name = FaceDetectionModule.NAME)
class FaceDetectionModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {

    companion object {
        const val NAME = "FaceDetection"
    }

    private val detector = FaceDetection.getClient(FaceDetectorOptions.Builder()
        .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
        .build())

    override fun getName(): String {
        return NAME
    }

    fun processImage(image: InputImage, callback: (List<Face>) -> Unit) {
        detector.process(image)
            .addOnSuccessListener { faces ->
                Log.d("FaceRecognition", "Faces detected: ${faces.size} log from FaceDetectionModule.kt")
                callback(faces)
            }
            .addOnFailureListener { e ->
                Log.e("FaceRecognition", "Face detection failed log from FaceDetectionModule.kt", e)
                callback(emptyList())
            }
    }
}

CameraViewManager.kt

package com.real_time_face_detection

import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import android.util.Log
import androidx.lifecycle.LifecycleOwner

class CameraViewManager : SimpleViewManager<CameraView>() {

    override fun getName(): String {
        return "CameraView"
    }

    override fun createViewInstance(reactContext: ThemedReactContext): CameraView {
        Log.d("FaceRecognition", "create view instance, log from CameraViewManager.kt")
        val lifecycleOwner = reactContext.currentActivity as? LifecycleOwner
            ?: throw IllegalStateException("Context is not a LifecycleOwner")
        val cameraView = CameraView(reactContext, lifecycleOwner = lifecycleOwner)
        val faceDetectionModule = reactContext.getNativeModule(FaceDetectionModule::class.java)
        faceDetectionModule?.let {
            cameraView.setFaceDetectionModule(it)
        }
        Log.d("FaceRecognition", "faceDetectionModule set, log from CameraViewManager.kt")
        return cameraView
    }

    @ReactProp(name = "cameraType")
    fun setCameraType(view: CameraView, cameraType: String) {
        Log.d("FaceRecognition", "Camera type from manager view - $cameraType log from CameraViewManager.kt")
        view.startCamera(cameraType)
    }
}

MainApplication.kt

package com.real_time_face_detection

import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.soloader.SoLoader

class MainApplication : Application(), ReactApplication {

  override val reactNativeHost: ReactNativeHost =
      object : DefaultReactNativeHost(this) {
        override fun getPackages(): List<ReactPackage> =
            PackageList(this).packages.apply {
              // Packages that cannot be autolinked yet can be added manually here, for example:
              // add(MyReactNativePackage())
              add(FaceDetectionPackage()) // <-- Add this line
            }

        override fun getJSMainModuleName(): String = "index"

        override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG

        override val isNewArchEnabled: Boolean get() = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
        override val isHermesEnabled: Boolean get() = BuildConfig.IS_HERMES_ENABLED
      }

  override val reactHost: ReactHost
    get() = getDefaultReactHost(applicationContext, reactNativeHost)

  override fun onCreate() {
    super.onCreate()
    SoLoader.init(this, false)
    if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
      // If you opted-in for the New Architecture, we load the native entry point for this app.
      load()
    }
  }
}

CameraView.kt

package com.real_time_face_detection

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.widget.FrameLayout
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.Face
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class CameraView @JvmOverloads constructor(
    context: Context,
    private val lifecycleOwner: LifecycleOwner,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    private var cameraProvider: ProcessCameraProvider? = null
    private var cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
    private var previewView: PreviewView = PreviewView(context)
    private var faceOverlay: FaceOverlay = FaceOverlay(context)
    private var faceDetectionModule: FaceDetectionModule? = null

    init {
        Log.d("FaceRecognition", "Initializing CameraView log from CameraView.kt")
        setupLayout()
        logLifecycleState()
    }

    private fun setupLayout() {
        Log.d("FaceRecognition", "Setting up layout log from CameraView.kt")
        previewView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
        faceOverlay.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
        this.addView(previewView)
        this.addView(faceOverlay)
        Log.d("FaceRecognition", "Layout setup completed log from CameraView.kt")
    }

    fun setFaceDetectionModule(module: FaceDetectionModule) {
        faceDetectionModule = module
        Log.d("FaceRecognition", "FaceDetectionModule set log from CameraView.kt")
    }

    private fun logLifecycleState() {
        val lifecycle = lifecycleOwner.lifecycle
        val observer = LifecycleEventObserver { _, event ->
            Log.d("FaceRecognition", "Lifecycle event: $event log from CameraView.kt")
        }
        lifecycle.addObserver(observer)
    }

    fun startCamera(cameraType: String) {
        Log.d("FaceRecognition", "Starting camera with type: $cameraType log from CameraView.kt")
        val cameraSelector = if (cameraType == "front") {
            CameraSelector.DEFAULT_FRONT_CAMERA
        } else {
            CameraSelector.DEFAULT_BACK_CAMERA
        }
        val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
        cameraProviderFuture.addListener({
            cameraProvider = cameraProviderFuture.get()
            Log.d("FaceRecognition", "CameraProvider obtained log from CameraView.kt")
            bindCameraUseCases(cameraSelector)
        }, ContextCompat.getMainExecutor(context))
    }

    private fun bindCameraUseCases(cameraSelector: CameraSelector) {
        val cameraProvider = cameraProvider ?: return
        val lifecycleState = lifecycleOwner.lifecycle.currentState
        Log.d("FaceRecognition", "Lifecycle state during bind: $lifecycleState log from CameraView.kt")

        if (lifecycleState.isAtLeast(Lifecycle.State.STARTED)) {
            val preview = Preview.Builder().build().also {
                Log.d("FaceRecognition", "Setting SurfaceProvider for preview log from CameraView.kt")
                // Ensure this is run on the main thread
                Handler(Looper.getMainLooper()).post {
                    it.setSurfaceProvider(previewView.surfaceProvider)
                    Log.d("FaceRecognition", "SurfaceProvider set for preview log from CameraView.kt")
                    Log.d("FaceRecognition", "PreviewView visibility: ${previewView.visibility} log from CameraView.kt")
                    Log.d("FaceRecognition", "PreviewView width: ${previewView.width}, height: ${previewView.height} log from CameraView.kt")
                }
            }

            val imageAnalyzer = ImageAnalysis.Builder().build().also {
                Log.d("FaceRecognition", "Setting Analyzer for ImageAnalysis log from CameraView.kt")
                it.setAnalyzer(cameraExecutor, { imageProxy -> processImageProxy(imageProxy) })
                Log.d("FaceRecognition", "Analyzer set for ImageAnalysis log from CameraView.kt")
            }

            try {
                cameraProvider.unbindAll()
                Log.d("FaceRecognition", "All use cases unbound log from CameraView.kt")
                cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageAnalyzer)
                Log.d("FaceRecognition", "Camera use cases bound log from CameraView.kt")
            } catch (exc: Exception) {
                Log.e("FaceRecognition", "Use case binding failed log from CameraView.kt", exc)
            }
        } else {
            Log.e("FaceRecognition", "Lifecycle state not suitable for binding camera use cases log from CameraView.kt")
        }
    }

    private fun processImageProxy(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image ?: return
        val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)

        Log.d("FaceRecognition", "Processing image log from CameraView.kt")
        faceDetectionModule?.processImage(image) { faces ->
            faceOverlay.setFaces(faces)
            Log.d("FaceRecognition", "Faces detected: ${faces.size} log from CameraView.kt")
        }

        imageProxy.close()
    }

    fun stopCamera() {
        Log.d("FaceRecognition", "Stopping camera log from CameraView.kt")
        cameraProvider?.unbindAll()
        cameraExecutor.shutdown()
    }

    private class FaceOverlay(context: Context) : View(context) {

        private var faces: List<Face> = emptyList()
        private val paint = Paint().apply {
            color = ContextCompat.getColor(context, android.R.color.holo_green_light)
            style = Paint.Style.STROKE
            strokeWidth = 8f
        }

        fun setFaces(faces: List<Face>) {
            this.faces = faces
            invalidate()
        }

        override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
            for (face in faces) {
                val bounds: Rect = face.boundingBox
                canvas.drawRect(bounds, paint)
            }
        }
    }
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <application
        android:name=".MainApplication"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:allowBackup="false"
        android:theme="@style/AppTheme">
        
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
            android:launchMode="singleTask"
            android:windowSoftInputMode="adjustResize"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        
    </application>

</manifest>

App.jd – React-native side

import React, {useState, useEffect} from 'react';
import {View, Button, StyleSheet, PermissionsAndroid} from 'react-native';
import CameraViewWrapper from './CameraViewWrapper';

const App = () => {
  const [cameraStarted, setCameraStarted] = useState(false);
  const [cameraType, setCameraType] = useState('back');
  console.log(cameraType);
  console.log(cameraStarted);

  useEffect(() => {
    requestCameraPermission();
  }, []);

  const requestCameraPermission = async () => {
    console.log('ll');
    try {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.CAMERA,
        {
          title: 'Camera Permission',
          message: 'App needs camera permission',
          buttonNeutral: 'Ask Me Later',
          buttonNegative: 'Cancel',
          buttonPositive: 'OK',
        },
      );
      console.log(granted, ';;;;');
      if (granted === PermissionsAndroid.RESULTS.GRANTED) {
        console.log(granted, '=============');
        console.log(PermissionsAndroid.RESULTS.GRANTED, '-------------');
        setCameraStarted(true);
      } else {
        console.log('Camera permission denied');
      }
    } catch (err) {
      console.warn(err);
    }
  };

  const startDetection = () => {
    requestCameraPermission();
  };

  const stopDetection = () => {
    setCameraStarted(false);
  };

  const switchToBackCamera = () => {
    setCameraType('back');
  };

  const switchToFrontCamera = () => {
    setCameraType('front');
  };

  return (
    <View style={styles.container}>
      {cameraStarted && (
        <CameraViewWrapper style={styles.cameraView} cameraType={cameraType} />
      )}
      <View style={styles.buttonContainer}>
        <Button title="Back Camera" onPress={switchToBackCamera} />
        <Button title="Front Camera" onPress={switchToFrontCamera} />
        <Button title="Start Detection" onPress={startDetection} />
        <Button title="Stop Detection" onPress={stopDetection} />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
  },
  cameraView: {
    flex: 1,
    height: 200,
    width: 200,
  },
  buttonContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    padding: 10,
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
    position: 'absolute',
    bottom: 0,
    width: '100%',
  },
});

export default App;

CamerViewWrapper.js

import {requireNativeComponent} from 'react-native';
import PropTypes from 'prop-types';
import React from 'react';

const CameraView = requireNativeComponent('CameraView');

const CameraViewWrapper = ({cameraType}) => {
  console.log('CameraType being passed ---: ', cameraType, CameraView); // Add this log
  return <CameraView style={{flex: 1}} cameraType={cameraType} />;
};

CameraViewWrapper.propTypes = {
  cameraType: PropTypes.string.isRequired,
};

export default CameraViewWrapper;

apply plugin: "com.android.application"
apply plugin: "org.jetbrains.kotlin.android"
apply plugin: "com.facebook.react"

/**
 * This is the configuration block to customize your React Native Android app.
 * By default you don't need to apply any configuration, just uncomment the lines you need.
 */
react {
    /* Folders */
    //   The root of your project, i.e. where "package.json" lives. Default is '..'
    // root = file("../")
    //   The folder where the react-native NPM package is. Default is ../node_modules/react-native
    // reactNativeDir = file("../node_modules/react-native")
    //   The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
    // codegenDir = file("../node_modules/@react-native/codegen")
    //   The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
    // cliFile = file("../node_modules/react-native/cli.js")

    /* Variants */
    //   The list of variants to that are debuggable. For those we're going to
    //   skip the bundling of the JS bundle and the assets. By default is just 'debug'.
    //   If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
    // debuggableVariants = ["liteDebug", "prodDebug"]

    /* Bundling */
    //   A list containing the node command and its flags. Default is just 'node'.
    // nodeExecutableAndArgs = ["node"]
    //
    //   The command to run when bundling. By default is 'bundle'
    // bundleCommand = "ram-bundle"
    //
    //   The path to the CLI configuration file. Default is empty.
    // bundleConfig = file(../rn-cli.config.js)
    //
    //   The name of the generated asset file containing your JS bundle
    // bundleAssetName = "MyApplication.android.bundle"
    //
    //   The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
    // entryFile = file("../js/MyApplication.android.js")
    //
    //   A list of extra flags to pass to the 'bundle' commands.
    //   See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
    // extraPackagerArgs = []

    /* Hermes Commands */
    //   The hermes compiler command to run. By default it is 'hermesc'
    // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
    //
    //   The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
    // hermesFlags = ["-O", "-output-source-map"]
}

/**
 * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
 */
def enableProguardInReleaseBuilds = false

/**
 * The preferred build flavor of JavaScriptCore (JSC)
 *
 * For example, to use the international variant, you can use:
 * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
 *
 * The international variant includes ICU i18n library and necessary data
 * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
 * give correct results when using with locales other than en-US. Note that
 * this variant is about 6MiB larger per architecture than default.
 */
def jscFlavor = 'org.webkit:android-jsc:+'

android {
    ndkVersion rootProject.ext.ndkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    compileSdk rootProject.ext.compileSdkVersion

    namespace "com.real_time_face_detection"
    defaultConfig {
        applicationId "com.real_time_face_detection"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"
    }
    signingConfigs {
        debug {
            storeFile file('debug.keystore')
            storePassword 'android'
            keyAlias 'androiddebugkey'
            keyPassword 'android'
        }
    }
    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }
        release {
            // Caution! In production, you need to generate your own keystore file.
            // see https://reactnative.dev/docs/signed-apk-android.
            signingConfig signingConfigs.debug
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        }
    }
}

dependencies {
    // The version of react-native is set by the React Native Gradle Plugin
    implementation("com.facebook.react:react-android")

    implementation "androidx.camera:camera-core:1.2.1"
    implementation "androidx.camera:camera-camera2:1.2.1"
    implementation "androidx.camera:camera-lifecycle:1.2.1"
    implementation "androidx.camera:camera-view:1.2.1"
    implementation "androidx.camera:camera-extensions:1.2.1"

    implementation "com.google.mlkit:face-detection:16.1.5"

    if (hermesEnabled.toBoolean()) {
        implementation("com.facebook.react:hermes-android")
    } else {
        implementation jscFlavor
    }
}

apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

I have tried everything, please let me know where I am doing wrong and what should I update so it can work.

previously when I was not using camerType props then it was working but when I added cameraType props it stopped working started showing black screen.
even I did restart the react-native, I rebuild many time still showing the same.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật