پیاده سازی Deep link در فلاتر
اگر با اپلیکیشن اینستاگرام کار کرده باشید متوجه میشید زمانی که وارد وبسایت آن میشوید به صورت خودکار به اپلیکیشن اینستاگرام که روی گوشی نصب شده برگشت داده می شوید.
این قابلیت با استفاده از Depp link پیاده سازی می شود.
شما به راحتی میتوانید آدرس های url خاصی را در برنامه خود تعریف کنید تا کاربر با وارد شدن به آن لینک به اپلیکیشن برگشت داده شود.
یکی از مثال های دیگر استفاده از Depp link برای زمانی هست از درگاه پرداخت استفاده میکنید و کاربر از صفحه بانک به اپلیکیشن بازگردانده می شود.
تنظیمات Depp link
قبل از انجام کارهای کدنویسی باید یک سری تنظیمات در پروژه انجام دهیم.
برای سیستم عامل iOS به دو روش میتونیم دیپ لینک را پیاده سازی کنیم.
Custom URL schemes: این روش به شما امکان کار کردن با هر نوع ترتیب یا طرحی را میدهد و نیازی به انتخاب کردن هاست یا دامنه اینترنتی ندارید. اما لازم هست تا طرحی که استفاده میکنید منحصر به فرد باشد.
برای مثال با آدرسی به شکل زیر میتونید کار کنید.
your_scheme://any_host
Universal Links: این نوع به شما امکان کار کردن با ادرس های مبتنی بر پروتکل https را میدهد اما فقط باید از دامنه ای که مشخص کردید استفاده کنید.
در این آموزش از روش اول برای پیاده سازی در فلاتر استفاده میکنیم.
وارد فایل Info.plist شوید و خط های زیر را وارد کنید.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>deeplink.flutter.dev</string>
<key>CFBundleURLSchemes</key>
<array>
<string>poc</string>
</array>
</dict>
</array>
با دستورات اگر وارد آدرس زیر شویم به اپلیکیشن برمیگردیم.
poc://deeplink.flutter.dev
برای اندروید هم دقیقا دو روش برای پیاده سازی Deep Link وجود دارد.
App Links: این روش به شما امکان کار کردن با https را میدهد و نیازمند تعریف کردن هاست یا ادرس سایت هستید.
Deep Links: این روش هم دقیقا مشابه Custom URL schemes هست.
برای انجام تنظیمات در اندروید باید وارد فایل مانیفست که حکم تنظیمات اپلیکیشن را دارد وارد شوید.
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="poc"
android:host="deeplink.flutter.dev" />
</intent-filter>
آماده سازی Platform Channels
تنظیمات بخش نیتیو برنامه را انجام دادیم. اما برای برقراری ارتباط فلاتر با بخش نیتیو نیاز به استفاده از Platform Channels داریم.
در اندروید نیاز داریم تا Intent های ورودی را در متد onCreate دریافت کنیم.
پس یک MethodChannel طراحی میکنیم و اگر اپلیکیشن توسط لینک باز شد URI را ارسال میکنیم به این متد.
private val CHANNEL = "poc.deeplink.flutter.dev/channel"
private var startString: String? = null
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "initialLink") {
if (startString != null) {
result.success(startString)
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = getIntent()
startString = intent.data?.toString()
}
برای iOS کمی پیاده سازی متفاوت است اما کلیت کار یکی هست و در صورت باز شدن برنامه یک URI ارسال میکنیم.
فایل AppDelegate.swift:
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
private var methodChannel: FlutterMethodChannel?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
let controller = window.rootViewController as! FlutterViewController
methodChannel = FlutterMethodChannel(name: "poc.deeplink.flutter.dev/cnannel", binaryMessenger: controller)
methodChannel?.setMethodCallHandler({ (call: FlutterMethodCall, result: FlutterResult) in
guard call.method == "initialLink" else {
result(FlutterMethodNotImplemented)
return
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
تا به اینجا شروع شدن برنامه توسط Deep Link را پیاده سازی کردیم.
اما اگر از قبل اپلیکیشن در حال اجرا بود چه کاری باید انجام دهیم؟
در اندروید متد onNewIntent را بازنویسی میکنیم تا Intent های جدید را پردازش کنیم.
اگر این لینک شامل اکشن بود یک event به EventChannel ارسال میکنیم.
private val EVENTS = "poc.deeplink.flutter.dev/events"
private var linksReceiver: BroadcastReceiver? = null
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
EventChannel(flutterEngine.dartExecutor, EVENTS).setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(args: Any?, events: EventSink) {
linksReceiver = createChangeReceiver(events)
}
override fun onCancel(args: Any?) {
linksReceiver = null
}
}
)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (intent.action === Intent.ACTION_VIEW) {
linksReceiver?.onReceive(this.applicationContext, intent)
}
}
fun createChangeReceiver(events: EventSink): BroadcastReceiver? {
return object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { // NOTE: assuming intent.getAction() is Intent.ACTION_VIEW
val dataString = intent.dataString ?:
events.error("UNAVAILABLE", "Link unavailable", null)
events.success(dataString)
}
}
}
در بخش iOS هم همینکار و انجام میدیم با کمی تفاوت.
در زبان سویفت برای این که لینک های ورودی را زمانی که برنامه در بک گراند در حال اجرا است را مدیریت کنیم باید یک FlutterStreamHandler ایجاد کنیم.
فایل AppDelegate.swift را به شکل زیر تغییر دهید.
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
private var eventChannel: FlutterEventChannel?
private let linkStreamHandler = LinkStreamHandler()
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
let controller = window.rootViewController as! FlutterViewController
eventChannel = FlutterEventChannel(name: "poc.deeplink.flutter.dev/events", binaryMessenger: controller)
GeneratedPluginRegistrant.register(with: self)
eventChannel?.setStreamHandler(linkStreamHandler)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
eventChannel?.setStreamHandler(linkStreamHandler)
return linkStreamHandler.handleLink(url.absoluteString)
}
}
class LinkStreamHandler:NSObject, FlutterStreamHandler {
var eventSink: FlutterEventSink?
// links will be added to this queue until the sink is ready to process them
var queuedLinks = [String]()
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
self.eventSink = events
queuedLinks.forEach({ events($0) })
queuedLinks.removeAll()
return nil
}
func onCancel(withArguments arguments: Any?) -> FlutterError? {
self.eventSink = nil
return nil
}
func handleLink(_ link: String) -> Bool {
guard let eventSink = eventSink else {
queuedLinks.append(link)
return false
}
eventSink(link)
return true
}
}
مدیریت Deep Link با بلاک
یکی از معماری های محبوب در برنامه نویسی فلاتر استفاده از بلاک می باشد که تا به حال آموزش های زیادی هم از آن قرار داده ایم.
کلاس بلاک را به شکل زیر با استفاده از State سراسری کدنویسی میکنیم.
class DeepLinkBloc extends Bloc {
//Event Channel creation
static const stream = const EventChannel('poc.deeplink.flutter.dev/events');
//Method channel creation
static const platform = const MethodChannel('poc.deeplink.flutter.dev/channel');
StreamController<String> _stateController = StreamController();
Stream<String> get state => _stateController.stream;
Sink<String> get stateSink => _stateController.sink;
//Adding the listener into contructor
DeepLinkBloc() {
//Checking application start by deep link
startUri().then(_onRedirected);
//Checking broadcast stream, if deep link was clicked in opened appication
stream.receiveBroadcastStream().listen((d) => _onRedirected(d));
}
_onRedirected(String uri) {
// Here can be any uri analysis, checking tokens etc, if it’s necessary
// Throw deep link URI into the BloC's stream
stateSink.add(uri);
}
@override
void dispose() {
_stateController.close();
}
Future<String> startUri() async {
try {
return platform.invokeMethod('initialLink');
} on PlatformException catch (e) {
return "Failed to Invoke: '${e.message}'.";
}
}
}
برای ویجت کلی برنامه از StreamBuilder استفاده میکنیم که با توجه به آپدیت شدن State محتویات جدیدی نمایش دهد.
class PocWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
DeepLinkBloc _bloc = Provider.of<DeepLinkBloc>(context);
return StreamBuilder<String>(
stream: _bloc.state,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
child: Center(
child: Text('No deep link was used ')));
} else {
return Container(
child: Center(
child: Padding(
padding: EdgeInsets.all(20.0),
child: Text('Redirected: ${snapshot.data}'))));
}
},
);
}
}
تمام کارهایی که لازم بود را انجام دادیم حالا کافی که وارد آدرس زیر شوید و منتظر شوید تا برنامه اجرا شود.
poc://deeplink.flutter.dev
مطالب زیر را حتما مطالعه کنید
آموزش پیاده سازی لینت Lint در برنامه نویسی فلاتر
آموزش الگوی تزریق وابستگی در فلاتر Dependency Injection
کتاب های آموزش برنامه نویسی فلاتر + دانلود PDF
آموزش نصب فلاتر و رفع خطاهای رایج ساخت پروژه + ویدیو
آموزش استفاده از نقشه در فلاتر
آموزش ساخت Navigation Drawer در فلاتر
7 دیدگاه
به گفتگوی ما بپیوندید و دیدگاه خود را با ما در میان بگذارید.
سلام . عالیه مطالب .
ولی ای کاش فیلمش رو هم میذاشتید تا کاربردی تر و بهتر درک بشه
باز هم ممنون از شما و خسته نباشید
سلام وقت بخیر من به یک درگاه پرداخت متصل میشم تمام این کارا رو هم انجام دادم.
آیا برنامه نویس بک اند باید ی اینتی رو کال کنه تا برگرده به برنامه من.؟؟
من نمیدونم دقیقا چه اطلاعاتی رو ازش بگیرم تا بعد از پرداخت برگرده به اپ من.
آیا اگر من ی لینک تستی ست کنم مثلا http://www.google.com و برنامه من هم در بک گراند در حال اجرا باشه
بعدش روی گوشیم برم این لینک رو با مرورگر باز کنم باید بیاد برنامه من رو باز کنه؟؟
ممنون میشم اگر راهنماییم کنید.
سلام بله مورد دوم که گفتید صحیح هست بعد از پرداخت کاربر به یک صفحه هدایت میشه معمولا که پرداخت تایید بشه بعد از این قسمت سناریوهای مختلفی میتونه رخ بده مثلا یک دکمه داخل این صفحه قرار داده بشه کاربر با کلیک کردن روی آن به ادرس اینترنتی منتقل بشه که داخل اپ به عنوان deep link تنظیم شده و اپلیکیشن به شکل خودکار باز بشه یا دکمه ای وجود نداشته باشه و بعد از تایید پرداخت ریدایرکت بشه به ادرس مورد نظر تا اپلیکیشن اجرا بشه.
سلام خسته نباشيد وقتتون بخير ببخشيد توي قسمت اندرويد آماده سازی Platform Channels دقيقا بايد چيكار كنيم توي چ فايلي بايد اينا رو بنويسيم چرا اينا سيميكالن ندارن ؟
فایل main, زبان کاتلین نیازی به سمیکالین نداره
آها خيلي ممنون بابت پاسخگويي يعني دقيقا اين مسير :
android/app/src/main/kotlin/com/example/project_name/MainActivity.kt
?