feat: Add url_launcher package and update platform plugin registrations and Android manifest for URL handling.

This commit is contained in:
Suherdy Yacob 2026-03-27 09:37:22 +07:00
parent cba7647038
commit 544439d571
8 changed files with 151 additions and 36 deletions

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,7 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application
android:label="odoo_loyalty_app"
android:label="Mie Mapan"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
@ -44,5 +44,17 @@
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="geo" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="whatsapp" />
</intent>
</queries>
</manifest>

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../services/odoo_service.dart';
import '../theme/app_theme.dart';
@ -36,6 +37,30 @@ class _BranchesScreenState extends State<BranchesScreen> {
}
}
Future<void> _launchMaps(String queryTerm) async {
final query = Uri.encodeComponent(queryTerm);
final url = Uri.parse('https://www.google.com/maps/search/?api=1&query=$query');
if (await canLaunchUrl(url)) {
await launchUrl(url, mode: LaunchMode.externalApplication);
} else {
if (mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Could not open map.')));
}
}
Future<void> _launchWhatsApp(String phone) async {
final cleanPhone = phone.replaceAll(RegExp(r'\D'), '');
String waPhone = cleanPhone;
if (waPhone.startsWith('0')) {
waPhone = '62${waPhone.substring(1)}';
}
final url = Uri.parse('https://wa.me/$waPhone');
if (await canLaunchUrl(url)) {
await launchUrl(url, mode: LaunchMode.externalApplication);
} else {
if (mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Could not open WhatsApp.')));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -69,44 +94,51 @@ class _BranchesScreenState extends State<BranchesScreen> {
)
]
),
child: ListTile(
contentPadding: const EdgeInsets.all(16),
leading: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppTheme.secondaryContainer.withOpacity(0.2),
shape: BoxShape.circle,
child: ListTile(
contentPadding: const EdgeInsets.all(16),
onTap: () => _launchMaps('${branch['name']} ${addressParts}'),
leading: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppTheme.secondaryContainer.withOpacity(0.2),
shape: BoxShape.circle,
),
child: const Icon(Icons.storefront, color: AppTheme.secondary),
),
child: const Icon(Icons.storefront, color: AppTheme.secondary),
),
title: Text(
branch['name'] ?? 'Mapan Branch',
style: Theme.of(context).textTheme.titleMedium,
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
addressParts.isEmpty ? 'No address specified' : addressParts,
style: Theme.of(context).textTheme.bodyMedium,
),
if (phone.isNotEmpty) ...[
const SizedBox(height: 4),
Row(
children: [
const Icon(Icons.phone, size: 14, color: AppTheme.primary),
const SizedBox(width: 4),
Text(phone, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: AppTheme.primary)),
],
title: Text(
branch['name'] ?? 'Mapan Branch',
style: Theme.of(context).textTheme.titleMedium,
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
addressParts.isEmpty ? 'No address specified' : addressParts,
style: Theme.of(context).textTheme.bodyMedium,
),
]
],
if (phone.isNotEmpty) ...[
const SizedBox(height: 4),
Row(
children: [
const Icon(Icons.phone, size: 14, color: AppTheme.primary),
const SizedBox(width: 4),
Text(phone, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: AppTheme.primary)),
],
),
]
],
),
),
trailing: phone.isNotEmpty
? IconButton(
icon: const Icon(Icons.chat_bubble_outline, color: Colors.green),
onPressed: () => _launchWhatsApp(phone),
tooltip: 'Chat on WhatsApp',
)
: const Icon(Icons.chevron_right, color: AppTheme.onSurfaceVariant),
),
trailing: const Icon(Icons.chevron_right, color: AppTheme.onSurfaceVariant),
),
);
},
),

View File

@ -7,8 +7,10 @@ import Foundation
import flutter_local_notifications
import shared_preferences_foundation
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

View File

@ -565,6 +565,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
url: "https://pub.dev"
source: hosted
version: "6.3.2"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
url: "https://pub.dev"
source: hosted
version: "6.3.29"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0"
url: "https://pub.dev"
source: hosted
version: "6.4.1"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
url: "https://pub.dev"
source: hosted
version: "3.2.2"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
url: "https://pub.dev"
source: hosted
version: "3.2.5"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
url: "https://pub.dev"
source: hosted
version: "2.4.2"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
url: "https://pub.dev"
source: hosted
version: "3.1.5"
vector_math:
dependency: transitive
description:

View File

@ -39,6 +39,7 @@ dependencies:
flutter_local_notifications: ^21.0.0
shared_preferences: ^2.5.4
google_fonts: ^6.2.1
url_launcher: ^6.3.2
dev_dependencies:
flutter_test:

View File

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h"
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST