# Complete UX (Main)

Display the full KinesteX experience with personalized workout plan selection based on category. Includes user survey, assessment, and personalized schedule generation. This is the easiest integration option.

**Available Plan Categories:**
| Plan Category | Key |
|---------------|-----|
| Strength | Strength |
| Cardio | Cardio |
| Weight Management | Weight Management |
| Rehabilitation | Rehabilitation |
| Custom | Custom |

**Define Plan Category**

First, define the plan category for personalized fitness goals:

_Swift (iOS)_
```swift
// Plan category for personalized fitness goals
@State private var planCategory: PlanCategory = .Cardio
```

_Kotlin (Android)_
```kotlin
// Plan category for personalized fitness goals
private var planCategory = PlanCategory.Cardio
```

_React Native_
```jsx
// Plan category for personalized fitness goals
const planCategory = PlanCategory.Cardio; // Strength, Cardio, etc.;
```

_Flutter_
```dart
// Plan category for personalized fitness goals
PlanCategory planCategory = PlanCategory.Cardio;
```

_HTML / JavaScript_
```html
// Plan category for personalized fitness goals
const planCategory = "Cardio";
```

_React (TypeScript)_
```tsx
// Plan category for personalized fitness goals
const planCategory = PlanCategory.Cardio; // Strength, Cardio, etc.
```

**Display Category View**

Display the category-based view with real-time message handling:

_Swift (iOS)_
```swift
kinestex.createCategoryView(
    planCategory: planCategory,
    user: user, // optional: can be nil
    isLoading: $isLoading,
    customParams: ["style": "dark"], // dark or light theme
    onMessageReceived: { message in
        switch message {
        case .kinestex_launched(let data):
            print("KinesteX Launched: \(data)")
        case .finished_workout(let data):
            print("Workout Finished: \(data)")
        case .exit_kinestex(let data):
            showKinesteX = false // Dismiss the view
        default:
            print("Received \(message)")
            break
        }
    }
)
// OPTIONAL: Display loading screen during view initialization
.overlay(
    Group {
        if showAnimation {
            Text("Aifying workouts...")
                .foregroundColor(.black)
                .font(.caption)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(Color.white)
        }
    }
)
.onChange(of: isLoading) { newValue in
    withAnimation(.easeInOut(duration: 2.5)) {
        showAnimation = !newValue
    }
}
```

_Kotlin (Android)_
```kotlin
KinesteXSDK.createMainView(
    context = this,
    planCategory = planCategory,
    user = userDetails, // optional user details
    customParams = mutableMapOf("style" to "dark"),
    isLoading = viewModel.isLoading,
    onMessageReceived = { message ->
        when (message) {
            is WebViewMessage.KinestexLaunched ->
                println("KinesteX Launched")
            is WebViewMessage.FinishedWorkout ->
                println("Workout Finished: ${message.data}")
            is WebViewMessage.ExitKinestex -> finish()
            else -> println("Received: $message")
        }
    },
    permissionHandler = this
)
```

_React Native_
```jsx
// postData structure
const postData: IPostData = {
  key: 'YOUR_API_KEY',
  userId: 'YOUR_USER_ID',
  company: 'YOUR_COMPANY_NAME',
  planCategory: planCategory,
  style: {
    style: 'dark', // or 'light'
  },
};

<KinestexSDK
  data={postData}
  integrationOption={IntegrationOption.MAIN}
  handleMessage={(type, data) => {
    switch (type) {
      case 'kinestex_launched':
        console.log('KinesteX Launched:', data);
        break;
      case 'finished_workout':
        console.log('Workout Finished:', data);
        break;
      case 'exit_kinestex':
        setShowKinesteX(false);
        break;
    }
  }}
/>
```

_Flutter_
```dart
KinesteXAIFramework.createMainView(
  isShowKinestex: showKinesteX,
  planCategory: planCategory,
  customParams: {"style": "dark"},
  isLoading: ValueNotifier<bool>(false),
  onMessageReceived: (message) {
    if (message is KinestexLaunched) {
      print('KinesteX Launched');
    } else if (message is FinishedWorkout) {
      print('Workout Finished: ${message.data}');
    } else if (message is ExitKinestex) {
      setState(() {
        showKinesteX.value = false;
      });
    }
  },
)
```

_HTML / JavaScript_
```html
// Add planCategory to postData
const postData = {
  // ... all initial fields
  planCategory: planCategory, // "Strength", "Cardio", etc.
};

const srcURL = "https://ai.kinestex.com";
webView.src = srcURL;
webView.onload = () => {
  sendMessage();
};

// Handle messages
window.addEventListener('message', (event) => {
  const { type, data } = event.data;
  switch (type) {
    case 'kinestex_launched':
      console.log('KinesteX Launched:', data);
      break;
    case 'exit_kinestex':
      // Hide the iframe
      break;
  }
});
```

_React (TypeScript)_
```tsx
import { IntegrationOption, KinesteXSDK, PlanCategory, type IPostData } from 'kinestex-sdk-react-ts';

// postData structure
const postData: IPostData = {
  key: 'YOUR_API_KEY',
  userId: 'YOUR_USER_ID',
  company: 'YOUR_COMPANY_NAME',
  planCategory: PlanCategory.Cardio,
  style: {
    style: 'dark',
  },
};

<KinesteXSDK
  data={postData}
  integrationOption={IntegrationOption.MAIN}
  handleMessage={(type, data) => {
    switch (type) {
      case 'kinestex_launched':
        console.log('KinesteX Launched:', data);
        break;
      case 'finished_workout':
        console.log('Workout Finished:', data);
        break;
      case 'exit_kinestex':
        setShowKinesteX(false);
        break;
    }
  }}
/>
```

**Complete Example**

Full implementation example with all required setup:

_Swift (iOS)_
```swift
import SwiftUI
import KinesteXAIKit

struct MainViewIntegration: View {
    @State private var showKinesteX = false
    @State private var isLoading = false

    // Replace with your KinesteX credentials
    let kinesteXKit = KinesteXAIKit(
        apiKey: "YOUR API KEY",
        companyName: "YOUR COMPANY NAME",
        userId: "YOUR USER ID"
    )

    // Plan category for personalized fitness goals
    @State private var planCategory: PlanCategory = .Cardio

    var body: some View {
        VStack {
            Text("KinesteX Main View")
                .font(.title)
                .padding()

            Spacer()

            Button(action: {
                showKinesteX.toggle()
            }) {
                Text("Open Main View")
                    .font(.title3)
                    .foregroundColor(.white)
                    .bold()
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.green.cornerRadius(10))
                    .padding(.horizontal)
            }

            Spacer()
        }
        .fullScreenCover(isPresented: $showKinesteX) {
            kinestex.createCategoryView(
                planCategory: planCategory,
                user: nil,
                isLoading: $isLoading,
                customParams: ["style": "light"],
                onMessageReceived: { message in
                    switch message {
                    case .exit_kinestex(_):
                        showKinesteX = false
                    default:
                        print("Message received: \(message)")
                    }
                }
            )
        }
    }
}

#Preview {
    MainViewIntegration()
}
```

_Kotlin (Android)_
```kotlin
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.mutableStateOf
import com.kinestex.kinestexsdkkotlin.GenericWebView
import com.kinestex.kinestexsdkkotlin.KinesteXSDK
import com.kinestex.kinestexsdkkotlin.PlanCategory
import com.kinestex.kinestexsdkkotlin.PermissionHandler
import com.kinestex.kinestexsdkkotlin.WebViewMessage
import kotlinx.coroutines.flow.MutableStateFlow

class MainViewActivity : ComponentActivity(), PermissionHandler {
    private val viewModel = MainViewModel()

    // OPTIONAL: UserDetails to customize workout intensity and calorie estimation
    // Note: User details are only used on-device during the session
    private val userDetails = UserDetails(
        age = 30,
        height = 180,
        weight = 75,
        gender = Gender.MALE,
        lifestyle = Lifestyle.ACTIVE
    )

    // Custom data for the WebView
    private val data = mutableMapOf<String, Any>()

    // Store reference to the KinesteX WebView
    private var kinesteXWebView: GenericWebView? = null

    // Register permission launcher
    private val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted: Boolean ->
        // Pass permission result to KinesteX webview
        kinesteXWebView?.handlePermissionResult(isGranted)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        data["style"] = "light"

        setContent {
            val webView = KinesteXSDK.createMainView(
                context = this,
                planCategory = PlanCategory.Cardio,
                user = userDetails,
                customParams = data,
                isLoading = viewModel.isLoading,
                onMessageReceived = ::handleWebViewMessage,
                permissionHandler = this
            ) as GenericWebView
            kinesteXWebView = webView
            webView.Render()
        }
    }

    private fun handleWebViewMessage(message: WebViewMessage) {
        when (message) {
            is WebViewMessage.ExitKinestex -> finish()
            is WebViewMessage.KinestexLaunched -> viewModel.isLoading.value = false
            else -> Toast.makeText(this, "Received: $message", Toast.LENGTH_SHORT).show()
        }
    }

    // When request is sent, display system dialog for camera access
    override fun requestCameraPermission() {
        requestPermissionLauncher.launch(Manifest.permission.CAMERA)
    }
}

class MainViewModel {
    val isLoading = MutableStateFlow(true)
}
```

_React Native_
```jsx
import React, { useState } from 'react';
import { View, Button, StyleSheet } from 'react-native';
import KinestexSDK from 'kinestex-sdk-react-native';
import { IntegrationOption, IPostData, PlanCategory } from 'kinestex-sdk-react-native/src/types';

const MainViewIntegration = () => {
  const [showKinesteX, setShowKinesteX] = useState(false);

  const postData: IPostData = {
    key: 'YOUR_API_KEY',
    userId: 'YOUR_USER_ID',
    company: 'YOUR_COMPANY_NAME',
    planCategory: PlanCategory.Cardio,
    style: {
      style: 'dark',
    },
  };

  return (
    <View style={styles.container}>
      {showKinesteX ? (
        <KinestexSDK
          data={postData}
          integrationOption={IntegrationOption.MAIN}
          handleMessage={(type, data) => {
            if (type === 'exit_kinestex') {
              setShowKinesteX(false);
            }
          }}
        />
      ) : (
        <Button
          title="Open Main View"
          onPress={() => setShowKinesteX(true)}
        />
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1 }
});

export default MainViewIntegration;
```

_Flutter_
```dart
import 'package:flutter/material.dart';
import 'package:kinestex_sdk_flutter/kinestex_sdk.dart';
import 'package:permission_handler/permission_handler.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await KinesteXAIFramework.initialize(
    apiKey: "your_api_key",
    companyName: "your_company_name",
    userId: "your_user_id",
  );
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void dispose() {
    disposeKinesteXAIFramework();
    super.dispose();
  }

  Future<void> disposeKinesteXAIFramework() async {
    await KinesteXAIFramework.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'KinesteX Main View',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ValueNotifier<bool> showKinesteX = ValueNotifier<bool>(false);

  @override
  void initState() {
    super.initState();
    _checkCameraPermission();
  }

  void _checkCameraPermission() async {
    if (await Permission.camera.request() != PermissionStatus.granted) {
      _showCameraAccessDeniedAlert();
    }
  }

  void _showCameraAccessDeniedAlert() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text("Camera Permission Denied"),
          content: const Text("Camera access is required for this app to function properly."),
          actions: <Widget>[
            TextButton(
              child: const Text("OK"),
              onPressed: () => Navigator.of(context).pop(),
            ),
          ],
        );
      },
    );
  }

  void handleWebViewMessage(WebViewMessage message) {
    if (message is ExitKinestex) {
      setState(() {
        showKinesteX.value = false;
      });
    }
  }

  Widget createMainView() {
    return Center(
      child: KinesteXAIFramework.createMainView(
        isShowKinestex: showKinesteX,
        planCategory: PlanCategory.Cardio,
        customParams: {
          "style": "dark",
        },
        isLoading: ValueNotifier<bool>(false),
        onMessageReceived: handleWebViewMessage,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder(
      valueListenable: showKinesteX,
      builder: (context, isShowKinesteX, child) {
        return isShowKinesteX
            ? SafeArea(
                child: createMainView(),
              )
            : Scaffold(
                body: Center(
                  child: ElevatedButton(
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 40,
                        vertical: 20,
                      ),
                      backgroundColor: Colors.green,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                    ),
                    onPressed: () {
                      showKinesteX.value = true;
                    },
                    child: const Text(
                      'Start Main View',
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.bold,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
              );
      },
    );
  }
}
```

_HTML / JavaScript_
```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>KinesteX: Complete User Experience</title>
  </head>
  <body>
    <button id="startKinesteX">Start KinesteX</button>
    <div
      id="webViewContainer"
      style="
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        display: none;
      "
    >
      <iframe
        id="webView"
        frameborder="0"
        allow="camera; autoplay; accelerometer; gyroscope; magnetometer"
        sandbox="allow-same-origin allow-scripts"
        style="width: 100%; height: 100%"
      ></iframe>
    </div>

    <script>
      const postData = {
        userId: "YOUR_USER_ID",
        company: "YOUR_COMPANY",
        key: "YOUR_API_KEY",
        style: "dark",
        planC: "Strength",
      };

      const webViewContainer = document.getElementById("webViewContainer");
      const webView = document.getElementById("webView");
      const startButton = document.getElementById("startKinesteX");
      const srcURL = "https://ai.kinestex.com";

      function sendMessage() {
        if (webView.contentWindow) {
          webView.contentWindow.postMessage(postData, srcURL);
        } else {
          setTimeout(() => {
            try {
              webView.contentWindow.postMessage(postData, srcURL);
            } catch {
              webView.contentWindow.postMessage(postData, srcURL);
            }
          }, 100);
        }
      }

      startButton.addEventListener("click", () => {
        const isVisible = webViewContainer.style.display !== "none";
        webViewContainer.style.display = isVisible ? "none" : "block";

        if (!isVisible) {
          webView.src = srcURL;
          webView.onload = () => {
            sendMessage();
          };
        }
      });

      window.addEventListener("message", (event) => {
        if (event.origin !== "https://ai.kinestex.com") return;

        try {
          const message = JSON.parse(event.data);
          switch (message.type) {
            case "kinestex_loaded":
              sendMessage();
              break;
            case "exercise_completed":
              console.log("Exercise completed:", message.data);
              break;
            case "plan_unlocked":
              console.log("Workout plan unlocked:", message.data);
              break;
            case "exit_kinestex":
              webViewContainer.style.display = "none";
              break;
            default:
              console.log("Unhandled message:", message);
          }
        } catch (e) {
          console.error("Failed to parse message:", e);
        }
      });
    </script>
  </body>
</html>
```

_React (TypeScript)_
```tsx
import React, { useState } from 'react';
import {
  IntegrationOption,
  KinesteXSDK,
  PlanCategory,
  type IPostData,
} from 'kinestex-sdk-react-ts';

const MainViewIntegration: React.FC = () => {
  const [showKinesteX, setShowKinesteX] = useState(false);

  const postData: IPostData = {
    key: 'YOUR_API_KEY',
    userId: 'YOUR_USER_ID',
    company: 'YOUR_COMPANY_NAME',
    planCategory: PlanCategory.Cardio,
    style: {
      style: 'dark',
    },
  };

  return (
    <div style={{ height: '100vh', width: '100vw' }}>
      {showKinesteX ? (
        <KinesteXSDK
          data={postData}
          integrationOption={IntegrationOption.MAIN}
          handleMessage={(type, data) => {
            if (type === 'exit_kinestex') {
              setShowKinesteX(false);
            }
          }}
        />
      ) : (
        <button onClick={() => setShowKinesteX(true)}>
          Open Main View
        </button>
      )}
    </div>
  );
};

export default MainViewIntegration;
```

---
Source: https://www.kinestex.com/docs/integration/main-view · Index: https://www.kinestex.com/llms.txt
