Android deep links

This commit is contained in:
Michael Speed 2024-04-23 15:38:28 +02:00
parent 085a930a4a
commit 5d7ca21dc8
20 changed files with 140 additions and 50 deletions

View File

@ -53,6 +53,26 @@
<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="https" />
<data android:host="medito.app" />
<data android:pathPrefix="/tracks/" />
</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="https" />
<data android:host="medito.app" />
<data android:pathPrefix="/packs/" />
</intent-filter>
</activity>
@ -62,6 +82,9 @@
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel" />
<meta-data
android:name="flutter_deeplinking_enabled"
android:value="true" />
<service
android:name=".AudioPlayerService"

View File

@ -2,17 +2,17 @@ class RouteConstants {
static const String root = '/';
static const String bottomNavbarPath = '/bottomNavbar';
static const String homePath = '/home';
static const String trackPath = '/track/:sid';
static const String trackPath = '/tracks/:sid';
static const String downloadsPath = '/downloads';
static const String maintenancePath = '/maintenance';
static const String playerPath = '/player';
static const String packPath = '/pack/:pid';
static const String player1 = '/pack/:pid/meditation/:sid';
static const String pack2Path = '/pack/:pid/pack2/:p2id';
static const String player2 = '/pack/:pid/pack2/:p2id/meditation/:sid';
static const String pack3Path = '/pack/:pid/pack2/:p2id/pack3/:p3id';
static const String packPath = '/packs/:pid';
static const String player1 = '/packs/:pid/meditations/:sid';
static const String pack2Path = '/packs/:pid/pack2/:p2id';
static const String player2 = '/packs/:pid/pack2/:p2id/meditations/:sid';
static const String pack3Path = '/packs/:pid/pack2/:p2id/pack3/:p3id';
static const String player3 =
'/pack/:pid/pack2/:p2id/pack3/:p3id/meditation/:sid';
'/packs/:pid/pack2/:p2id/pack3/:p3id/meditations/:sid';
static const String urlPath = '/url';
static const String connectivityErrorPath = '/connectivityError';
static const String backgroundSoundsPath = '/backgroundsounds';

View File

@ -262,37 +262,41 @@ MaterialPage<void> _getFolderMaterialPage(GoRouterState state) {
Future<void> handleNavigation(
String? place,
List<String?> ids, {
BuildContext? context,
List<String?> ids,
BuildContext context, {
WidgetRef? ref,
GoRouter? goRouterContext,
}) async {
ids.removeWhere((element) => element == null);
var path;
var params;
if (place == TypeConstants.track) {
unawaited(
showModalBottomSheet<void>(
context: context!,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(24.0),
),
),
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: ColorConstants.ebony,
builder: (BuildContext context) {
return SafeArea(
child: TrackView(
id: ids.first!,
if (place == 'tracks') {
try {
unawaited(
showModalBottomSheet<void>(
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(24.0),
),
);
},
).then(
(value) => ref?.refresh(fetchStatsProvider),
),
);
),
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: ColorConstants.ebony,
builder: (BuildContext context) {
return SafeArea(
child: TrackView(
id: ids.first!,
),
);
},
).then(
(value) => ref?.refresh(fetchStatsProvider),
),
);
} catch (e, s) {
print(s);
}
return;
} else if (place != null && place.contains('pack3')) {
@ -335,3 +339,18 @@ Future<void> handleNavigation(
unawaited(goRouterContext.push(path, extra: params));
}
}
void handleDeepLink(Uri? uri, BuildContext context) {
var path = uri?.path;
if (path != null) {
if (path.contains('tracks')) {
handleNavigation(
'tracks',
[uri?.pathSegments.last ?? ''],
context,
);
} else {
unawaited(context.push(path));
}
}
}

View File

@ -9,6 +9,7 @@ import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:permission_handler/permission_handler.dart';
late final FirebaseMessaging? _messaging;
@ -127,10 +128,10 @@ void _navigate(
var goRouterContext = ref.read(goRouterProvider);
if (data.type.isNotNullAndNotEmpty()) {
handleNavigation(
goRouterContext: goRouterContext,
context: context,
data.type,
[data.id.toString().getIdFromPath(), data.path],
context,
goRouterContext: goRouterContext,
);
} else {
goRouterContext.go(RouteConstants.bottomNavbarPath);

View File

@ -72,7 +72,11 @@ class DonationWidget extends ConsumerWidget {
height: 48,
width: MediaQuery.of(context).size.width,
child: LoadingButtonWidget(
onPressed: () => handleNavigation(donationPageModel.ctaType, [donationPageModel.ctaPath]),
onPressed: () => handleNavigation(
donationPageModel.ctaType,
[donationPageModel.ctaPath],
context,
),
btnText: donationPageModel.ctaTitle ?? StringConstants.donateNow,
bgColor: ColorConstants.walterWhite,
textColor: parseColor(donationPageModel.colorBackground),

View File

@ -54,9 +54,9 @@ class ExploreInitialPageWidget extends ConsumerWidget {
subTitle: element.subtitle,
coverUrlPath: element.coverUrl,
onTap: () => handleNavigation(
context: context,
element.type,
[element.id.toString(), element.path],
context,
ref: ref,
),
),

View File

@ -79,9 +79,9 @@ class ExploreResultWidget extends ConsumerWidget {
checkConnectivity().then((value) {
if (value) {
handleNavigation(
context: context,
type,
[id.toString(), path],
context,
);
} else {
createSnackBar(StringConstants.checkConnection, context);

View File

@ -8,8 +8,11 @@ import 'package:Medito/views/home/widgets/header_and_announcement_widget.dart';
import 'package:Medito/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:uni_links/uni_links.dart';
import '../../models/home/home_model.dart';
import '../../routes/routes.dart';
import 'widgets/quote/quote_widget.dart';
import 'widgets/shortcuts/shortcuts_widget.dart';
@ -22,6 +25,18 @@ class HomeView extends ConsumerStatefulWidget {
class _HomeViewState extends ConsumerState<HomeView>
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
@override
void initState() {
super.initState();
navigateToDeepLink();
}
void navigateToDeepLink() {
Future.delayed(Duration(seconds: 1), () async {
handleDeepLink(await getInitialUri(), context);
});
}
@override
Widget build(BuildContext context) {
super.build(context);

View File

@ -134,9 +134,9 @@ class _AnnouncementWidgetState extends ConsumerState<AnnouncementWidget> {
var path = widget.announcement.ctaPath;
_handleTrackEvent();
await handleNavigation(
context: context,
widget.announcement.ctaType,
[path.toString().getIdFromPath(), path],
context,
ref: ref,
);
}

View File

@ -91,9 +91,9 @@ class MenuBottomSheetWidget extends ConsumerWidget {
) async {
Navigator.pop(context);
await handleNavigation(
context: context,
element.type,
[element.path.toString().getIdFromPath(), element.path],
context,
ref: ref,
);
}

View File

@ -47,9 +47,9 @@ class CarouselWidget extends ConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: padding20),
child: InkWell(
onTap: () => handleNavigation(
context: context,
first.type,
[first.path.toString().getIdFromPath(), first.path],
context,
),
child: Stack(
children: [

View File

@ -35,9 +35,9 @@ class _ShortcutsItemsWidgetState extends ConsumerState<ShortcutsItemsWidget> {
ShortcutsModel element,
) async {
await handleNavigation(
context: context,
element.type,
[element.path.toString().getIdFromPath(), element.path],
context,
ref: ref,
);
}

View File

@ -155,22 +155,22 @@ class _PackViewState extends ConsumerState<PackView>
if (type == TypeConstants.pack) {
if (location.contains('pack2')) {
unawaited(handleNavigation(
context: context,
RouteConstants.pack3Path,
[location.split('/')[2], widget.id, id.toString()],
context,
));
} else {
unawaited(handleNavigation(
context: context,
RouteConstants.pack2Path,
[widget.id, id.toString()],
context,
));
}
} else {
unawaited(handleNavigation(
context: context,
type,
[id.toString(), path],
context,
ref: ref,
));
}

View File

@ -117,7 +117,9 @@ class _PlayerViewState extends ConsumerState<PlayerView> {
void onPlayPausePressed() {
var isPlaying = ref.read(audioStateProvider).isPlaying;
ref.read(playerProvider.notifier).playPause();
ref.read(backgroundSoundsNotifierProvider.notifier).togglePlayPause(isPlaying);
ref
.read(backgroundSoundsNotifierProvider.notifier)
.togglePlayPause(isPlaying);
}
bool _isBackgroundSoundSelected() {

View File

@ -81,9 +81,9 @@ class ArtistTitleWidget extends ConsumerWidget {
void _handleArtistNameTap(BuildContext context) async {
if (isPlayerScreen && artistUrlPath != null) {
await handleNavigation(
context: context,
TypeConstants.url,
[artistUrlPath],
context,
);
}
}

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:uni_links/uni_links.dart';
class SplashView extends ConsumerStatefulWidget {
const SplashView({super.key});

View File

@ -86,9 +86,9 @@ class _TrackViewState extends ConsumerState<TrackView>
bgSoundNotifier.getVolumeFromPref();
bgSoundNotifier.playBackgroundSoundFromPref();
await ref.read(playerProvider.notifier).loadSelectedTrack(
trackModel: trackModel,
file: file,
);
trackModel: trackModel,
file: file,
);
unawaited(context.push(RouteConstants.playerPath));
} catch (e) {
print(e);
@ -331,9 +331,9 @@ class _TrackViewState extends ConsumerState<TrackView>
),
onTapLink: (text, href, title) {
handleNavigation(
context: context,
TypeConstants.url,
[href],
context,
);
},
),

View File

@ -58,9 +58,9 @@ class MarkdownWidget extends StatelessWidget {
void _linkTap(BuildContext context, String? href) {
handleNavigation(
context: context,
TypeConstants.url,
[href],
context,
);
}
}

View File

@ -1469,6 +1469,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.2"
uni_links:
dependency: "direct main"
description:
name: uni_links
sha256: "051098acfc9e26a9fde03b487bef5d3d228ca8f67693480c6f33fd4fbb8e2b6e"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
uni_links_platform_interface:
dependency: transitive
description:
name: uni_links_platform_interface
sha256: "929cf1a71b59e3b7c2d8a2605a9cf7e0b125b13bc858e55083d88c62722d4507"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
uni_links_web:
dependency: transitive
description:
name: uni_links_web
sha256: "7539db908e25f67de2438e33cc1020b30ab94e66720b5677ba6763b25f6394df"
url: "https://pub.dev"
source: hosted
version: "0.1.0"
url_launcher:
dependency: "direct main"
description:

View File

@ -61,6 +61,7 @@ dependencies:
shared_preferences: ^2.2.2
url_launcher: ^6.1.14
workmanager: ^0.5.2
uni_links: ^0.5.1
dependency_overrides:
firebase_core_platform_interface: 4.5.1