diff --git a/android/app/build.gradle b/android/app/build.gradle
index 1ebd5a9b..b4f1f08b 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -47,7 +47,7 @@ android {
applicationId "com.webreinvent.vaahflutter"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
- minSdkVersion flutter.minSdkVersion
+ minSdkVersion 19
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index ab80b9a6..84c4431e 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -24,6 +24,12 @@
+
+
+
+
+
+
diff --git a/ios/Podfile b/ios/Podfile
index 88359b22..e98a4545 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -37,5 +37,8 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
+ target.build_configurations.each do |config|
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
+ end
end
end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 9e4b38ac..9771e359 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -33,14 +33,40 @@ PODS:
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
+ - Firebase/CoreOnly (10.3.0):
+ - FirebaseCore (= 10.3.0)
+ - Firebase/DynamicLinks (10.3.0):
+ - Firebase/CoreOnly
+ - FirebaseDynamicLinks (~> 10.3.0)
+ - firebase_core (2.7.1):
+ - Firebase/CoreOnly (= 10.3.0)
+ - Flutter
+ - firebase_dynamic_links (5.0.16):
+ - Firebase/DynamicLinks (= 10.3.0)
+ - firebase_core
+ - Flutter
+ - FirebaseCore (10.3.0):
+ - FirebaseCoreInternal (~> 10.0)
+ - GoogleUtilities/Environment (~> 7.8)
+ - GoogleUtilities/Logger (~> 7.8)
+ - FirebaseCoreInternal (10.5.0):
+ - "GoogleUtilities/NSData+zlib (~> 7.8)"
+ - FirebaseDynamicLinks (10.3.0):
+ - FirebaseCore (~> 10.0)
- Flutter (1.0.0)
- fluttertoast (0.0.2):
- Flutter
- Toast
+ - GoogleUtilities/Environment (7.11.0):
+ - PromisesObjC (< 3.0, >= 1.2)
+ - GoogleUtilities/Logger (7.11.0):
+ - GoogleUtilities/Environment
+ - "GoogleUtilities/NSData+zlib (7.11.0)"
- package_info_plus (0.4.5):
- Flutter
- path_provider_ios (0.0.1):
- Flutter
+ - PromisesObjC (2.2.0)
- SDWebImage (5.14.2):
- SDWebImage/Core (= 5.14.2)
- SDWebImage/Core (5.14.2)
@@ -54,6 +80,8 @@ PODS:
DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
+ - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
+ - firebase_dynamic_links (from `.symlinks/plugins/firebase_dynamic_links/ios`)
- Flutter (from `Flutter`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
@@ -64,6 +92,12 @@ SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
+ - Firebase
+ - FirebaseCore
+ - FirebaseCoreInternal
+ - FirebaseDynamicLinks
+ - GoogleUtilities
+ - PromisesObjC
- SDWebImage
- Sentry
- SwiftyGif
@@ -72,6 +106,10 @@ SPEC REPOS:
EXTERNAL SOURCES:
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
+ firebase_core:
+ :path: ".symlinks/plugins/firebase_core/ios"
+ firebase_dynamic_links:
+ :path: ".symlinks/plugins/firebase_dynamic_links/ios"
Flutter:
:path: Flutter
fluttertoast:
@@ -87,9 +125,18 @@ SPEC CHECKSUMS:
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
+ Firebase: f92fc551ead69c94168d36c2b26188263860acd9
+ firebase_core: 1ae9f9aa76e6e1edc14fb181637ad466fd6c6fa4
+ firebase_dynamic_links: 42ab4dd387b07d5c7af2e9aa63a7b5fa3518dd8b
+ FirebaseCore: 988754646ab3bd4bdcb740f1bfe26b9f6c0d5f2a
+ FirebaseCoreInternal: e463f41bb935cd049505bf7e9a5bdd7dcea90df6
+ FirebaseDynamicLinks: 51c81d07bd63155bb56d76b0abdda79c8a3d8d02
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
+ GoogleUtilities: c2bdc4cf2ce786c4d2e6b3bcfd599a25ca78f06f
+ package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
+ PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
Sentry: 4c9babff9034785067c896fd580b1f7de44da020
sentry_flutter: b10ae7a5ddcbc7f04648eeb2672b5747230172f1
diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements
new file mode 100644
index 00000000..56178028
--- /dev/null
+++ b/ios/Runner/Runner.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.developer.associated-domains
+
+ applinks:YOUR_FIREBASE_APP_DYNAMIC_LINK_PREFIX.page.link
+
+
+
diff --git a/lib/main.dart b/lib/main.dart
index fc29ccae..8cbd3461 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -7,5 +7,7 @@ import './vaahextendflutter/base/base_controller.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
BaseController baseController = Get.put(BaseController());
- await baseController.init(const AppConfig()); // Pass main app as argument in init method
+ await baseController.init(
+ app: const AppConfig(),
+ ); // Pass main app as argument in init method
}
diff --git a/lib/vaahextendflutter/base/base_controller.dart b/lib/vaahextendflutter/base/base_controller.dart
index 0947a271..db4290dd 100644
--- a/lib/vaahextendflutter/base/base_controller.dart
+++ b/lib/vaahextendflutter/base/base_controller.dart
@@ -1,25 +1,41 @@
import 'dart:async';
+import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
-import '../../controllers/root_assets_controller.dart';
import '../app_theme.dart';
import '../env.dart';
import '../services/api.dart';
+import '../services/dynamic_links.dart';
class BaseController extends GetxController {
- Future init(Widget app) async {
+ Future init({
+ required Widget app,
+ FirebaseOptions? firebaseOptions,
+ }) async {
+ // Storage initialization to store some properties locally
await GetStorage.init();
+
+ // Environment initialization
EnvironmentConfig.setEnvConfig();
+ final EnvironmentConfig config = EnvironmentConfig.getEnvConfig();
+
+ // Initialization of Firebase and Services
+ if (firebaseOptions != null) {
+ await Firebase.initializeApp(
+ options: firebaseOptions,
+ );
+ DynamicLinks.init();
+ }
+
+ // Other Local Initializations (Depends on your app)
AppTheme.init();
Api.init();
- Get.put(RootAssetsController());
-
- final EnvironmentConfig config = EnvironmentConfig.getEnvConfig();
+ // Sentry Initialization (And/ Or) Running main app
if (null != config.sentryConfig && config.sentryConfig!.dsn.isNotEmpty) {
await SentryFlutter.init(
(options) => options
@@ -44,8 +60,10 @@ class BaseController extends GetxController {
child: child,
);
}
+ // Running main app
runApp(child);
} else {
+ // Running main app when sentry config is not there
runApp(app);
}
}
diff --git a/lib/vaahextendflutter/env.dart b/lib/vaahextendflutter/env.dart
index 2229d62e..150d8e57 100644
--- a/lib/vaahextendflutter/env.dart
+++ b/lib/vaahextendflutter/env.dart
@@ -26,7 +26,7 @@ final EnvironmentConfig defaultConfig = EnvironmentConfig(
enableCloudLogs: true,
enableApiLogInterceptor: true,
showDebugPanel: true,
- debugPanelColor: AppTheme.colors['black']!.withOpacity(0.7),
+ debugPanelColor: AppTheme.colors['black']!.withOpacity(0.8),
);
// To add new configuration add new key, value pair in envConfigs
diff --git a/lib/vaahextendflutter/services/dynamic_links.dart b/lib/vaahextendflutter/services/dynamic_links.dart
new file mode 100644
index 00000000..27c44b9f
--- /dev/null
+++ b/lib/vaahextendflutter/services/dynamic_links.dart
@@ -0,0 +1,108 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
+import 'package:get/get.dart';
+
+import './logging_library/logging_library.dart';
+
+abstract class DynamicLinks {
+ static void init() async {
+ // handle initial dynamic link
+ final getInitialLink = await _firebaseDynamicLinks.getInitialLink();
+ if (getInitialLink != null) {
+ _handleLink(getInitialLink);
+ }
+
+ // listen/ subscribe to the links which comes later and handle them
+ _firebaseDynamicLinks.onLink
+ .listen(
+ _handleLink,
+ )
+ .onError(
+ (error, stackTrace) => Log.exception(error, stackTrace: stackTrace),
+ );
+ }
+
+ static final FirebaseDynamicLinks _firebaseDynamicLinks = FirebaseDynamicLinks.instance;
+ static final StreamController _dynamicLinksStreamController =
+ StreamController.broadcast();
+ static final Stream dynamicLinksStream = _dynamicLinksStreamController.stream;
+
+ static Future createLink({
+ required String? path,
+ required dynamic data,
+ required dynamic auth,
+ }) async {
+ try {
+ final String parameters = jsonEncode({"path": path, "data": data, "auth": auth});
+ return await _firebaseDynamicLinks.buildShortLink(
+ DynamicLinkParameters(
+ link: Uri.parse("https://your.domain?payload=$parameters"),
+ uriPrefix: "https://YOUR_FIREBASE_APP_DYNAMIC_LINK_PREFIX.page.link",
+ androidParameters: const AndroidParameters(packageName: "your.package.name"),
+ iosParameters: const IOSParameters(bundleId: "your.bundle.identifier"),
+ ),
+ shortLinkType: ShortDynamicLinkType.unguessable,
+ );
+ } catch (error, stackTrace) {
+ Log.exception(error, stackTrace: stackTrace, hint: "Error creating dynamic link!");
+ return null;
+ }
+ }
+
+ static Future _handleLink(PendingDynamicLinkData linkData) async {
+ try {
+ final Uri decodedLink = Uri.parse(Uri.decodeFull(linkData.link.toString()));
+ final dynamic payload = _decodePayload(decodedLink);
+ _dynamicLinksStreamController.add(
+ DeepLink(
+ encoded: linkData.link.toString(),
+ decoded: "${linkData.link.host}${linkData.link.path}?payload=$payload",
+ ),
+ );
+ Log.success({
+ "encoded": linkData.link.toString(),
+ "decoded": "${linkData.link.host}${linkData.link.path}?payload=$payload",
+ });
+ if (payload != null && payload['path'] != null) {
+ Get.offAllNamed(
+ payload['path'],
+ arguments: {
+ 'data': payload['data'],
+ 'auth': payload['auth'],
+ },
+ );
+ }
+ } catch (error, stackTrace) {
+ Log.exception(
+ error,
+ stackTrace: stackTrace,
+ hint: "Error handling dynamic link! ${linkData.asMap()}",
+ );
+ }
+ }
+
+ static dynamic _decodePayload(Uri link) {
+ try {
+ return jsonDecode(link.queryParameters['payload'].toString());
+ } catch (error, stackTrace) {
+ Log.exception(
+ error,
+ stackTrace: stackTrace,
+ hint: "Error decoding payload! $link",
+ );
+ return null;
+ }
+ }
+}
+
+class DeepLink {
+ final String encoded;
+ final String decoded;
+
+ const DeepLink({
+ required this.encoded,
+ required this.decoded,
+ });
+}
diff --git a/lib/vaahextendflutter/services/logging_library/logging_library.dart b/lib/vaahextendflutter/services/logging_library/logging_library.dart
index fb20f2e3..e4461423 100644
--- a/lib/vaahextendflutter/services/logging_library/logging_library.dart
+++ b/lib/vaahextendflutter/services/logging_library/logging_library.dart
@@ -77,7 +77,7 @@ class Log {
bool disableCloudLogging = false,
}) {
if (_config.enableLocalLogs && !disableLocalLogging) {
- Console.danger(throwable.toString(), data);
+ Console.danger('$throwable\n$hint', data);
}
if (_config.enableCloudLogs && !disableCloudLogging) {
final hintWithData = {
diff --git a/lib/vaahextendflutter/widgets/atoms/app_expansion_panel.dart b/lib/vaahextendflutter/widgets/atoms/app_expansion_panel.dart
index 6a7801fc..6b0f6e22 100644
--- a/lib/vaahextendflutter/widgets/atoms/app_expansion_panel.dart
+++ b/lib/vaahextendflutter/widgets/atoms/app_expansion_panel.dart
@@ -143,7 +143,8 @@ class _AppExpansionPanelState extends State
@immutable
class AppExpansionPanelIcon extends StatelessWidget {
- const AppExpansionPanelIcon({Key? key}) : super(key: key);
+ final Color? color;
+ const AppExpansionPanelIcon({Key? key, this.color}) : super(key: key);
@override
Widget build(BuildContext context) {
@@ -151,7 +152,7 @@ class AppExpansionPanelIcon extends StatelessWidget {
turns: context.findAncestorStateOfType<_AppExpansionPanelState>()!._iconTurns,
child: FaIcon(
FontAwesomeIcons.angleDown,
- color: AppTheme.colors['primary'],
+ color: color ?? AppTheme.colors['primary'],
),
);
}
diff --git a/lib/vaahextendflutter/widgets/debug.dart b/lib/vaahextendflutter/widgets/debug.dart
index 5831dd21..ae81feae 100644
--- a/lib/vaahextendflutter/widgets/debug.dart
+++ b/lib/vaahextendflutter/widgets/debug.dart
@@ -8,10 +8,13 @@
// *****************************************
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import '../app_theme.dart';
import '../env.dart';
import '../helpers/constants.dart';
import '../helpers/styles.dart';
+import '../services/dynamic_links.dart';
const double constHandleWidth = 180.0; // tag handle width
const double constHandleHeight = 38.0; // tag handle height
@@ -85,111 +88,200 @@ class DebugWidgetState extends State with SingleTickerProviderState
@override
Widget build(BuildContext context) {
+ final double topMargin = MediaQuery.of(context).padding.top + defaultMargin;
return showDebugPanel
? LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
- final height = constraints.maxHeight;
+ final height = constraints.maxHeight - topMargin;
final minFactor = (_handleHeight / height);
return Stack(
fit: StackFit.expand,
children: [
widget.child,
- GestureDetector(
- onVerticalDragDown: (DragDownDetails details) {
- _controller.stop();
- },
- onVerticalDragUpdate: (DragUpdateDetails details) {
- _controller.value += (-details.primaryDelta! / height);
- },
- onVerticalDragEnd: (DragEndDetails details) {
- if (_controller.isDismissed) {
- return;
- }
- if (details.primaryVelocity!.abs() >= 365.0) {
- final visualVelocity = -details.primaryVelocity! / height;
- _controller.fling(velocity: visualVelocity);
- } else if (_controller.value < 0.5) {
- close();
- } else {
- open();
- }
- },
- onVerticalDragCancel: () {
- if (_controller.isDismissed || _controller.isAnimating) {
- return;
- }
- if (_controller.value < 0.5) {
- close();
- } else {
- open();
- }
- },
- excludeFromSemantics: true,
- child: RepaintBoundary(
- child: Align(
- alignment: Alignment.bottomCenter,
- child: AnimatedBuilder(
- animation: _controller,
- builder: (BuildContext context, Widget? child) {
- return Align(
- alignment: Alignment.topCenter,
- heightFactor: _controller.value + minFactor,
- child: child,
- );
- },
- child: RepaintBoundary(
- child: FocusScope(
- key: _drawerKey,
- node: _focusScopeNode,
- child: _EnvPanel(
- handleHeight: _handleHeight,
- onHandlePressed: toggle,
- config: _environmentConfig,
- child: Builder(
- builder: (BuildContext context) {
- return ListView(
- primary: false,
- padding: EdgeInsets.only(
- top: MediaQuery.of(context).padding.top +
- defaultPadding +
- _handleHeight,
- bottom: defaultPadding,
- left: defaultPadding,
- right: defaultPadding,
- ),
- children: [
- ..._showDetails(
- [
- 'App Title: ${_environmentConfig.appTitle}',
- 'App Title Short: ${_environmentConfig.appTitleShort}',
- _environmentConfig.envType,
- 'Version: ${_environmentConfig.version}',
- 'Build: ${_environmentConfig.build}',
- ],
+ Container(
+ margin: EdgeInsets.only(top: topMargin),
+ child: GestureDetector(
+ onVerticalDragDown: (DragDownDetails details) {
+ _controller.stop();
+ },
+ onVerticalDragUpdate: (DragUpdateDetails details) {
+ _controller.value += (-details.primaryDelta! / height);
+ },
+ onVerticalDragEnd: (DragEndDetails details) {
+ if (_controller.isDismissed) {
+ return;
+ }
+ if (details.primaryVelocity!.abs() >= 365.0) {
+ final visualVelocity = -details.primaryVelocity! / height;
+ _controller.fling(velocity: visualVelocity);
+ } else if (_controller.value < 0.5) {
+ close();
+ } else {
+ open();
+ }
+ },
+ onVerticalDragCancel: () {
+ if (_controller.isDismissed || _controller.isAnimating) {
+ return;
+ }
+ if (_controller.value < 0.5) {
+ close();
+ } else {
+ open();
+ }
+ },
+ excludeFromSemantics: true,
+ child: RepaintBoundary(
+ child: Align(
+ alignment: Alignment.bottomCenter,
+ child: AnimatedBuilder(
+ animation: _controller,
+ builder: (BuildContext context, Widget? child) {
+ return Align(
+ alignment: Alignment.topCenter,
+ heightFactor: _controller.value + minFactor,
+ child: child,
+ );
+ },
+ child: RepaintBoundary(
+ child: FocusScope(
+ key: _drawerKey,
+ node: _focusScopeNode,
+ child: _EnvPanel(
+ handleHeight: _handleHeight,
+ onHandlePressed: toggle,
+ config: _environmentConfig,
+ child: Builder(
+ builder: (BuildContext context) {
+ return Padding(
+ padding: EdgeInsets.only(
+ top: _handleHeight,
),
- verticalMargin24,
- ..._showDetails(
- [
- 'Backend URL: ${_environmentConfig.backendUrl}',
- 'API URL: ${_environmentConfig.apiUrl}',
- 'Request and Response Timeout: ${(_environmentConfig.timeoutLimit) / 1000} Seconds',
- 'Firebase Id: ${_environmentConfig.firebaseId}',
- 'Local Logs Enabled (Console + Device Specific): ${_environmentConfig.enableLocalLogs}',
- 'Cloud Logs Enabled: ${_environmentConfig.enableCloudLogs}',
- if (null != _environmentConfig.sentryConfig) ...[
- 'Sentry DSN: ${_environmentConfig.sentryConfig!.dsn}',
- 'Sentry Auto App Start (Record Cold And Warm Start Time): ${_environmentConfig.sentryConfig!.autoAppStart}',
- 'Sentry Traces Sample Rate: ${_environmentConfig.sentryConfig!.tracesSampleRate}',
- 'Sentry User Interaction Tracing: ${_environmentConfig.sentryConfig!.enableUserInteractionTracing}',
- 'Sentry Auto Performance Tracking: ${_environmentConfig.sentryConfig!.enableAutoPerformanceTracking}',
- 'Sentry Assets Instrumentation: ${_environmentConfig.sentryConfig!.enableAssetsInstrumentation}',
- ],
- 'API Logs Interceptor: ${_environmentConfig.enableApiLogInterceptor}',
- ],
+ child: Container(
+ margin: allPadding24,
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ child: Column(
+ children: [
+ _ShowDetails(
+ contentHolder: PanelDataContentHolder(
+ content: {
+ 'App Title':
+ Data(value: _environmentConfig.appTitle),
+ 'App Title Short': Data(
+ value: _environmentConfig.appTitleShort,
+ ),
+ 'Environment': Data(
+ value: _environmentConfig.envType,
+ ),
+ 'Version':
+ Data(value: _environmentConfig.version),
+ 'Build':
+ Data(value: _environmentConfig.build),
+ 'Backend URL': Data(
+ value: _environmentConfig.backendUrl,
+ ),
+ 'API URL':
+ Data(value: _environmentConfig.apiUrl),
+ 'Request and Response Timeout': Data(
+ value:
+ '${_environmentConfig.timeoutLimit / 1000} Seconds',
+ ),
+ 'Firebase Id': Data(
+ value: _environmentConfig.firebaseId,
+ ),
+ 'API Logs Interceptor': Data(
+ value: _environmentConfig
+ .enableApiLogInterceptor
+ ? 'enabled'
+ : 'disabled',
+ color: _environmentConfig
+ .enableApiLogInterceptor
+ ? AppTheme.colors['success']
+ : AppTheme.colors['danger'],
+ ),
+ 'Local Logs': Data(
+ value: _environmentConfig.enableLocalLogs
+ ? 'enabled'
+ : 'disabled',
+ color: _environmentConfig.enableLocalLogs
+ ? AppTheme.colors['success']
+ : AppTheme.colors['danger'],
+ ),
+ 'Cloud Logs': Data(
+ value: _environmentConfig.enableCloudLogs
+ ? 'enabled'
+ : 'disabled',
+ color: _environmentConfig.enableCloudLogs
+ ? AppTheme.colors['success']
+ : AppTheme.colors['danger'],
+ ),
+ if (null !=
+ _environmentConfig.sentryConfig) ...{
+ 'Sentry DSN': Data(
+ value:
+ _environmentConfig.sentryConfig!.dsn,
+ ),
+ 'Sentry Traces Sample Rate': Data(
+ value: _environmentConfig
+ .sentryConfig!.tracesSampleRate
+ .toString(),
+ ),
+ 'Sentry Auto App Start (Record Cold And Warm Start Time)':
+ Data(
+ value: _environmentConfig
+ .sentryConfig!.autoAppStart
+ ? 'enabled'
+ : 'disabled',
+ color: _environmentConfig
+ .sentryConfig!.autoAppStart
+ ? AppTheme.colors['success']
+ : AppTheme.colors['danger'],
+ ),
+ 'Sentry User Interaction Tracing': Data(
+ value: _environmentConfig.sentryConfig!
+ .enableUserInteractionTracing
+ ? 'enabled'
+ : 'disabled',
+ color: _environmentConfig.sentryConfig!
+ .enableUserInteractionTracing
+ ? AppTheme.colors['success']
+ : AppTheme.colors['danger'],
+ ),
+ 'Sentry Auto Performance Tracking': Data(
+ value: _environmentConfig.sentryConfig!
+ .enableAutoPerformanceTracking
+ ? 'enabled'
+ : 'disabled',
+ color: _environmentConfig.sentryConfig!
+ .enableAutoPerformanceTracking
+ ? AppTheme.colors['success']
+ : AppTheme.colors['danger'],
+ ),
+ 'Sentry Assets Instrumentation': Data(
+ value: _environmentConfig.sentryConfig!
+ .enableAssetsInstrumentation
+ ? 'enabled'
+ : 'disabled',
+ color: _environmentConfig.sentryConfig!
+ .enableAssetsInstrumentation
+ ? AppTheme.colors['success']
+ : AppTheme.colors['danger'],
+ ),
+ },
+ },
+ ),
+ ),
+ verticalMargin24,
+ const _StreamLinksSection(),
+ verticalMargin24,
+ ],
+ ),
+ ),
),
- ],
- );
- },
+ );
+ },
+ ),
),
),
),
@@ -204,24 +296,6 @@ class DebugWidgetState extends State with SingleTickerProviderState
)
: widget.child;
}
-
- List _showDetails(List details) {
- return details
- .map(
- (detail) => Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SelectableText(
- detail,
- style: TextStyles.regular3,
- ),
- verticalMargin8,
- ],
- ),
- )
- .toList(growable: false);
- }
}
@immutable
@@ -379,3 +453,143 @@ class _PanelBorder extends ShapeBorder {
//
}
}
+
+class _StreamLinksSection extends StatefulWidget {
+ const _StreamLinksSection({Key? key}) : super(key: key);
+
+ @override
+ State<_StreamLinksSection> createState() => _StreamLinksSectionState();
+}
+
+class _StreamLinksSectionState extends State<_StreamLinksSection> {
+ DeepLink? link;
+
+ @override
+ void initState() {
+ super.initState();
+ DynamicLinks.dynamicLinksStream.listen((DeepLink deeplink) {
+ setState(() {
+ link = deeplink;
+ });
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return link == null
+ ? emptyWidget
+ : Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text('Dynamic Link'),
+ verticalMargin8,
+ _ShowDetails(contentHolder: PanelLinkContentHolder(content: link!)),
+ ],
+ );
+ }
+}
+
+class _ShowDetails extends StatefulWidget {
+ final PanelContentHolder contentHolder;
+
+ const _ShowDetails({
+ Key? key,
+ required this.contentHolder,
+ }) : super(key: key);
+
+ @override
+ State<_ShowDetails> createState() => _ShowDetailsState();
+}
+
+class _ShowDetailsState extends State<_ShowDetails> {
+ @override
+ Widget build(BuildContext context) {
+ final PanelContentHolder contentHolder = widget.contentHolder;
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (contentHolder is PanelDataContentHolder)
+ Builder(
+ builder: (context) {
+ final List rows = [];
+ contentHolder.content.forEach(
+ (key, data) {
+ rows.add(
+ TableRow(
+ children: [
+ Padding(
+ padding: allPadding8,
+ child: Text(key),
+ ),
+ Padding(
+ padding: allPadding8,
+ child: SelectableText(
+ data.value ?? '',
+ style: TextStyle(color: data.color ?? AppTheme.colors['warning']),
+ ),
+ ),
+ ],
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(color: AppTheme.colors['white']!.withOpacity(0.55)),
+ ),
+ ),
+ ),
+ );
+ },
+ );
+ return Table(
+ children: rows,
+ );
+ },
+ )
+ else if (contentHolder is PanelLinkContentHolder) ...[
+ SelectableText(
+ contentHolder.content.encoded,
+ style: TextStyles.regular3?.copyWith(color: AppTheme.colors['danger']),
+ onTap: () => Clipboard.setData(ClipboardData(text: contentHolder.content.encoded)),
+ ),
+ SelectableText(
+ contentHolder.content.decoded,
+ style: TextStyles.regular3?.copyWith(color: AppTheme.colors['success']),
+ onTap: () => Clipboard.setData(ClipboardData(text: contentHolder.content.decoded)),
+ ),
+ ]
+ ],
+ );
+ }
+}
+
+abstract class PanelContentHolder {
+ const PanelContentHolder();
+}
+
+class PanelDataContentHolder extends PanelContentHolder {
+ final Map content;
+
+ const PanelDataContentHolder({
+ required this.content,
+ });
+}
+
+class PanelLinkContentHolder extends PanelContentHolder {
+ final DeepLink content;
+
+ const PanelLinkContentHolder({
+ required this.content,
+ });
+}
+
+class Data {
+ final String? value;
+ final String? tooltip;
+ final Color? color;
+
+ const Data({
+ this.value,
+ this.tooltip,
+ this.color,
+ });
+}
diff --git a/pubspec.lock b/pubspec.lock
index f31766c8..3381b05a 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,6 +1,14 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ _flutterfire_internals:
+ dependency: transitive
+ description:
+ name: _flutterfire_internals
+ sha256: cb3a948a1eebbf8efd987c43f95418269930e912a88bc7b6a5a7658423133635
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.17"
async:
dependency: transitive
description:
@@ -105,6 +113,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.2.5"
+ firebase_core:
+ dependency: "direct main"
+ description:
+ name: firebase_core
+ sha256: "1c121a478af23755b0b93fd4aa70d3bd32a587dd51ef0a3979091ac0d2317d32"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.7.1"
+ firebase_core_platform_interface:
+ dependency: transitive
+ description:
+ name: firebase_core_platform_interface
+ sha256: "5615b30c36f55b2777d0533771deda7e5730e769e5d3cb7fda79e9bed86cfa55"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.5.3"
+ firebase_core_web:
+ dependency: transitive
+ description:
+ name: firebase_core_web
+ sha256: "0c1cf1f1022d2245ac117443bb95207952ca770281524d2908e323bc063fb8ff"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.2"
+ firebase_dynamic_links:
+ dependency: "direct main"
+ description:
+ name: firebase_dynamic_links
+ sha256: "6eb2ce2793fa68f2749d32ca0210cb355d64deaf3e5b123eca23ca0b7bad92fc"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.16"
+ firebase_dynamic_links_platform_interface:
+ dependency: transitive
+ description:
+ name: firebase_dynamic_links_platform_interface
+ sha256: "30470498001d3883d118da43de09e05415200ab668455bfd935943db73f06541"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.3+31"
flutter:
dependency: "direct main"
description: flutter
diff --git a/pubspec.yaml b/pubspec.yaml
index ea998b2c..ef9aae64 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -23,6 +23,8 @@ dependencies:
file_picker: ^5.2.5
font_awesome_flutter: ^10.4.0
sentry_flutter: ^6.20.1
+ firebase_dynamic_links: ^5.0.16
+ firebase_core: ^2.7.1
dev_dependencies:
flutter_test: