Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multi-view troubleshooting guide for sentry dart #11308

Merged
merged 18 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7a14338
add multi-view troubleshooting guide for sentry dart
mar-hai Sep 10, 2024
c58f930
remove unused code
mar-hai Sep 10, 2024
407ad91
rephrase text and split up in general and example app related part.
mar-hai Sep 10, 2024
4b69eae
add complete example application instead of sentry example app adapta…
mar-hai Sep 30, 2024
b7f5dc3
Merge branch 'master' into martinhaintz/fix-flutter-multiview
mar-hai Oct 1, 2024
2c30ea8
Update docs/platforms/flutter/troubleshooting.mdx
martinhaintz Oct 1, 2024
7f7955b
Update docs/platforms/flutter/troubleshooting.mdx
martinhaintz Oct 1, 2024
a2d37be
Update includes/troubleshooting/flutter/enable_multi_view.mdx
martinhaintz Oct 16, 2024
6ac644e
Update docs/platforms/flutter/troubleshooting.mdx
martinhaintz Oct 16, 2024
e66ed2f
Update docs/platforms/flutter/troubleshooting.mdx
martinhaintz Oct 22, 2024
242ec96
Update docs/platforms/flutter/troubleshooting.mdx
martinhaintz Oct 22, 2024
91a443d
Update docs/platforms/flutter/troubleshooting.mdx
martinhaintz Oct 22, 2024
68f99a6
Update docs/platforms/flutter/troubleshooting.mdx
martinhaintz Oct 22, 2024
994c2be
Update includes/troubleshooting/flutter/enable_multi_view.mdx
martinhaintz Oct 22, 2024
7f0e3fe
update to match the latest PR
mar-hai Oct 22, 2024
97c512c
revert changes to previous commit.
mar-hai Oct 28, 2024
06c2123
Merge branch 'master' into martinhaintz/fix-flutter-multiview
mar-hai Nov 5, 2024
05f8102
Merge branch 'master' into martinhaintz/fix-flutter-multiview
mar-hai Nov 11, 2024
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
22 changes: 22 additions & 0 deletions docs/platforms/flutter/troubleshooting.mdx
martinhaintz marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,25 @@ There is an [issue with the Sentry Flutter SDK](https://github.com/getsentry/sen
This is an [issue](https://github.com/flutter/flutter/issues/135245) with Flutter itself and should be fixed in [Flutter 3.15](https://github.com/flutter/flutter/labels/found%20in%20release%3A%203.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`.

## Using Flutter multi-view for Web
martinhaintz marked this conversation as resolved.
Show resolved Hide resolved

Multi-view embedding was introduced in Flutter 3.24. A short description of this feature can be found [here on Medium](https://medium.com/flutter/whats-new-in-flutter-3-24-6c040f87d1e4).
A more detailed guide about the usage is available in the [Flutter docs](https://docs.flutter.dev/platform-integration/web/embedding-flutter-web).
martinhaintz marked this conversation as resolved.
Show resolved Hide resolved

Using Sentry in a multi-view application is possible, but there are some limitations. To make the [sentry flutter example app](https://github.com/getsentry/sentry-dart/tree/main/flutter/example) `multi-view` ready, you will need to modify some parts.
martinhaintz marked this conversation as resolved.
Show resolved Hide resolved

The `SentryWidget` with the `SentryScreenshotWidget` and the `SentryUserInteractionWidget` do not currently support multiple instances, and cannot be used in a `multi-view` application.

Also note the `NavigatorKey` in the `MaterialApp` widget. This key is a single `GlobalKey` in the `example app` and therefore doesn't support multiple instances. If you rely on this `NavigatorKey` you will need to handle multiple `Keys` and pass them to the appropriate view/instance. For simplicity, we will not use the `NavigatorKey` in the following example.
martinhaintz marked this conversation as resolved.
Show resolved Hide resolved

### Example app as multi-view

Modify your `main.dart` and create the `multi_view_app.dart` file next to your `main.dart` and insert the content from the [docs](https://docs.flutter.dev/platform-integration/web/embedding-flutter-web#handling-view-changes-from-dart).
Modify the `index.html`in the `web` directory and add the `flutter_bootstrap.js`.

The last step is to add `--web-renderer auto` as additional run args.

Now you should be able to see 2 instances of the same application side by side, with different ViewIds in the `AppBar`.

<Include name="troubleshooting/flutter/enable_multi_view.mdx" />
216 changes: 216 additions & 0 deletions includes/troubleshooting/flutter/enable_multi_view.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
```dart {filename:multi_view_app.dart}{tabTitle: multi_view_app.dart}
// 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.

import 'dart:ui' show FlutterView;
import 'package:flutter/widgets.dart';

/// 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;

@override
State<MultiViewApp> createState() => _MultiViewAppState();
}

class _MultiViewAppState extends State<MultiViewApp>
with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_updateViews();
}

@override
void didUpdateWidget(MultiViewApp oldWidget) {
super.didUpdateWidget(oldWidget);
// Need to re-evaluate the viewBuilder callback for all views.
_views.clear();
_updateViews();
}

@override
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,
),
);
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

@override
Widget build(BuildContext context) {
return ViewCollection(views: _views.values.toList(growable: false));
}
}
```

```dart {filename:main.dart}{tabTitle: main.dart}
import 'multi_view_app.dart';
import 'package:sentry_flutter/src/integrations/widgets_binding_integration.dart';

...

Future<void> main() async {
await setupSentry(
() => runWidget(
MultiViewApp(
viewBuilder: (BuildContext context) => DefaultAssetBundle(
bundle: SentryAssetBundle(),
child: const MyApp(),
),
),
),
exampleDsn,
);
}

Future<void> setupSentry(
...
}) async {
await SentryFlutter.init(
(options) {
...
final integration = options.integrations
.firstWhere((element) => element is WidgetsBindingIntegration);
options.removeIntegration(integration);
},
// Init your App.
appRunner: appRunner,
);
}

class MyApp extends StatefulWidget {
const MyApp({super.key});

@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return feedback.BetterFeedback(
child: ChangeNotifierProvider<ThemeProvider>(
create: (_) => ThemeProvider(),
child: Builder(
builder: (context) => MaterialApp(
navigatorObservers: [
SentryNavigatorObserver(),
],
theme: Provider.of<ThemeProvider>(context).theme,
home: const MainScaffold(),
),
),
),
);
}
}

...


class MainScaffold extends StatelessWidget {
...

@override
Widget build(BuildContext context) {
...
return Scaffold(
appBar: AppBar(
title:
Text('Sentry Flutter Example (ViewId:${View.of(context).viewId})'),
actions: [
...
],
),
body: SingleChildScrollView(
...
),
);
}

...
}


```

```js {filename:flutter_bootstrap.js}{tabTitle: flutter_bootstrap.js}
// flutter_bootstrap.js
{{ flutter_js; }}
{{ flutter_build_config; }}

_flutter.loader.load({
onEntrypointLoaded: async function onEntrypointLoaded(engineInitializer) {
let engine = await engineInitializer.initializeEngine({
multiViewEnabled: true, // Enables embedded mode.
});
let app = await engine.runApp();
// Make this `app` object available to your JS app.
app.addView({ hostElement: document.querySelector("#left") });
app.addView({ hostElement: document.querySelector("#right") });
},
});
```

```html {filename:index.html}{tabTitle: index.html}
<!doctype html>
<html>
...
<body>
<div style="width: 100%; height: 100%; position: absolute;">
<div id="left" style="width: 50%; height: 100%; float: left;"></div>
<div id="right" style="width: 50%; height: 100%; float: left;"></div>
</div>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if ("serviceWorker" in navigator) {
window.addEventListener("load", function () {
navigator.serviceWorker.register("flutter_service_worker.js");
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
martinhaintz marked this conversation as resolved.
Show resolved Hide resolved
<script
src="flutter_bootstrap.js"
type="application/javascript"
async
></script>
</body>
</html>
```
Loading