Skip to content

Commit e148bf8

Browse files
authored
[flutter_web_plugins] Migrate to null safety. (flutter#69844)
1 parent 2b51278 commit e148bf8

File tree

11 files changed

+79
-103
lines changed

11 files changed

+79
-103
lines changed

packages/flutter_web_plugins/lib/flutter_web_plugins.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// @dart = 2.8
6-
75
/// The platform channels and plugin registry implementations for
86
/// the web implementations of Flutter plugins.
97
///

packages/flutter_web_plugins/lib/src/navigation/js_url_strategy.dart

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// @dart = 2.8
6-
75
@JS()
86
library js_location_strategy;
97

@@ -16,7 +14,7 @@ import 'package:meta/meta.dart';
1614

1715
import 'url_strategy.dart';
1816

19-
typedef _JsSetUrlStrategy = void Function(JsUrlStrategy);
17+
typedef _JsSetUrlStrategy = void Function(JsUrlStrategy?);
2018

2119
/// A JavaScript hook to customize the URL strategy of a Flutter app.
2220
//
@@ -29,7 +27,7 @@ external _JsSetUrlStrategy get jsSetUrlStrategy;
2927

3028
typedef _PathGetter = String Function();
3129

32-
typedef _StateGetter = Object Function();
30+
typedef _StateGetter = Object? Function();
3331

3432
typedef _AddPopStateListener = ui.VoidCallback Function(html.EventListener);
3533

@@ -43,10 +41,6 @@ typedef _HistoryMove = Future<void> Function(int count);
4341
/// Given a Dart implementation of URL strategy, converts it to a JavaScript
4442
/// URL strategy to be passed through JS interop.
4543
JsUrlStrategy convertToJsUrlStrategy(UrlStrategy strategy) {
46-
if (strategy == null) {
47-
return null;
48-
}
49-
5044
return JsUrlStrategy(
5145
getPath: allowInterop(strategy.getPath),
5246
getState: allowInterop(strategy.getState),

packages/flutter_web_plugins/lib/src/navigation/url_strategy.dart

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// @dart = 2.8
6-
75
import 'dart:async';
86
import 'dart:html' as html;
97
import 'dart:ui' as ui;
@@ -14,8 +12,12 @@ import 'utils.dart';
1412
/// Change the strategy to use for handling browser URL.
1513
///
1614
/// Setting this to null disables all integration with the browser history.
17-
void setUrlStrategy(UrlStrategy strategy) {
18-
jsSetUrlStrategy(convertToJsUrlStrategy(strategy));
15+
void setUrlStrategy(UrlStrategy? strategy) {
16+
JsUrlStrategy? jsUrlStrategy;
17+
if (strategy != null) {
18+
jsUrlStrategy = convertToJsUrlStrategy(strategy);
19+
}
20+
jsSetUrlStrategy(jsUrlStrategy);
1921
}
2022

2123
/// Represents and reads route state from the browser's URL.
@@ -37,7 +39,7 @@ abstract class UrlStrategy {
3739
/// The state of the current browser history entry.
3840
///
3941
/// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state
40-
Object getState();
42+
Object? getState();
4143

4244
/// Given a path that's internal to the app, create the external url that
4345
/// will be used in the browser.
@@ -101,7 +103,7 @@ class HashUrlStrategy extends UrlStrategy {
101103
String getPath() {
102104
// the hash value is always prefixed with a `#`
103105
// and if it is empty then it will stay empty
104-
final String path = _platformLocation.hash ?? '';
106+
final String path = _platformLocation.hash;
105107
assert(path.isEmpty || path.startsWith('#'));
106108

107109
// We don't want to return an empty string as a path. Instead we default to "/".
@@ -113,7 +115,7 @@ class HashUrlStrategy extends UrlStrategy {
113115
}
114116

115117
@override
116-
Object getState() => _platformLocation.state;
118+
Object? getState() => _platformLocation.state;
117119

118120
@override
119121
String prepareExternalUrl(String internalUrl) {
@@ -148,7 +150,7 @@ class HashUrlStrategy extends UrlStrategy {
148150
/// `history.back` transition.
149151
Future<void> _waitForPopState() {
150152
final Completer<void> completer = Completer<void>();
151-
ui.VoidCallback unsubscribe;
153+
late ui.VoidCallback unsubscribe;
152154
unsubscribe = addPopStateListener((_) {
153155
unsubscribe();
154156
completer.complete();
@@ -238,7 +240,7 @@ abstract class PlatformLocation {
238240
/// The `state` in the current history entry.
239241
///
240242
/// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state
241-
Object get state;
243+
Object? get state;
242244

243245
/// Adds a new entry to the browser history stack.
244246
///
@@ -266,14 +268,22 @@ abstract class PlatformLocation {
266268
/// The base href where the Flutter app is being served.
267269
///
268270
/// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
269-
String getBaseHref();
271+
String? getBaseHref();
270272
}
271273

272274
/// Delegates to real browser APIs to provide platform location functionality.
273275
class BrowserPlatformLocation extends PlatformLocation {
274276
/// Default constructor for [BrowserPlatformLocation].
275277
const BrowserPlatformLocation();
276278

279+
// Default value for [pathname] when it's not set in window.location.
280+
// According to MDN this should be ''. Chrome seems to return '/'.
281+
static const String _defaultPathname = '';
282+
283+
// Default value for [search] when it's not set in window.location.
284+
// According to both chrome, and the MDN, this is ''.
285+
static const String _defaultSearch = '';
286+
277287
html.Location get _location => html.window.location;
278288
html.History get _history => html.window.history;
279289

@@ -288,16 +298,16 @@ class BrowserPlatformLocation extends PlatformLocation {
288298
}
289299

290300
@override
291-
String get pathname => _location.pathname;
301+
String get pathname => _location.pathname ?? _defaultPathname;
292302

293303
@override
294-
String get search => _location.search;
304+
String get search => _location.search ?? _defaultSearch;
295305

296306
@override
297307
String get hash => _location.hash;
298308

299309
@override
300-
Object get state => _history.state;
310+
Object? get state => _history.state;
301311

302312
@override
303313
void pushState(Object state, String title, String url) {
@@ -315,6 +325,5 @@ class BrowserPlatformLocation extends PlatformLocation {
315325
}
316326

317327
@override
318-
String getBaseHref() => getBaseElementHrefFromDom();
319-
// String getBaseHref() => html.document.baseUri;
328+
String? getBaseHref() => getBaseElementHrefFromDom();
320329
}

packages/flutter_web_plugins/lib/src/navigation/utils.dart

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,33 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// @dart = 2.8
6-
75
import 'dart:html';
86

9-
AnchorElement _urlParsingNode;
7+
// TODO(mdebbar): Use the `URI` class instead?
8+
final AnchorElement _urlParsingNode = AnchorElement();
109

1110
/// Extracts the pathname part of a full [url].
1211
///
1312
/// Example: for the url `http://example.com/foo`, the extracted pathname will
1413
/// be `/foo`.
1514
String extractPathname(String url) {
16-
// TODO(mdebbar): Use the `URI` class instead?
17-
_urlParsingNode ??= AnchorElement();
1815
_urlParsingNode.href = url;
19-
final String pathname = _urlParsingNode.pathname;
16+
final String pathname = _urlParsingNode.pathname ?? '';
2017
return (pathname.isEmpty || pathname[0] == '/') ? pathname : '/$pathname';
2118
}
2219

23-
Element _baseElement;
20+
// The <base> element in the document.
21+
final Element? _baseElement = document.querySelector('base');
2422

25-
/// Finds the <base> element in the document and returns its `href` attribute.
23+
/// Returns the `href` attribute of the <base> element in the document.
2624
///
2725
/// Returns null if the element isn't found.
28-
String getBaseElementHrefFromDom() {
29-
if (_baseElement == null) {
30-
_baseElement = document.querySelector('base');
31-
if (_baseElement == null) {
32-
return null;
33-
}
34-
}
35-
return _baseElement.getAttribute('href');
36-
}
26+
String? getBaseElementHrefFromDom() => _baseElement?.getAttribute('href');
3727

3828
/// Checks that [baseHref] is set.
3929
///
4030
/// Throws an exception otherwise.
41-
String checkBaseHref(String baseHref) {
31+
String checkBaseHref(String? baseHref) {
4232
if (baseHref == null) {
4333
throw Exception('Please add a <base> element to your index.html');
4434
}

packages/flutter_web_plugins/lib/src/plugin_event_channel.dart

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// @dart = 2.8
6-
75
import 'dart:async';
86

97
import 'package:flutter/services.dart';
@@ -28,14 +26,14 @@ import 'plugin_registry.dart';
2826
///
2927
/// The first method is `listen`. When called, it begins forwarding
3028
/// messages to the framework side when they are added to the
31-
/// [controller]. This triggers the [StreamController.onListen] callback
32-
/// on the [controller].
29+
/// `controller`. This triggers the [StreamController.onListen] callback
30+
/// on the `controller`.
3331
///
3432
/// The other method is `cancel`. When called, it stops forwarding
3533
/// events to the framework. This triggers the [StreamController.onCancel]
36-
/// callback on the [controller].
34+
/// callback on the `controller`.
3735
///
38-
/// Events added to the [controller] when the framework is not
36+
/// Events added to the `controller` when the framework is not
3937
/// subscribed are silently discarded.
4038
class PluginEventChannel<T> {
4139
/// Creates a new plugin event channel.
@@ -45,8 +43,8 @@ class PluginEventChannel<T> {
4543
this.name, [
4644
this.codec = const StandardMethodCodec(),
4745
this.binaryMessenger,
48-
]) : assert(name != null),
49-
assert(codec != null);
46+
]) : assert(name != null), // ignore: unnecessary_null_comparison
47+
assert(codec != null); // ignore: unnecessary_null_comparison
5048

5149
/// The logical channel on which communication happens.
5250
///
@@ -63,7 +61,7 @@ class PluginEventChannel<T> {
6361
/// When this is null, the [pluginBinaryMessenger] is used instead,
6462
/// which sends messages from the platform-side to the
6563
/// framework-side.
66-
final BinaryMessenger binaryMessenger;
64+
final BinaryMessenger? binaryMessenger;
6765

6866
/// Use [setController] instead.
6967
///
@@ -81,7 +79,7 @@ class PluginEventChannel<T> {
8179
///
8280
/// Setting the controller to null disconnects from the channel (setting
8381
/// the message handler on the [binaryMessenger] to null).
84-
void setController(StreamController<T> controller) {
82+
void setController(StreamController<T>? controller) {
8583
final BinaryMessenger messenger = binaryMessenger ?? pluginBinaryMessenger;
8684
if (controller == null) {
8785
messenger.setMessageHandler(name, null);
@@ -105,16 +103,21 @@ class PluginEventChannel<T> {
105103
}
106104

107105
class _EventChannelHandler<T> {
108-
_EventChannelHandler(this.name, this.codec, this.controller, this.messenger) : assert(messenger != null);
106+
_EventChannelHandler(
107+
this.name,
108+
this.codec,
109+
this.controller,
110+
this.messenger,
111+
) : assert(messenger != null); // ignore: unnecessary_null_comparison
109112

110113
final String name;
111114
final MethodCodec codec;
112115
final StreamController<T> controller;
113116
final BinaryMessenger messenger;
114117

115-
StreamSubscription<T> subscription;
118+
StreamSubscription<T>? subscription;
116119

117-
Future<ByteData> handle(ByteData message) {
120+
Future<ByteData>? handle(ByteData? message) {
118121
final MethodCall call = codec.decodeMethodCall(message);
119122
switch (call.method) {
120123
case 'listen':
@@ -128,9 +131,8 @@ class _EventChannelHandler<T> {
128131
}
129132

130133
Future<ByteData> _listen() async {
131-
if (subscription != null) {
132-
await subscription.cancel();
133-
}
134+
// Cancel any existing subscription.
135+
await subscription?.cancel();
134136
subscription = controller.stream.listen((dynamic event) {
135137
messenger.send(name, codec.encodeSuccessEnvelope(event));
136138
}, onError: (dynamic error) {
@@ -146,7 +148,7 @@ class _EventChannelHandler<T> {
146148
message: 'No active subscription to cancel.',
147149
);
148150
}
149-
await subscription.cancel();
151+
await subscription!.cancel();
150152
subscription = null;
151153
return codec.encodeSuccessEnvelope(null);
152154
}

0 commit comments

Comments
 (0)