# एक फ़्लटर प्रोजेक्ट में लाइव एक्टिविटी iOS

हम एक फ़्लटर प्रोजेक्ट के लिए स्टॉपवॉच कार्यान्वयन के साथ एक लाइव एक्टिविटी बनाएंगे। इस प्रोजेक्ट में फ़्लटर-साइड कार्यान्वयन के साथ-साथ Xcode प्रोजेक्ट में नेटिव कार्यान्वयन भी शामिल होगा।

## फ़्लटर साइड कार्यान्वयन
हम एक क्लास बनाएंगे जो तीन मेथड लागू करेगी:
- `startLiveActivity()`
- `updateLiveActivity()`
- `stopLiveActivity()`।

```dart
class DynamicIslandManager {
  final String channelKey;
  late final MethodChannel _methodChannel;

  DynamicIslandManager({required this.channelKey}) {
    _methodChannel = MethodChannel(channelKey);
  }

  Future<void> startLiveActivity({required Map<String, dynamic> jsonData}) async {
    try {
      await _methodChannel.invokeListMethod('startLiveActivity', jsonData);
    } catch (e, st) {
      log(e.toString(), stackTrace: st);
    }
  }

  Future<void> updateLiveActivity(
      {required Map<String, dynamic> jsonData}) async {
    try {
      await _methodChannel.invokeListMethod('updateLiveActivity', jsonData);
    } catch (e, st) {
      log(e.toString(), stackTrace: st);
    }
  }

  Future<void> stopLiveActivity() async {
    try {
      await _methodChannel.invokeListMethod('stopLiveActivity');
    } catch (e, st) {
      log(e.toString(), stackTrace: st);
    }
  }
}
```
हम एक स्टॉपवॉच ऐप बनाएंगे, और स्टॉपवॉच टाइमर डेटा को नेटिव मेथड में पास करने के लिए, हमें एक डेटा मॉडल बनाना होगा और इसे JSON-जैसे मैप प्रारूप में मेथड चैनल इनवोकेशन में भेजना होगा।

```dart
class DynamicIslandStopwatchDataModel {
  final int elapsedSeconds;

  DynamicIslandStopwatchDataModel({
    required this.elapsedSeconds,
  });

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'elapsedSeconds': elapsedSeconds,
    };
  }
}
```
प्रत्येक मेथड में संबंधित नेटिव मेथड को कॉल करने के लिए आवश्यक कोड शामिल होगा।

यह फ़्लटर साइड पर इसे चलाने के लिए आवश्यक प्रारंभिक सेटअप था। इसके बाद, आवश्यक मेथड कॉल के साथ एक बेसिक स्टॉपवॉच ऐप UI लागू किया गया था।

```dart
class _StopWatchScreenState extends State<StopWatchScreen> {
  int seconds = 0;
  bool isRunning = false;
  Timer? timer;

  /// channel key is used to send data from flutter to swift side over
  /// a unique bridge (link between flutter & swift)
  final DynamicIslandManager diManager = DynamicIslandManager(channelKey: 'PW');

  void startTimer() {
    setState(() {
      isRunning = true;
    });

    // invoking startLiveActivity Method
    diManager.startLiveActivity(
      jsonData: DynamicIslandStopwatchDataModel(elapsedSeconds: 0).toMap(),
    );

    timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        seconds++;
      });
            // invoking the updateLiveActivity Method
      diManager.updateLiveActivity(
        jsonData: DynamicIslandStopwatchDataModel(
          elapsedSeconds: seconds,
        ).toMap(),
      );
    });
  }

  void stopTimer() {
    timer?.cancel();
    setState(() {
      seconds = 0;
      isRunning = false;
    });

    // invoking the stopLiveActivity Method
    diManager.stopLiveActivity();
  }

  @override
  void dispose() {
    timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Stopwatch App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Stopwatch: $seconds seconds',
              style: const TextStyle(fontSize: 24),
            ),
            const SizedBox(height: 20),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                ElevatedButton(
                  onPressed: isRunning ? null : startTimer,
                  child: const Text('Start'),
                ),
                const SizedBox(width: 20),
                ElevatedButton(
                  onPressed: isRunning ? stopTimer : null,
                  child: const Text('Stop'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
```
## नेटिव साइड कार्यान्वयन

### Xcode प्रोजेक्ट कॉन्फ़िगरेशन

1. Xcode पर अपना प्रोजेक्ट खोलें और जनरल टैब में न्यूनतम डिप्लॉयमेंट को iOS 16.1 पर सेट करें
<img src="/flutter-flutter-ios-live-activities-1.webp" alt="Xcode जनरल टैब न्यूनतम डिप्लॉयमेंट को iOS 16.1 पर सेट करते हुए दिखा रहा है"/>

2. `Info.plist` में एक नई कुंजी `NSSupportsLiveActivities` जोड़ें और इसका मान `YES` (बूलियन) पर सेट करें।

### Info.plist
```xml
<key>NSSupportsLiveActivities</key>
<true/>
```
3. एक्शन बार से, **File > New > Target** चुनें और `WidgetExtensionand` खोजें और एक नया विजेट बनाएं।

## कोड साइड कार्यान्वयन
आइए `LiveActivityManager` नामक एक क्लास बनाएं जो हमारे डायनामिक आइलैंड के लिए लाइव एक्टिविटीज़ का प्रबंधन करेगी। इस क्लास में तीन मेथड होंगे: `startLiveActivity()`, `updateLiveActivity()`, और `stopLiveActivity()`।

**Swift**

```swift
import ActivityKit
import Flutter
import Foundation

@available(iOS 16.1, *)
class LiveActivityManager {

    private var stopwatchActivity: Activity<StopwatchWidgetAttributes>? = nil

    func startLiveActivity(data: [String: Any]?, result: FlutterResult) {
        let attributes = StopwatchWidgetAttributes()

        if let info = data {
            let state = StopwatchWidgetAttributes.ContentState(
                elapsedTime: info["elapsedSeconds"] as? Int ?? 0
            )
            stopwatchActivity = try? Activity<StopwatchWidgetAttributes>.request(
                attributes: attributes, contentState: state, pushType: nil)
        } else {
            result(FlutterError(code: "418", message: "Live activity didn't invoked", details: nil))
        }
    }

    func updateLiveActivity(data: [String: Any]?, result: FlutterResult) {
        if let info = data {
            let updatedState = StopwatchWidgetAttributes.ContentState(
                elapsedTime: info["elapsedSeconds"] as? Int ?? 0
            )

            Task {
                await stopwatchActivity?.update(using: updatedState)
            }
        } else {
            result(FlutterError(code: "418", message: "Live activity didn't updated", details: nil))
        }
    }

    func stopLiveActivity(result: FlutterResult) {
        do {
            Task {
                await stopwatchActivity?.end(using: nil, dismissalPolicy: .immediate)
            }
        } catch {
            result(FlutterError(code: "418", message: error.localizedDescription, details: nil))
        }
    }
}
```

प्रत्येक मेथड की अपनी विशिष्ट कार्यक्षमता होती है। `startLiveActivity()` (जैसा कि नाम से पता चलता है) लाइव एक्टिविटी शुरू करने के लिए ज़िम्मेदार है, जो डायनामिक आइलैंड की कार्यक्षमता को ट्रिगर करता है। इसी तरह, `stopLiveActivity()` और `updateLiveActivity()` लाइव एक्टिविटी को रोकने और डायनामिक आइलैंड पर प्रदर्शित डेटा को अपडेट करने का काम करते हैं।

इसके बाद, `StopWatchDIWidgetLiveActivity.swift` खोलें और `StopwatchDIWidgetAttributes` स्ट्रक्ट को संशोधित करें (जैसा कि स्निपेट में दिखाया गया है)। यह एट्रिब्यूट स्ट्रक्ट डेटा मॉडल के रूप में कार्य करता है जो UI पर प्रदर्शित की जाने वाली जानकारी (सीधे फ़्लटर से) रखता है और आवश्यकतानुसार UI को भी संशोधित कर सकता है।

**Swift**

```swift
struct StopwatchWidgetAttributes: ActivityAttributes {
    public typealias stopwatchStatus = ContentState

    public struct ContentState: Codable, Hashable {
        var elapsedTime: Int
    }
}
```
अब, केवल डायनामिक आइलैंड के लिए UI बचा है! (बहुत बढ़िया, हम यहाँ तक आ गए हैं!)
डायनामिक आइलैंड के लिए पूरा UI SwiftUI का उपयोग करके बनाया जाना चाहिए। इस लेख के लिए, एक सरल UI डिज़ाइन किया गया है (लेकिन आप इसे आवश्यकतानुसार अनुकूलित करने के लिए स्वतंत्र हैं)।

यह UI कोड `StopwatchWidgetLiveActivity` स्ट्रक्ट के अंदर लिखा जाना चाहिए। स्ट्रक्ट से मौजूदा कोड को हटा दें, और आप नीचे दिए गए कोड का पालन कर सकते हैं:

**SwiftUI**
```swift
struct StopwatchWidgetLiveActivity: Widget {

    func getTimeString(_ seconds: Int) -> String {
        let hours = seconds / 3600
        let minutes = (seconds % 3600) / 60
        let seconds = (seconds % 3600) % 60

        return hours == 0 ?
                String(format: "%02d:%02d", minutes, seconds)
                : String(format: "%02d:%02d:%02d", hours, minutes, seconds)
    }

    var body: some WidgetConfiguration {
        ActivityConfiguration(for: StopwatchWidgetAttributes.self) { context in
            HStack {
                Text("Time ellapsed")
                    .font(.system(size: 20, weight: .semibold))
                    .foregroundColor(.white)
                Spacer()
                Image(systemName: "timer")
                    .foregroundColor(.white)
                Spacer().frame(width: 10)
                Text(getTimeString(context.state.elapsedTime))
                    .font(.system(size: 24, weight: .semibold))
                    .foregroundColor(.yellow)
            }
            .padding(.horizontal)
            .activityBackgroundTint(Color.black.opacity(0.5))

        }
        dynamicIsland: { context in
            DynamicIsland {
                DynamicIslandExpandedRegion(.center) {
                    VStack(alignment: .center) {
                        Text("Pushwoosh Timer")
                        Spacer().frame(height: 24)
                        HStack {
                            Text("Time ellapsed")
                                .font(.system(size: 20, weight: .semibold))
                                .foregroundColor(.white)
                            Spacer()
                            Image(systemName: "timer")
                            Spacer().frame(width: 10)
                            Text(getTimeString(context.state.elapsedTime))
                                .font(.system(size: 24, weight: .semibold))
                                .foregroundColor(.yellow)
                        }.padding(.horizontal)
                    }
                }
            } compactLeading: {
                Image(systemName: "timer").padding(.leading, 4)
            } compactTrailing: {
                Text(getTimeString(context.state.elapsedTime)).foregroundColor(.yellow)
                    .padding(.trailing, 4)
            } minimal: {
                Image(systemName: "timer")
                    .foregroundColor(.yellow)
                    .padding(.all, 4)
            }
            .widgetURL(URL(string: "http://www.pushwoosh.com"))
            .keylineTint(Color.red)
        }
    }
}
```
`AppDelegate कोड` को भी संशोधित करना न भूलें।

**AppDelegate.swift**
```swift
import UIKit
import Flutter

@main
@objc class AppDelegate: FlutterAppDelegate {

    private let liveActivityManager: LiveActivityManager = LiveActivityManager()

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {

        GeneratedPluginRegistrant.register(with: self)

        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        let diChannel =  FlutterMethodChannel(name: "PW", binaryMessenger: controller.binaryMessenger)

        diChannel.setMethodCallHandler({ [weak self] (
            call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            switch call.method {
            case "startLiveActivity":
                self?.liveActivityManager.startLiveActivity(
                    data: call.arguments as? Dictionary<String,Any>,
                    result: result)
                break

            case "updateLiveActivity":
                self?.liveActivityManager.updateLiveActivity(
                    data: call.arguments as? Dictionary<String,Any>,
                    result: result)
                break

            case "stopLiveActivity":
                self?.liveActivityManager.stopLiveActivity(result: result)
                break

            default:
                result(FlutterMethodNotImplemented)
            }
        })

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}
```
और बस! इसमें डायनामिक आइलैंड को लागू करने और इसे फ़्लटर कोड से जोड़ने के लिए आवश्यक सब कुछ शामिल है!

## Pushwoosh कोड कार्यान्वयन
Pushwoosh फ़्लटर प्लगइन प्रोजेक्ट में लाइव एक्टिविटी के प्रबंधन के लिए दो मेथड प्रदान करता है।

```dart
  /// Default setup Live Activity
  Future<void> defaultSetup() async {
    await _channel.invokeMethod("defaultSetup");
  }

  /// Default start Live Activity
  /// [activityId] activity ID
  /// [attributes] attributes
  /// [content] content
  Future<void> defaultStart(String activityId, Map<String, dynamic> attributes, Map<String, dynamic> content) async {
    await _channel.invokeMethod("defaultStart", {"activityId": activityId, "attributes": attributes, "content": content});
  }
```
`defaultSetup()` मेथड आपको Pushwoosh साइड पर लाइव एक्टिविटी और टोकन की संरचना को प्रबंधित करने की अनुमति देता है।

एप्लिकेशन इनिशियलाइज़ेशन के दौरान इस मेथड को कॉल करें।

```dart
  Pushwoosh.initialize({"app_id": "XXXXX-XXXXX"});
/**
* Call this method `defaultSetup()`
*/
  Pushwoosh.getInstance.defaultSetup();
```
ऐप के इनिशियलाइज़ेशन के दौरान, Pushwoosh आपके पुश-टू-स्टार्ट टोकन को सर्वर पर भेजेगा, जो बाद में आपको API कॉल के माध्यम से लाइव एक्टिविटी शुरू करने की अनुमति देगा।
लाइव एक्टिविटी शुरू करने के लिए, आपको एक API अनुरोध करना होगा। नीचे अनुरोध का एक उदाहरण है:

```json
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "YOUR_AUTH_API_TOKEN",
    "notifications": [
      {
        "content": "Message",
        "title":"Title",
        "live_activity": {
          "event": "start", // `start` event
            "content-state": {
                "data": { // You need to pass the parameters in a dictionary with the key data.
                    "emoji": "dynamic data"
                }
            },
           "attributes-type": "DefaultLiveActivityAttributes",
            "attributes": {
                "data": {
                    "name": "static data"
                }
        }
        },
        "devices": [ "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" // HWID
       ],
        "live_activity_id": "your_activity_id"
      }
    ]
  }
}
```

[API पर अधिक विस्तृत दस्तावेज़ीकरण](/hi/developer/api-reference/ios-live-activities-api)

यदि आप एक सरल लाइव एक्टिविटी शुरू करना चाहते हैं, उदाहरण के लिए, अपने ऐप से, तो आप फ़्लटर में निम्नलिखित मेथड का उपयोग कर सकते हैं: `defaultStart(String activityId, Map<String, dynamic> attributes, Map<String, dynamic> content)`

इस मेथड को किसी भी ईवेंट पर कॉल किया जा सकता है जो लाइव एक्टिविटी की शुरुआत को ट्रिगर करता है।

```dart
// Function to start Live Activity
  void startLiveActivity() {
    // Create your activity ID
    String activityId = "stopwatch_activity";

    // Define the attributes you want to send to the Live Activity
    Map<String, dynamic> attributes = {
      'title': 'Stopwatch Activity',
      'description': 'This is a live activity for a stopwatch.'
    };

    // Define the content state to update on the Dynamic Island
    Map<String, dynamic> content = {
      'elapsedSeconds': 0
    };

    // Call Pushwoosh's defaultStart method to trigger the Live Activity
    Pushwoosh.getInstance().defaultStart(activityId, attributes, content);
  }
```