Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Unreleased

* **Corrected minimums.** `pubspec.yaml` now states the Flutter and Dart versions the SDK actually needs: Flutter 3.38.1+ and Dart 3.10+ (it said Flutter 3.10 and Dart 3.0). The oldest versions of `app_links`, `device_info_plus` and `package_info_plus` the SDK accepts already required them, so an older toolchain could not resolve the package; nothing changes for an app that builds today. Apps need iOS 13+ and Android API 24+ (`minSdk 24`), which every accepted `app_links` version requires. The example app sets `platform :ios, '13.0'` and `minSdk = 24` explicitly.

## 0.6.0

* **Google Play install referrer.** On Android, the launch that reports the install reads the Play install referrer and sends it as `installReferrer`. A LinkForty link puts its click id in the referrer of the Play listing it sends a visitor to, so a server on `@linkforty/core` 1.41.1 or later matches the install to that exact click: `attributionMethod` is `"install_referrer"`, `confidenceScore` 100, `matchedFactors` `["install_referrer"]` and `competingLinks` 0. The SDK waits at most 2 seconds for Play and asks once more after a failed read; without a referrer (iOS, another store, a sideload, no answer) the install is matched by fingerprint as before.
Expand Down
3 changes: 2 additions & 1 deletion example/android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ android {
applicationId = "linkforty.com.example"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
// API 24 is the minimum the LinkForty SDK's dependencies (app_links) support.
minSdk = 24
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
Expand Down
4 changes: 2 additions & 2 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
# iOS 13 is the minimum the LinkForty SDK's dependencies (app_links) support.
platform :ios, '13.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
143 changes: 59 additions & 84 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ void main() {
}

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'LinkForty Example',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
// Auto-reports a `screen_view` (stamped with last-click attribution) for
// every named route. Give your routes names to have them tracked.
navigatorObservers: [LinkFortyNavigatorObserver()],
Expand All @@ -26,7 +23,7 @@ class MyApp extends StatelessWidget {
}

class LinkFortyDemo extends StatefulWidget {
const LinkFortyDemo({Key? key}) : super(key: key);
const LinkFortyDemo({super.key});

@override
State<LinkFortyDemo> createState() => _LinkFortyDemoState();
Expand Down Expand Up @@ -108,13 +105,10 @@ class _LinkFortyDemoState extends State<LinkFortyDemo> {

Future<void> _trackEvent(String name) async {
try {
await LinkForty.instance.trackEvent(
name,
{
'timestamp': DateTime.now().millisecondsSinceEpoch / 1000,
'source': 'example_app',
},
);
await LinkForty.instance.trackEvent(name, {
'timestamp': DateTime.now().millisecondsSinceEpoch / 1000,
'source': 'example_app',
});

if (!mounted) return;
setState(() {
Expand Down Expand Up @@ -157,10 +151,7 @@ class _LinkFortyDemoState extends State<LinkFortyDemo> {
await LinkForty.instance.trackRevenue(
amount: amount,
currency: currency,
properties: {
'product': 'example_product',
'quantity': 1,
},
properties: {'product': 'example_product', 'quantity': 1},
);

if (!mounted) return;
Expand Down Expand Up @@ -232,9 +223,7 @@ class _LinkFortyDemoState extends State<LinkFortyDemo> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('LinkForty Example'),
),
appBar: AppBar(title: const Text('LinkForty Example')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
Expand Down Expand Up @@ -297,11 +286,7 @@ class _SectionContainer extends StatelessWidget {
final String title;
final Widget child;

const _SectionContainer({
Key? key,
required this.title,
required this.child,
}) : super(key: key);
const _SectionContainer({required this.title, required this.child});

@override
Widget build(BuildContext context) {
Expand All @@ -315,9 +300,10 @@ class _SectionContainer extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Text(
title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
child,
],
Expand All @@ -332,11 +318,10 @@ class _StatusSection extends StatelessWidget {
final int queuedEvents;

const _StatusSection({
Key? key,
required this.isInitialized,
required this.eventCount,
required this.queuedEvents,
}) : super(key: key);
});

@override
Widget build(BuildContext context) {
Expand All @@ -353,13 +338,17 @@ class _StatusSection extends StatelessWidget {
),
_RowItem(
label: 'Events Tracked:',
value: Text('$eventCount',
style: const TextStyle(fontWeight: FontWeight.bold)),
value: Text(
'$eventCount',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
_RowItem(
label: 'Queued Events:',
value: Text('$queuedEvents',
style: const TextStyle(fontWeight: FontWeight.bold)),
value: Text(
'$queuedEvents',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
],
),
Expand All @@ -371,11 +360,7 @@ class _AttributionSection extends StatelessWidget {
final String? installId;
final bool isAttributed;

const _AttributionSection({
Key? key,
this.installId,
required this.isAttributed,
}) : super(key: key);
const _AttributionSection({this.installId, required this.isAttributed});

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -414,12 +399,11 @@ class _EventsSection extends StatelessWidget {
final VoidCallback onFlushEvents;

const _EventsSection({
Key? key,
required this.onTrackEvent,
required this.onTrackScreenView,
required this.onTrackRevenue,
required this.onFlushEvents,
}) : super(key: key);
});

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -473,11 +457,10 @@ class _LinkCreationSection extends StatelessWidget {
final String? createdLinkShortCode;

const _LinkCreationSection({
Key? key,
required this.onCreateLink,
this.createdLinkURL,
this.createdLinkShortCode,
}) : super(key: key);
});

@override
Widget build(BuildContext context) {
Expand All @@ -495,8 +478,10 @@ class _LinkCreationSection extends StatelessWidget {
const SizedBox(height: 10),
Text('URL: $createdLinkURL', style: const TextStyle(fontSize: 12)),
if (createdLinkShortCode != null)
Text('Short Code: $createdLinkShortCode',
style: const TextStyle(fontSize: 12)),
Text(
'Short Code: $createdLinkShortCode',
style: const TextStyle(fontSize: 12),
),
],
],
),
Expand All @@ -507,18 +492,17 @@ class _LinkCreationSection extends StatelessWidget {
class _DeepLinkSection extends StatelessWidget {
final DeepLinkData? deepLinkData;

const _DeepLinkSection({
Key? key,
this.deepLinkData,
}) : super(key: key);
const _DeepLinkSection({this.deepLinkData});

@override
Widget build(BuildContext context) {
return _SectionContainer(
title: 'Deep Link Data',
child: deepLinkData == null
? const Text('No deep link data',
style: TextStyle(color: Colors.grey, fontSize: 12))
? const Text(
'No deep link data',
style: TextStyle(color: Colors.grey, fontSize: 12),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expand All @@ -527,31 +511,38 @@ class _DeepLinkSection extends StatelessWidget {
_TextItem(label: 'iOS URL', value: deepLinkData!.iosURL),
if (deepLinkData!.deepLinkPath != null)
_TextItem(
label: 'Deep Link Path',
value: deepLinkData!.deepLinkPath),
label: 'Deep Link Path',
value: deepLinkData!.deepLinkPath,
),
if (deepLinkData!.appScheme != null)
_TextItem(
label: 'App Scheme', value: deepLinkData!.appScheme),
label: 'App Scheme',
value: deepLinkData!.appScheme,
),
if (deepLinkData!.linkId != null)
_TextItem(
label: 'Link ID',
value: deepLinkData!.linkId,
color: Colors.grey),
label: 'Link ID',
value: deepLinkData!.linkId,
color: Colors.grey,
),
if (deepLinkData!.utmParameters != null) ...[
if (deepLinkData!.utmParameters!.source != null)
_TextItem(
label: 'UTM Source',
value: deepLinkData!.utmParameters!.source),
label: 'UTM Source',
value: deepLinkData!.utmParameters!.source,
),
if (deepLinkData!.utmParameters!.campaign != null)
_TextItem(
label: 'UTM Campaign',
value: deepLinkData!.utmParameters!.campaign),
label: 'UTM Campaign',
value: deepLinkData!.utmParameters!.campaign,
),
],
if (deepLinkData!.customParameters != null &&
deepLinkData!.customParameters!.isNotEmpty)
_TextItem(
label: 'Custom Params',
value: '${deepLinkData!.customParameters!.length}'),
label: 'Custom Params',
value: '${deepLinkData!.customParameters!.length}',
),
],
),
);
Expand All @@ -561,10 +552,7 @@ class _DeepLinkSection extends StatelessWidget {
class _DataManagementSection extends StatelessWidget {
final VoidCallback onClearData;

const _DataManagementSection({
Key? key,
required this.onClearData,
}) : super(key: key);
const _DataManagementSection({required this.onClearData});

@override
Widget build(BuildContext context) {
Expand All @@ -584,22 +572,15 @@ class _RowItem extends StatelessWidget {
final String label;
final Widget value;

const _RowItem({
Key? key,
required this.label,
required this.value,
}) : super(key: key);
const _RowItem({required this.label, required this.value});

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label),
value,
],
children: [Text(label), value],
),
);
}
Expand All @@ -610,12 +591,7 @@ class _TextItem extends StatelessWidget {
final String? value;
final Color? color;

const _TextItem({
Key? key,
required this.label,
this.value,
this.color,
}) : super(key: key);
const _TextItem({required this.label, this.value, this.color});

@override
Widget build(BuildContext context) {
Expand All @@ -636,12 +612,11 @@ class _FullWidthButton extends StatelessWidget {
final Color color;

const _FullWidthButton({
Key? key,
required this.onPressed,
required this.icon,
required this.label,
required this.color,
}) : super(key: key);
});

@override
Widget build(BuildContext context) {
Expand Down
4 changes: 2 additions & 2 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1

environment:
sdk: '>=2.12.0 <4.0.0'
flutter: '>=3.0.0'
sdk: '>=3.10.0 <4.0.0'
flutter: '>=3.38.1'

# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
Expand Down
Loading
Loading