การใช้ Deep Linking ใน Flutter: เปิดแอปจาก Link ภายนอก
ในบทความก่อนหน้า เราได้พูดถึง Code Push ซึ่งเป็นวิธีที่ช่วยให้เราอัปเดตแอปพลิเคชัน Flutter ได้อย่างรวดเร็วโดยไม่ต้องผ่านกระบวนการ Release ใหม่บน Store ต่างๆ ในบทความนี้ เราจะมาสำรวจอีกหนึ่งฟีเจอร์ที่สำคัญไม่แพ้กัน นั่นคือ Deep Linking ซึ่งจะช่วยให้ผู้ใช้สามารถเปิดแอปพลิเคชันของเราได้โดยตรงจากลิงก์ภายนอก ไม่ว่าจะเป็นจากเว็บไซต์, อีเมล, หรือแอปพลิเคชันอื่นๆ
Deep Linking เป็นเครื่องมือที่มีประโยชน์อย่างมากในการปรับปรุงประสบการณ์ผู้ใช้ (User Experience) และเพิ่มประสิทธิภาพทางการตลาดของแอปพลิเคชันของเรา ลองจินตนาการว่าผู้ใช้กำลังดูสินค้าบนเว็บไซต์ของเรา และต้องการดูรายละเอียดเพิ่มเติมในแอปพลิเคชัน การที่พวกเขาสามารถคลิกลิงก์แล้วเปิดแอปพลิเคชันไปยังหน้าสินค้าดังกล่าวได้ทันที ย่อมดีกว่าการที่ต้องเปิดแอปพลิเคชันเอง แล้วค้นหาสินค้าอีกครั้ง
Deep Linking คืออะไร?
Deep Linking คือการสร้างลิงก์ที่สามารถนำผู้ใช้ไปยังหน้าเฉพาะเจาะจงภายในแอปพลิเคชันได้โดยตรง ต่างจาก Universal Link (iOS) และ App Links (Android) ที่ต้องมีการยืนยันความเป็นเจ้าของ Domain name ก่อน Deep Link จะทำงานได้ทันที แต่ก็มีข้อจำกัดเรื่องความน่าเชื่อถือมากกว่า (เช่น หากไม่มีแอปติดตั้ง ลิงก์อาจเปิดใน Browser แทน)
เมื่อผู้ใช้คลิกลิงก์ Deep Link ระบบปฏิบัติการจะตรวจสอบว่ามีแอปพลิเคชันที่สามารถจัดการลิงก์นั้นได้หรือไม่ หากมี ระบบก็จะเปิดแอปพลิเคชันไปยังหน้าที่ระบุไว้ในลิงก์นั้น
Uri: หัวใจสำคัญของ Deep Linking
Deep Linking ทำงานโดยอาศัย Uniform Resource Identifier (URI) ซึ่งเป็น String ที่ใช้ระบุแหล่งข้อมูล (Resource) บนอินเทอร์เน็ต หรือในที่นี้คือหน้าต่างๆ ภายในแอปพลิเคชันของเรา URI ประกอบด้วยส่วนประกอบต่างๆ ดังนี้:
- Scheme: ระบุโปรโตคอลที่ใช้ในการเข้าถึง Resource (เช่น
http,https,myapp) - Authority: ระบุชื่อ Domain หรือชื่อ Host ของ Resource (เช่น
www.example.com) - Path: ระบุตำแหน่งของ Resource ภายใน Authority (เช่น
/products/123) - Query: ระบุพารามิเตอร์เพิ่มเติมที่ใช้ในการเข้าถึง Resource (เช่น
?color=red&size=large) - Fragment: ระบุส่วนย่อยของ Resource ที่ต้องการเข้าถึง (เช่น
#section2)
ตัวอย่าง URI สำหรับ Deep Linking ในแอปพลิเคชันของเรา:
myapp://products/123?color=red&size=large
ในตัวอย่างนี้:
myappคือ Scheme ที่เรากำหนดเองสำหรับแอปพลิเคชันของเราproducts/123คือ Path ที่ระบุว่าเราต้องการไปยังหน้าสินค้าที่มี ID เป็น 123color=red&size=largeคือ Query ที่ระบุพารามิเตอร์เพิ่มเติม เช่น สีและขนาดของสินค้า
Link Handling ใน Flutter
ในการใช้งาน Deep Linking ใน Flutter เราจะต้องทำการ Config แอปพลิเคชันของเราให้สามารถจัดการกับ URI ที่เรากำหนดได้ ซึ่งมีขั้นตอนหลักๆ ดังนี้:
- กำหนด Scheme: เลือก Scheme ที่เหมาะสมสำหรับแอปพลิเคชันของเรา (เช่น ชื่อแอปพลิเคชัน หรือชื่อ Domain ของเรา)
- Config Platform-Specific Settings:
- Android: แก้ไขไฟล์
AndroidManifest.xmlเพื่อระบุintent-filterที่จะจัดการกับ Scheme ของเรา - iOS: แก้ไขไฟล์
Info.plistเพื่อระบุCFBundleURLTypesที่จะจัดการกับ Scheme ของเรา
- Android: แก้ไขไฟล์
- Implement Link Handling Logic: เขียน Code ใน Flutter เพื่อดึงข้อมูลจาก URI และนำผู้ใช้ไปยังหน้าที่ถูกต้องภายในแอปพลิเคชัน
ตัวอย่าง Code
ต่อไปนี้เป็นตัวอย่าง Code ที่แสดงวิธีการ Implement Link Handling Logic ใน Flutter:
import 'package:flutter/material.dart';
import 'package:uni_links/uni_links.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String? _latestLink = 'Unknown';
String? _latestUri;
@override
void initState() {
super.initState();
_initURIHandler();
_incomingLinkHandler();
}
Future<void> _initURIHandler() async {
if (!mounted) return;
try {
final initialUri = await getInitialUri();
if (initialUri == null) {
print('No initial URI received');
} else {
_latestUri = initialUri.toString();
_latestLink = 'Initial URI: $_latestUri';
print('Initial URI received: $_latestUri');
// TODO: Parse the URI and navigate to the correct screen
}
} on PlatformException {
print('Failed to receive initial URI');
} on FormatException catch (err) {
print('Malformed Initial URI received: ${err.message}');
}
if (!mounted) return;
setState(() {});
}
Future<void> _incomingLinkHandler() async {
if (!mounted) return;
uriLinkStream.listen((Uri? uri) {
if (!mounted) return;
print('Received URI: $uri');
setState(() {
_latestUri = uri.toString();
_latestLink = 'Received URI: $_latestUri';
});
// TODO: Parse the URI and navigate to the correct screen
}, onError: (Object err) {
if (!mounted) return;
print('Error occurred: $err');
setState(() {
_latestUri = 'Failed to get latest URI: $err.';
_latestLink = 'Error: $_latestUri';
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Deep Linking Example'),
),
body: Center(
child: Text('Latest Link: $_latestLink\n\nLatest URI: $_latestUri'),
),
),
);
}
}
คำอธิบาย Code:
- เราใช้ Package
uni_linksเพื่อจัดการกับ Deep Links _initURIHandler()ใช้เพื่อดึง URI เมื่อแอปพลิเคชันถูกเปิดจาก Deep Link เป็นครั้งแรก_incomingLinkHandler()ใช้เพื่อรับ URI เมื่อแอปพลิเคชันถูกเปิดจาก Deep Link ในขณะที่แอปพลิเคชันกำลังทำงานอยู่แล้ว- ใน
setState()เรา Update State เพื่อแสดง URI ล่าสุดบนหน้าจอ - TODO: ส่วนที่ต้อง Implement เพิ่มเติมคือการ Parse URI และนำผู้ใช้ไปยังหน้าที่ถูกต้องภายในแอปพลิเคชัน
Android Configuration (AndroidManifest.xml)
<activity
android:name=".MainActivity"
android:launchMode="singleTask"> <!-- Important: set launchMode to singleTask -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<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="myapp" /> <!-- Replace with your scheme -->
</intent-filter>
</activity>
iOS Configuration (Info.plist)
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string> <!-- Replace with your scheme -->
</array>
<key>CFBundleURLName</key>
<string>com.example.myapp</string> <!-- Replace with your bundle identifier -->
</dict>
</array>
ข้อควรระวังและแนวปฏิบัติที่ดี
- Security: ตรวจสอบและ Validate ข้อมูลที่ได้รับจาก URI อย่างระมัดระวัง เพื่อป้องกันการโจมตี Cross-Site Scripting (XSS) หรือ SQL Injection
- Error Handling: จัดการกับกรณีที่ URI ไม่ถูกต้อง หรือไม่สามารถนำผู้ใช้ไปยังหน้าที่ต้องการได้
- User Experience: สร้างประสบการณ์ผู้ใช้ที่ราบรื่นและเป็นธรรมชาติ เมื่อผู้ใช้เปิดแอปพลิเคชันจาก Deep Link
- Testing: ทดสอบ Deep Linking อย่างละเอียดบนอุปกรณ์และระบบปฏิบัติการต่างๆ
- Deferred Deep Linking: พิจารณาใช้ Deferred Deep Linking เพื่อให้ผู้ใช้ไปยังหน้าที่ต้องการได้ แม้ว่าพวกเขาจะยังไม่ได้ติดตั้งแอปพลิเคชัน (ซึ่งต้องใช้ third-party service)
สรุป
Deep Linking เป็นเครื่องมือที่มีประสิทธิภาพในการปรับปรุงประสบการณ์ผู้ใช้และเพิ่มประสิทธิภาพทางการตลาดของแอปพลิเคชัน Flutter ของเรา การเข้าใจหลักการทำงานของ URI และการ Implement Link Handling Logic อย่างถูกต้อง จะช่วยให้เราสามารถสร้าง Deep Links ที่มีประโยชน์และใช้งานง่ายได้
ในบทความถัดไป เราจะมาสำรวจอีกหนึ่งฟีเจอร์ที่สำคัญ นั่นคือ Push Notifications ซึ่งจะช่วยให้เราสามารถส่งข้อความแจ้งเตือนไปยังผู้ใช้แอปพลิเคชันของเราได้