traput_dynamic_linking 0.0.1
traput_dynamic_linking: ^0.0.1 copied to clipboard
Traput Dynamic Linking SDK for Flutter. Handles Universal Links (iOS), App Links (Android), and deferred deep links via a single API (Traput.onLink()). Logic-only, no UI.
import 'package:flutter/material.dart';
import 'package:traput_dynamic_linking/traput_dynamic_linking.dart';
import 'screens/home_screen.dart';
import 'screens/link_opened_screen.dart';
import 'screens/deferred_screen.dart';
import 'screens/debug_screen.dart';
const _baseUrl = String.fromEnvironment('TRAPUT_BASE_URL');
const _apiKey = String.fromEnvironment('TRAPUT_API_KEY');
const _subdomain = String.fromEnvironment('TRAPUT_SUBDOMAIN');
void main() {
WidgetsFlutterBinding.ensureInitialized();
final missing = <String>[
if (_baseUrl.trim().isEmpty) 'TRAPUT_BASE_URL',
if (_apiKey.trim().isEmpty) 'TRAPUT_API_KEY',
if (_subdomain.trim().isEmpty) 'TRAPUT_SUBDOMAIN',
];
if (missing.isNotEmpty) {
runApp(ConfigErrorApp(missingKeys: missing));
return;
}
Traput.initialize(
apiKey: _apiKey,
baseUrl: _baseUrl,
subdomain: _subdomain,
enableDebugLogging: true,
);
runApp(const MyApp());
}
class ConfigErrorApp extends StatelessWidget {
const ConfigErrorApp({super.key, required this.missingKeys});
final List<String> missingKeys;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 64, color: Colors.red.shade300),
const SizedBox(height: 24),
Text(
'Configuration Missing',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.red.shade700,
),
),
const SizedBox(height: 16),
Text(
'Missing: ${missingKeys.join(', ')}',
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 8),
const Text(
'Provide them via --dart-define or --dart-define-from-file.',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.grey),
),
const SizedBox(height: 24),
const Text(
'See README.md for setup instructions.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontStyle: FontStyle.italic,
color: Colors.grey,
),
),
],
),
),
),
),
);
}
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
final List<TraputLink> _linkHistory = [];
TraputLink? _currentLink;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
// Register callback to handle all link types
Traput.onLink((link) {
if (!mounted) return;
setState(() {
_currentLink = link;
_linkHistory.insert(0, link);
// Keep only last 20 links
if (_linkHistory.length > 20) {
_linkHistory.removeLast();
}
});
// Navigate to appropriate screen based on link type
if (link.isDeferred) {
_navigatorKey.currentState?.pushNamed('/deferred', arguments: link);
} else {
_navigatorKey.currentState?.pushNamed('/link-opened', arguments: link);
}
});
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed) {
Traput.checkPendingLink();
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Traput Demo',
navigatorKey: _navigatorKey,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const HomeScreen(),
onGenerateRoute: (settings) {
switch (settings.name) {
case '/link-opened':
final link = settings.arguments as TraputLink;
return MaterialPageRoute(
builder: (context) => LinkOpenedScreen(link: link),
);
case '/deferred':
final link = settings.arguments as TraputLink;
return MaterialPageRoute(
builder: (context) => DeferredScreen(link: link),
);
case '/debug':
return MaterialPageRoute(
builder: (context) => DebugScreen(
linkHistory: _linkHistory,
onClearHistory: () {
setState(() {
_linkHistory.clear();
});
Navigator.pop(context);
},
),
);
default:
return MaterialPageRoute(
builder: (context) => const HomeScreen(),
);
}
},
);
}
}