Skip to content

feat: add preliminary web support#1

Open
Frank3K wants to merge 12 commits intonextfrom
feat/web-support
Open

feat: add preliminary web support#1
Frank3K wants to merge 12 commits intonextfrom
feat/web-support

Conversation

@Frank3K
Copy link

@Frank3K Frank3K commented Feb 25, 2026

Goal

This pull request refactors the platform-specific implementation of the bugsnag_flutter client to use a more modular and extensible approach, enabling preliminary support for web.

Note that our goal is to add MVP support for Flutter on web. Only the methods and properties that are actually used in our applications are supported; other methods throw UnimplementedErrors.

Installation

In order to add support for bugsnag on Flutter web, the following script needs to be added to the index.html of the application (taken from the docs).

<script src="//d2wy8f7a9ursnm.cloudfront.net/v8/bugsnag.min.js"></script>

Design

The core change splits the monolithic client.dart into a platform-conditional architecture using Dart's conditional
imports
:

// client.dart
import 'client_stub.dart'
    if (dart.library.io) 'client_io.dart'
    if (dart.library.js_interop) 'client_web.dart';

At compile time, Dart selects the correct implementation file based on the target platform's available libraries. Each file exports the same two top-level factory functions — platformStart() and platformAttach() — which the Bugsnag facade calls to obtain a platform-specific BugsnagClient.

  graph TD
      subgraph "Public API"
          C["BugsnagClient<br/><i>abstract class</i>"]
      end

      subgraph "Platform resolution"
          C -->|"dart.library.io"| D["client_io.dart<br/><b>ChannelClient</b>"]
          C -->|"dart.library.js_interop"| E["client_web.dart<br/><b>WebClient</b>"]
          C -->|"fallback"| F["client_stub.dart<br/><i>throws UnsupportedError</i>"]
      end

      subgraph "Native layer"
          D -->|"MethodChannel"| G["Android/iOS<br/>Bugsnag SDK"]
          E -->|"dart:js_interop"| H["@bugsnag/browser<br/>JS library"]
      end
Loading

File responsibilities

  • client.dart — Abstract BugsnagClient interface, DelegateClient mixin, Bugsnag facade. Fully platform-agnostic — no dart:io or dart:js_interop imports.
  • client_io.dart — ChannelClient — existing Android/iOS implementation. Communicates with native SDKs via Flutter MethodChannel. Selected when dart.library.io is available.
  • client_web.dart — WebClient — new web implementation. Calls @bugsnag/browser via dart:js_interop bindings (@JS() annotations). Selected when dart.library.js_interop is available.
  • client_stub.dart — Default fallback. Both platformStart() and platformAttach() throw UnsupportedError.

How delegation works

The Bugsnag singleton uses the DelegateClient mixin, which holds a nullable _client reference. When bugsnag.start() or bugsnag.attach() is called, it invokes the conditionally-imported platformStart()/platformAttach() function, which returns a ChannelClient (IO) or WebClient (web). All subsequent API calls on bugsnag are forwarded to this inner client.

Web implementation details

WebClient uses dart:js_interop to call the @bugsnag/browser JS library that must already be loaded in the host page. Currently implemented: start, notify, setUser, resumeSession, and automatic unhandled error capture (via FlutterError.onError and PlatformDispatcher.instance.onError). Remaining API methods throw UnimplementedError and can be added incrementally.

Changeset

  • Min dart SDK has been raised from 3.0.0 to 3.3.0.

Testing

Frank3K and others added 12 commits February 24, 2026 13:19
Replace `dart:io` Platform checks with `package:flutter/foundation.dart`
defaultTargetPlatform in model files, making them compatible with web.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract ChannelClient into client_io.dart and use conditional imports
to select the platform client at compile time. Add client_web.dart
with stub implementations that print method names for start(), setUser(),
and notify(). Register the web platform in pubspec.yaml with a no-op
plugin entry point.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Which makes it easier to find it amongst other logs.
Call Bugsnag.start() via dart:js_interop to initialize the browser
SDK. Pass through apiKey, appVersion, releaseStage,
enabledReleaseStages, and collectUserIp to the JS config. Hook up
Flutter error handlers when autoDetectErrors is enabled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Called automatically on bootstrap when autoTrackSessions is enabled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sends errors to bugsnag-js with Dart error metadata attached.
Constructs a BugsnagEvent for Dart-side onError callbacks before
delegating to the JS SDK. Also fixes BugsnagError.toJson() to
properly serialize stackframes into plain maps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Aligns with ChannelClient pattern where autoDetectErrors is handled
in the constructor rather than platformStart. Removes unused
_autoDetectErrors field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix _notifyUnhandled to correctly mark errors as unhandled
- Fix _bugsnagNotify to use proper JS onError callback instead of options object
- Add _BugsnagJsEvent extension type for bugsnag-js event interop
- Bump minimum Dart SDK to >=3.3.0 (required for extension types)
- Remove unused web dependency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant