বিষয়বস্তুতে যান

একটি ফ্লাটার প্রজেক্টে লাইভ অ্যাক্টিভিটি iOS

আমরা একটি ফ্লাটার প্রজেক্টের জন্য স্টপওয়াচ ইমপ্লিমেন্টেশন সহ একটি লাইভ অ্যাক্টিভিটি তৈরি করব। এই প্রজেক্টে ফ্লাটার-সাইড ইমপ্লিমেন্টেশন এবং Xcode প্রজেক্টে নেটিভ ইমপ্লিমেন্টেশন উভয়ই অন্তর্ভুক্ত থাকবে।

ফ্লাটার সাইড ইমপ্লিমেন্টেশন

Anchor link to

আমরা একটি ক্লাস তৈরি করব যা তিনটি মেথড ইমপ্লিমেন্ট করবে:

  • startLiveActivity()
  • updateLiveActivity()
  • stopLiveActivity().
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-এর মতো ম্যাপ ফরম্যাটে পাঠাতে হবে।

class DynamicIslandStopwatchDataModel {
final int elapsedSeconds;
DynamicIslandStopwatchDataModel({
required this.elapsedSeconds,
});
Map<String, dynamic> toMap() {
return <String, dynamic>{
'elapsedSeconds': elapsedSeconds,
};
}
}

প্রতিটি মেথডে সংশ্লিষ্ট নেটিভ মেথড কল করার জন্য প্রয়োজনীয় কোড অন্তর্ভুক্ত থাকবে।

এটি ফ্লাটার সাইডে চালানোর জন্য প্রয়োজনীয় প্রাথমিক সেটআপ ছিল। এর পরে, প্রয়োজনীয় মেথড কল সহ একটি বেসিক স্টপওয়াচ অ্যাপ UI ইমপ্লিমেন্ট করা হয়েছিল।

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'),
),
],
),
],
),
),
);
}
}

নেটিভ সাইড ইমপ্লিমেন্টেশন

Anchor link to

Xcode প্রজেক্ট কনফিগারেশন

Anchor link to
  1. আপনার প্রজেক্টটি Xcode-এ খুলুন এবং জেনারেল ট্যাবে মিনিমাম ডেপ্লয়মেন্ট iOS 16.1 সেট করুন।
Xcode জেনারেল ট্যাব iOS 16.1 মিনিমাম ডেপ্লয়মেন্ট দেখাচ্ছে
  1. Info.plist-এ একটি নতুন কী NSSupportsLiveActivities যোগ করুন এবং এর মান YES (বুলিয়ান) সেট করুন।

Info.plist

Anchor link to
<key>NSSupportsLiveActivities</key>
<true/>
  1. অ্যাকশন বার থেকে, File > New > Target নির্বাচন করুন এবং WidgetExtensionand অনুসন্ধান করে একটি নতুন উইজেট তৈরি করুন।

কোড সাইড ইমপ্লিমেন্টেশন

Anchor link to

চলুন LiveActivityManager নামে একটি ক্লাস তৈরি করি যা আমাদের ডাইনামিক আইল্যান্ডের জন্য লাইভ অ্যাক্টিভিটিগুলো পরিচালনা করবে। এই ক্লাসে তিনটি মেথড থাকবে: startLiveActivity(), updateLiveActivity(), এবং stopLiveActivity()

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

struct StopwatchWidgetAttributes: ActivityAttributes {
public typealias stopwatchStatus = ContentState
public struct ContentState: Codable, Hashable {
var elapsedTime: Int
}
}

এখন, শুধু ডাইনামিক আইল্যান্ডের জন্য UI বাকি আছে! (আমরা এতদূর এসেছি!) ডাইনামিক আইল্যান্ডের সম্পূর্ণ UI SwiftUI ব্যবহার করে তৈরি করতে হবে। এই আর্টিকেলের জন্য, একটি সাধারণ UI ডিজাইন করা হয়েছে (তবে প্রয়োজন অনুযায়ী এটি কাস্টমাইজ করতে পারেন)।

এই UI কোডটি StopwatchWidgetLiveActivity স্ট্রাকটের ভিতরে লিখতে হবে। স্ট্রাকট থেকে বিদ্যমান কোডটি সরিয়ে ফেলুন, এবং আপনি নীচের কোডটি অনুসরণ করতে পারেন:

SwiftUI

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

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 কোড ইমপ্লিমেন্টেশন

Anchor link to

Pushwoosh ফ্লাটার প্লাগইন প্রজেক্টে লাইভ অ্যাক্টিভিটি পরিচালনা করার জন্য দুটি মেথড প্রদান করে।

/// 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 সাইডে লাইভ অ্যাক্টিভিটির কাঠামো এবং টোকেন পরিচালনা করার অনুমতি দেয়।

অ্যাপ্লিকেশন ইনিশিয়ালাইজেশনের সময় এই মেথডটি কল করুন।

Pushwoosh.initialize({"app_id": "XXXXX-XXXXX", "sender_id": "XXXXXXXXXXXX"});
/**
* Call this method `defaultSetup()`
*/
Pushwoosh.getInstance.defaultSetup();

অ্যাপ ইনিশিয়ালাইজেশনের সময়, Pushwoosh আপনার পুশ-টু-স্টার্ট টোকেন সার্ভারে পাঠাবে, যা আপনাকে পরে একটি API কলের মাধ্যমে লাইভ অ্যাক্টিভিটি শুরু করার অনুমতি দেবে। লাইভ অ্যাক্টিভিটি শুরু করার জন্য, আপনাকে একটি API রিকোয়েস্ট করতে হবে। নীচে রিকোয়েস্টের একটি উদাহরণ দেওয়া হল:

{
"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 সম্পর্কে আরও বিস্তারিত ডকুমেন্টেশন

আপনি যদি একটি সাধারণ লাইভ অ্যাক্টিভিটি শুরু করতে চান, উদাহরণস্বরূপ, আপনার অ্যাপ থেকে, আপনি ফ্লাটারে নিম্নলিখিত মেথডটি ব্যবহার করতে পারেন: defaultStart(String activityId, Map<String, dynamic> attributes, Map<String, dynamic> content)

এই মেথডটি যেকোনো ইভেন্টে কল করা যেতে পারে যা লাইভ অ্যাক্টিভিটি শুরু করতে ট্রিগার করে।

// 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);
}