How to Handle Deep Linking in Flutter: Fix and Best Practices
uni_links package to listen for incoming links and parse them to navigate your app. Make sure to configure your Android and iOS platforms properly to support deep links and handle link streams in your Flutter code.Why This Happens
Deep linking issues in Flutter often happen because the app does not listen to incoming links properly or the platform (Android/iOS) is not configured to handle the links. For example, if you only check for the initial link once and ignore link streams, your app won't respond to links opened while running.
import 'package:flutter/material.dart'; import 'package:uni_links/uni_links.dart'; void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { String _link = 'No link detected'; @override void initState() { super.initState(); // Only get initial link once, no stream listening initUniLinks(); } Future<void> initUniLinks() async { try { final initialLink = await getInitialLink(); if (initialLink != null) { setState(() { _link = initialLink; }); } } catch (e) { setState(() { _link = 'Failed to get initial link'; }); } } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('Deep Linking Broken Example')), body: Center(child: Text(_link)), ), ); } }
The Fix
To fix deep linking, listen to the link stream so your app responds to links opened anytime. Also, configure AndroidManifest.xml and Info.plist to declare supported URL schemes or app links. This ensures your app receives the links from the OS.
import 'package:flutter/material.dart'; import 'package:uni_links/uni_links.dart'; import 'dart:async'; void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { String _link = 'No link detected'; StreamSubscription<String?>? _sub; @override void initState() { super.initState(); initUniLinks(); } Future<void> initUniLinks() async { try { final initialLink = await getInitialLink(); if (initialLink != null) { setState(() { _link = initialLink; }); } } catch (e) { setState(() { _link = 'Failed to get initial link'; }); } _sub = linkStream.listen((String? link) { if (link != null) { setState(() { _link = link; }); } }, onError: (err) { setState(() { _link = 'Failed to listen for links'; }); }); } @override void dispose() { _sub?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('Deep Linking Fixed Example')), body: Center(child: Text(_link)), ), ); } }
Prevention
Always listen to the linkStream to handle links opened while the app runs. Configure your AndroidManifest.xml with intent filters and your iOS Info.plist with URL schemes or universal links. Test deep linking on real devices and simulators. Use packages like uni_links or go_router for easier navigation handling.
Keep your deep link handling code clean and centralized to avoid bugs. Use error handling to catch malformed links. Regularly update dependencies to get latest fixes.
Related Errors
- App not opening from link: Check platform config and URL scheme correctness.
- Link stream not firing: Ensure you subscribe to
linkStreamand cancel subscription on dispose. - Malformed link parsing: Validate and parse links safely to avoid crashes.