Troubleshooting
Troubleshoot and resolve edge cases regarding known limitations and bundling.
If you need help solving issues with Sentry's Flutter SDK, you can read the edge cases documented here. If you need additional help, you can ask on GitHub. Customers on a paid plan may also contact support.
Starting with Android 15, AOSP supports devices with a 16 KB page size. If your app uses NDK libraries (directly or via an SDK), you’ll need to rebuild it for compatibility with these devices.
Update to Sentry Flutter SDK version 8.11.0
and above order to support 16 KB page sizes on Android devices.
Please read the Android developer documentation to update and test your app with 16 KB page size support.
Starting May 1, 2024, Apple requires all apps submitted to the App Store to provide a list of privacy-related APIs they use, including the reasons under which they use it. If you received an email from Apple with the message "ITMS-91053: Missing API declaration", your app doesn't fulfill the requirements. To solve this, follow our Apple Privacy Manifest guide.
- If you enable the
split-debug-info
andobfuscate
features, you must upload debug symbols. - Issue titles might be obfuscated as we rely on the
runtimeType
, but they may not be human-readable. See the Obfuscate Caveat. - Layout related errors are only caught by FlutterError.onError in debug mode. In release mode, they are removed by the Flutter framework. See Flutter build modes.
- Use inbound filters to exclude unhandled errors that are caught outside of your application in release builds. The SDK cannot filter these directly due to obfuscated stack traces.
- If your app runs on Windows and uses a Flutter version below
3.3.0
, you need to set the version and build number manually, see this issue on GitHub. To do so:- Use Dart defines to build the app:
flutter build windows --dart-define=SENTRY_RELEASE=my_app@1.0.0+1
- Or, set the release on SentryOptions
options.release = 'my_app@1.0.0+1'
during SDK initialization.
- Use Dart defines to build the app:
The Sentry Flutter SDK includes the Sentry Android SDK, which bundles multiple native libraries for multiple ABIs. Building a Flutter app for a specific ABI using the --target-platform
argument, for example an ARM 32bit apk, looks like this, which should also include the --split-per-abi
flag:
flutter build apk --target-platform=android-arm --split-per-abi
The configuration for symbolication of Native events (C/C++) is documented in our Android Native Development Kit content. If you are having issues with symbolication in Flutter, check that your configuration is correct, as discussed in our Flutter content that covers Uploading for Android NDK
Flutter split-debug-info
and obfuscate
flags are supported on iOS/macOS. They require compiling your app using Flutter, version 3.7.0
and above and the Sentry Flutter SDK, version 6.10.0
and above.
Source Context support requires compiling your app using the split-debug-info
build parameter on Flutter 3.10.0
and above. You must also upload debug symbols with the upload_sources
option enabled.
If you are using the Sentry Dart Plugin to upload Debug Symbols, refer to the points below to resolve potential issues.
A Sentry auth_token
can be generated at the Organization Auth Tokens settings page.
Dart's --obfuscate
option is required to be paired with --split-debug-info
to generate a symbol map. See Dart docs for more information.
The --split-debug-info
option requires setting an output directory. The directory must be an inner folder of the project's folder. See Flutter docs for more information.
Flutter's build web
command requires setting the --source-maps
parameter to generate source maps. See Flutter GitHub Issue for more information.
There is an issue with the Sentry Flutter SDK where apps can crash if taking screenshots in the background.
This is an issue with Flutter itself and should be fixed in Flutter 3.15.
For prior versions, you can work around this by configuring the SDK only to take screenshots when the app is in the resumed
state. To do this, set SentryFlutterOptions.attachScreenshotOnlyWhenResumed
to true
.
Multi-view embedding was introduced in Flutter 3.24. You'll find a detailed guide about it in the Flutter docs .
Using Sentry in a multi-view application is possible, but there are some limitations you should be aware of. The following features don't currently support multi-view:
- Screenshots via the
SentryScreenshotWidget
(which is part of theSentryWidget
) - User interaction integration via the
SentryUserInteractionWidget
(which is part of theSentryWidget
) - Window and Device events via the
WidgetsBindingIntegration
To prevent the WidgetsBindingIntegration
from loading by default, you'll need to remove the integration as shown below:
// ignore: implementation_imports
import 'package:sentry_flutter/src/integrations/widgets_binding_integration.dart';
...
SentryFlutter.init(
(options) {
...
final integration = options.integrations
.firstWhere((element) => element is WidgetsBindingIntegration);
options.removeIntegration(integration);
},
// Init your App.
appRunner: appRunner,
);
Copy the main.dart
file into the lib
folder of your existing project. This file already contains the code of the multi_view_app.dart
from the flutter documentation
. Next, copy the flutter_bootstrap.js
file and the index.html
file into the web
folder.
Make sure you're using Flutter 3.24 or newer and run the application.
Now you should be able to see two instances of the same application side by side, with different ViewIds in the body
.
main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_flutter/src/integrations/widgets_binding_integration.dart';
import 'dart:ui' show FlutterView;
const String exampleDsn = 'https://examplePublicKey@o0.ingest.sentry.io/0';
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = exampleDsn;
final integration = options.integrations
.firstWhere((element) => element is WidgetsBindingIntegration);
options.removeIntegration(integration);
},
// Init your App.
appRunner: () => runWidget(
MultiViewApp(
viewBuilder: (BuildContext context) => DefaultAssetBundle(
bundle: SentryAssetBundle(),
child: const MyApp(),
),
),
),
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers: [
SentryNavigatorObserver(),
],
home: Scaffold(
body: Center(
child: Text(
'Sentry Flutter Example (ViewId:${View.of(context).viewId})'),
),
),
);
}
}
// multi_view_app.dart
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Calls [viewBuilder] for every view added to the app to obtain the widget to
/// render into that view. The current view can be looked up with [View.of].
class MultiViewApp extends StatefulWidget {
const MultiViewApp({super.key, required this.viewBuilder});
final WidgetBuilder viewBuilder;
State<MultiViewApp> createState() => _MultiViewAppState();
}
class _MultiViewAppState extends State<MultiViewApp>
with WidgetsBindingObserver {
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_updateViews();
}
void didUpdateWidget(MultiViewApp oldWidget) {
super.didUpdateWidget(oldWidget);
// Need to re-evaluate the viewBuilder callback for all views.
_views.clear();
_updateViews();
}
void didChangeMetrics() {
_updateViews();
}
Map<Object, Widget> _views = <Object, Widget>{};
void _updateViews() {
final Map<Object, Widget> newViews = <Object, Widget>{};
for (final FlutterView view
in WidgetsBinding.instance.platformDispatcher.views) {
final Widget viewWidget = _views[view.viewId] ?? _createViewWidget(view);
newViews[view.viewId] = viewWidget;
}
setState(() {
_views = newViews;
});
}
Widget _createViewWidget(FlutterView view) {
return View(
view: view,
child: Builder(
builder: widget.viewBuilder,
),
);
}
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Widget build(BuildContext context) {
return ViewCollection(views: _views.values.toList(growable: false));
}
}
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").