diff --git a/packages/flutter_web_plugins/lib/flutter_web_plugins.dart b/packages/flutter_web_plugins/lib/flutter_web_plugins.dart index 1bb8281b871..53a19bdbabd 100644 --- a/packages/flutter_web_plugins/lib/flutter_web_plugins.dart +++ b/packages/flutter_web_plugins/lib/flutter_web_plugins.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - /// The platform channels and plugin registry implementations for /// the web implementations of Flutter plugins. /// diff --git a/packages/flutter_web_plugins/lib/src/navigation/js_url_strategy.dart b/packages/flutter_web_plugins/lib/src/navigation/js_url_strategy.dart index 9ebaac560e7..86621eb3909 100644 --- a/packages/flutter_web_plugins/lib/src/navigation/js_url_strategy.dart +++ b/packages/flutter_web_plugins/lib/src/navigation/js_url_strategy.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - @JS() library js_location_strategy; @@ -16,7 +14,7 @@ import 'package:meta/meta.dart'; import 'url_strategy.dart'; -typedef _JsSetUrlStrategy = void Function(JsUrlStrategy); +typedef _JsSetUrlStrategy = void Function(JsUrlStrategy?); /// A JavaScript hook to customize the URL strategy of a Flutter app. // @@ -29,7 +27,7 @@ external _JsSetUrlStrategy get jsSetUrlStrategy; typedef _PathGetter = String Function(); -typedef _StateGetter = Object Function(); +typedef _StateGetter = Object? Function(); typedef _AddPopStateListener = ui.VoidCallback Function(html.EventListener); @@ -43,10 +41,6 @@ typedef _HistoryMove = Future Function(int count); /// Given a Dart implementation of URL strategy, converts it to a JavaScript /// URL strategy to be passed through JS interop. JsUrlStrategy convertToJsUrlStrategy(UrlStrategy strategy) { - if (strategy == null) { - return null; - } - return JsUrlStrategy( getPath: allowInterop(strategy.getPath), getState: allowInterop(strategy.getState), diff --git a/packages/flutter_web_plugins/lib/src/navigation/url_strategy.dart b/packages/flutter_web_plugins/lib/src/navigation/url_strategy.dart index 2470f6bf0e4..59360cb5956 100644 --- a/packages/flutter_web_plugins/lib/src/navigation/url_strategy.dart +++ b/packages/flutter_web_plugins/lib/src/navigation/url_strategy.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - import 'dart:async'; import 'dart:html' as html; import 'dart:ui' as ui; @@ -14,8 +12,12 @@ import 'utils.dart'; /// Change the strategy to use for handling browser URL. /// /// Setting this to null disables all integration with the browser history. -void setUrlStrategy(UrlStrategy strategy) { - jsSetUrlStrategy(convertToJsUrlStrategy(strategy)); +void setUrlStrategy(UrlStrategy? strategy) { + JsUrlStrategy? jsUrlStrategy; + if (strategy != null) { + jsUrlStrategy = convertToJsUrlStrategy(strategy); + } + jsSetUrlStrategy(jsUrlStrategy); } /// Represents and reads route state from the browser's URL. @@ -37,7 +39,7 @@ abstract class UrlStrategy { /// The state of the current browser history entry. /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state - Object getState(); + Object? getState(); /// Given a path that's internal to the app, create the external url that /// will be used in the browser. @@ -101,7 +103,7 @@ class HashUrlStrategy extends UrlStrategy { String getPath() { // the hash value is always prefixed with a `#` // and if it is empty then it will stay empty - final String path = _platformLocation.hash ?? ''; + final String path = _platformLocation.hash; assert(path.isEmpty || path.startsWith('#')); // We don't want to return an empty string as a path. Instead we default to "/". @@ -113,7 +115,7 @@ class HashUrlStrategy extends UrlStrategy { } @override - Object getState() => _platformLocation.state; + Object? getState() => _platformLocation.state; @override String prepareExternalUrl(String internalUrl) { @@ -148,7 +150,7 @@ class HashUrlStrategy extends UrlStrategy { /// `history.back` transition. Future _waitForPopState() { final Completer completer = Completer(); - ui.VoidCallback unsubscribe; + late ui.VoidCallback unsubscribe; unsubscribe = addPopStateListener((_) { unsubscribe(); completer.complete(); @@ -238,7 +240,7 @@ abstract class PlatformLocation { /// The `state` in the current history entry. /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state - Object get state; + Object? get state; /// Adds a new entry to the browser history stack. /// @@ -266,7 +268,7 @@ abstract class PlatformLocation { /// The base href where the Flutter app is being served. /// /// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base - String getBaseHref(); + String? getBaseHref(); } /// Delegates to real browser APIs to provide platform location functionality. @@ -274,6 +276,14 @@ class BrowserPlatformLocation extends PlatformLocation { /// Default constructor for [BrowserPlatformLocation]. const BrowserPlatformLocation(); + // Default value for [pathname] when it's not set in window.location. + // According to MDN this should be ''. Chrome seems to return '/'. + static const String _defaultPathname = ''; + + // Default value for [search] when it's not set in window.location. + // According to both chrome, and the MDN, this is ''. + static const String _defaultSearch = ''; + html.Location get _location => html.window.location; html.History get _history => html.window.history; @@ -288,16 +298,16 @@ class BrowserPlatformLocation extends PlatformLocation { } @override - String get pathname => _location.pathname; + String get pathname => _location.pathname ?? _defaultPathname; @override - String get search => _location.search; + String get search => _location.search ?? _defaultSearch; @override String get hash => _location.hash; @override - Object get state => _history.state; + Object? get state => _history.state; @override void pushState(Object state, String title, String url) { @@ -315,6 +325,5 @@ class BrowserPlatformLocation extends PlatformLocation { } @override - String getBaseHref() => getBaseElementHrefFromDom(); - // String getBaseHref() => html.document.baseUri; + String? getBaseHref() => getBaseElementHrefFromDom(); } diff --git a/packages/flutter_web_plugins/lib/src/navigation/utils.dart b/packages/flutter_web_plugins/lib/src/navigation/utils.dart index ea742aa8e7d..a1250cdd25a 100644 --- a/packages/flutter_web_plugins/lib/src/navigation/utils.dart +++ b/packages/flutter_web_plugins/lib/src/navigation/utils.dart @@ -2,43 +2,33 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - import 'dart:html'; -AnchorElement _urlParsingNode; +// TODO(mdebbar): Use the `URI` class instead? +final AnchorElement _urlParsingNode = AnchorElement(); /// Extracts the pathname part of a full [url]. /// /// Example: for the url `http://example.com/foo`, the extracted pathname will /// be `/foo`. String extractPathname(String url) { - // TODO(mdebbar): Use the `URI` class instead? - _urlParsingNode ??= AnchorElement(); _urlParsingNode.href = url; - final String pathname = _urlParsingNode.pathname; + final String pathname = _urlParsingNode.pathname ?? ''; return (pathname.isEmpty || pathname[0] == '/') ? pathname : '/$pathname'; } -Element _baseElement; +// The element in the document. +final Element? _baseElement = document.querySelector('base'); -/// Finds the element in the document and returns its `href` attribute. +/// Returns the `href` attribute of the element in the document. /// /// Returns null if the element isn't found. -String getBaseElementHrefFromDom() { - if (_baseElement == null) { - _baseElement = document.querySelector('base'); - if (_baseElement == null) { - return null; - } - } - return _baseElement.getAttribute('href'); -} +String? getBaseElementHrefFromDom() => _baseElement?.getAttribute('href'); /// Checks that [baseHref] is set. /// /// Throws an exception otherwise. -String checkBaseHref(String baseHref) { +String checkBaseHref(String? baseHref) { if (baseHref == null) { throw Exception('Please add a element to your index.html'); } diff --git a/packages/flutter_web_plugins/lib/src/plugin_event_channel.dart b/packages/flutter_web_plugins/lib/src/plugin_event_channel.dart index eea612e1d73..d84f36190b6 100644 --- a/packages/flutter_web_plugins/lib/src/plugin_event_channel.dart +++ b/packages/flutter_web_plugins/lib/src/plugin_event_channel.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - import 'dart:async'; import 'package:flutter/services.dart'; @@ -28,14 +26,14 @@ import 'plugin_registry.dart'; /// /// The first method is `listen`. When called, it begins forwarding /// messages to the framework side when they are added to the -/// [controller]. This triggers the [StreamController.onListen] callback -/// on the [controller]. +/// `controller`. This triggers the [StreamController.onListen] callback +/// on the `controller`. /// /// The other method is `cancel`. When called, it stops forwarding /// events to the framework. This triggers the [StreamController.onCancel] -/// callback on the [controller]. +/// callback on the `controller`. /// -/// Events added to the [controller] when the framework is not +/// Events added to the `controller` when the framework is not /// subscribed are silently discarded. class PluginEventChannel { /// Creates a new plugin event channel. @@ -45,8 +43,8 @@ class PluginEventChannel { this.name, [ this.codec = const StandardMethodCodec(), this.binaryMessenger, - ]) : assert(name != null), - assert(codec != null); + ]) : assert(name != null), // ignore: unnecessary_null_comparison + assert(codec != null); // ignore: unnecessary_null_comparison /// The logical channel on which communication happens. /// @@ -63,7 +61,7 @@ class PluginEventChannel { /// When this is null, the [pluginBinaryMessenger] is used instead, /// which sends messages from the platform-side to the /// framework-side. - final BinaryMessenger binaryMessenger; + final BinaryMessenger? binaryMessenger; /// Use [setController] instead. /// @@ -81,7 +79,7 @@ class PluginEventChannel { /// /// Setting the controller to null disconnects from the channel (setting /// the message handler on the [binaryMessenger] to null). - void setController(StreamController controller) { + void setController(StreamController? controller) { final BinaryMessenger messenger = binaryMessenger ?? pluginBinaryMessenger; if (controller == null) { messenger.setMessageHandler(name, null); @@ -105,16 +103,21 @@ class PluginEventChannel { } class _EventChannelHandler { - _EventChannelHandler(this.name, this.codec, this.controller, this.messenger) : assert(messenger != null); + _EventChannelHandler( + this.name, + this.codec, + this.controller, + this.messenger, + ) : assert(messenger != null); // ignore: unnecessary_null_comparison final String name; final MethodCodec codec; final StreamController controller; final BinaryMessenger messenger; - StreamSubscription subscription; + StreamSubscription? subscription; - Future handle(ByteData message) { + Future? handle(ByteData? message) { final MethodCall call = codec.decodeMethodCall(message); switch (call.method) { case 'listen': @@ -128,9 +131,8 @@ class _EventChannelHandler { } Future _listen() async { - if (subscription != null) { - await subscription.cancel(); - } + // Cancel any existing subscription. + await subscription?.cancel(); subscription = controller.stream.listen((dynamic event) { messenger.send(name, codec.encodeSuccessEnvelope(event)); }, onError: (dynamic error) { @@ -146,7 +148,7 @@ class _EventChannelHandler { message: 'No active subscription to cancel.', ); } - await subscription.cancel(); + await subscription!.cancel(); subscription = null; return codec.encodeSuccessEnvelope(null); } diff --git a/packages/flutter_web_plugins/lib/src/plugin_registry.dart b/packages/flutter_web_plugins/lib/src/plugin_registry.dart index 9563ea97cd9..f9a2fff6a4f 100644 --- a/packages/flutter_web_plugins/lib/src/plugin_registry.dart +++ b/packages/flutter_web_plugins/lib/src/plugin_registry.dart @@ -2,15 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - import 'dart:async'; import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -typedef _MessageHandler = Future Function(ByteData); +// TODO(hterkelsen): Why is this _MessageHandler duplicated here? +typedef _MessageHandler = Future? Function(ByteData?); /// This class registers web platform plugins. /// @@ -97,12 +96,12 @@ class _PlatformBinaryMessenger extends BinaryMessenger { @override Future handlePlatformMessage( String channel, - ByteData data, - ui.PlatformMessageResponseCallback callback, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, ) async { - ByteData response; + ByteData? response; try { - final MessageHandler handler = _handlers[channel]; + final MessageHandler? handler = _handlers[channel]; if (handler != null) { response = await handler(data); } @@ -122,9 +121,9 @@ class _PlatformBinaryMessenger extends BinaryMessenger { /// Sends a platform message from the platform side back to the framework. @override - Future send(String channel, ByteData message) { + Future send(String channel, ByteData? message) { final Completer completer = Completer(); - ui.window.onPlatformMessage(channel, message, (ByteData reply) { + ui.window.onPlatformMessage!(channel, message, (ByteData? reply) { try { completer.complete(reply); } catch (exception, stack) { @@ -140,7 +139,7 @@ class _PlatformBinaryMessenger extends BinaryMessenger { } @override - void setMessageHandler(String channel, MessageHandler handler) { + void setMessageHandler(String channel, MessageHandler? handler) { if (handler == null) _handlers.remove(channel); else @@ -148,12 +147,12 @@ class _PlatformBinaryMessenger extends BinaryMessenger { } @override - bool checkMessageHandler(String channel, MessageHandler handler) => _handlers[channel] == handler; + bool checkMessageHandler(String channel, MessageHandler? handler) => _handlers[channel] == handler; @override void setMockMessageHandler( String channel, - Future Function(ByteData message) handler, + MessageHandler? handler, ) { throw FlutterError( 'Setting mock handlers is not supported on the platform side.', @@ -161,7 +160,7 @@ class _PlatformBinaryMessenger extends BinaryMessenger { } @override - bool checkMockMessageHandler(String channel, MessageHandler handler) { + bool checkMockMessageHandler(String channel, MessageHandler? handler) { throw FlutterError( 'Setting mock handlers is not supported on the platform side.', ); diff --git a/packages/flutter_web_plugins/pubspec.yaml b/packages/flutter_web_plugins/pubspec.yaml index b4550801199..e0f19e5dc60 100644 --- a/packages/flutter_web_plugins/pubspec.yaml +++ b/packages/flutter_web_plugins/pubspec.yaml @@ -4,7 +4,7 @@ author: Flutter Authors homepage: http://flutter.dev environment: - sdk: ">=2.0.0-dev.28.0 <3.0.0" + sdk: ">=2.12.0-0 <3.0.0" dependencies: flutter: diff --git a/packages/flutter_web_plugins/test/navigation/url_strategy_test.dart b/packages/flutter_web_plugins/test/navigation/url_strategy_test.dart index 156a631aa40..b3f9de79cd9 100644 --- a/packages/flutter_web_plugins/test/navigation/url_strategy_test.dart +++ b/packages/flutter_web_plugins/test/navigation/url_strategy_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - import 'dart:html'; @TestOn('chrome') // Uses web-only Flutter SDK @@ -13,16 +11,12 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart'; void main() { group('$HashUrlStrategy', () { - TestPlatformLocation location; + late TestPlatformLocation location; setUp(() { location = TestPlatformLocation(); }); - tearDown(() { - location = null; - }); - test('leading slash is optional', () { final HashUrlStrategy strategy = HashUrlStrategy(location); @@ -48,16 +42,12 @@ void main() { }); group('$PathUrlStrategy', () { - TestPlatformLocation location; + late TestPlatformLocation location; setUp(() { location = TestPlatformLocation(); }); - tearDown(() { - location = null; - }); - test('validates base href', () { location.baseHref = '/'; expect( @@ -153,7 +143,7 @@ class TestPlatformLocation extends PlatformLocation { String hash = ''; @override - dynamic state; + Object? Function() state = () => null; /// Mocks the base href of the document. String baseHref = ''; diff --git a/packages/flutter_web_plugins/test/navigation/utils_test.dart b/packages/flutter_web_plugins/test/navigation/utils_test.dart index 419b49f7f64..fa834809947 100644 --- a/packages/flutter_web_plugins/test/navigation/utils_test.dart +++ b/packages/flutter_web_plugins/test/navigation/utils_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - @TestOn('browser') // Uses web-only Flutter SDK import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/flutter_web_plugins/test/plugin_event_channel_test.dart b/packages/flutter_web_plugins/test/plugin_event_channel_test.dart index e7a1a8502e7..fcac25a290b 100644 --- a/packages/flutter_web_plugins/test/plugin_event_channel_test.dart +++ b/packages/flutter_web_plugins/test/plugin_event_channel_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - @TestOn('chrome') // Uses web-only Flutter SDK import 'dart:async'; @@ -29,7 +27,7 @@ void main() { PluginEventChannel('test'); final StreamController controller = StreamController(); - sendingChannel.controller = controller; + sendingChannel.setController(controller); expect(listeningChannel.receiveBroadcastStream(), emitsInOrder(['hello', 'world'])); @@ -61,7 +59,7 @@ void main() { PluginEventChannel('test2'); final StreamController controller = StreamController(); - sendingChannel.controller = controller; + sendingChannel.setController(controller); expect( listeningChannel.receiveBroadcastStream(), @@ -96,7 +94,7 @@ void main() { final StreamController controller = StreamController( onListen: expectAsync0(() {}, count: 1)); - sendingChannel.controller = controller; + sendingChannel.setController(controller); expect(listeningChannel.receiveBroadcastStream(), emitsInOrder(['hello'])); @@ -128,11 +126,11 @@ void main() { final StreamController controller = StreamController(onCancel: expectAsync0(() {})); - sendingChannel.controller = controller; + sendingChannel.setController(controller); final Stream eventStream = listeningChannel.receiveBroadcastStream(); - StreamSubscription subscription; + late StreamSubscription subscription; subscription = eventStream.listen(expectAsync1((dynamic x) { expect(x, equals('hello')); @@ -153,7 +151,7 @@ void main() { final Stream eventStream = listeningChannel.receiveBroadcastStream(); - StreamSubscription subscription; + late StreamSubscription subscription; subscription = eventStream.listen(expectAsync1((dynamic x) { expect(x, equals('hello')); diff --git a/packages/flutter_web_plugins/test/plugin_registry_test.dart b/packages/flutter_web_plugins/test/plugin_registry_test.dart index 64464a5e3f5..79571000fc0 100644 --- a/packages/flutter_web_plugins/test/plugin_registry_test.dart +++ b/packages/flutter_web_plugins/test/plugin_registry_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - @TestOn('chrome') // Uses web-only Flutter SDK import 'dart:ui' as ui; // ignore: unused_import, it looks unused as web-only elements are the only elements used. @@ -56,8 +54,8 @@ void main() { const StandardMessageCodec codec = StandardMessageCodec(); final List loggedMessages = []; - ServicesBinding.instance.defaultBinaryMessenger - .setMessageHandler('test_send', (ByteData data) { + ServicesBinding.instance!.defaultBinaryMessenger + .setMessageHandler('test_send', (ByteData? data) { loggedMessages.add(codec.decodeMessage(data) as String); return null; }); @@ -70,14 +68,14 @@ void main() { 'test_send', codec.encodeMessage('world')); expect(loggedMessages, equals(['hello', 'world'])); - ServicesBinding.instance.defaultBinaryMessenger + ServicesBinding.instance!.defaultBinaryMessenger .setMessageHandler('test_send', null); }); test('throws when trying to set a mock handler', () { expect( () => pluginBinaryMessenger.setMockMessageHandler( - 'test', (ByteData data) async => ByteData(0)), + 'test', (ByteData? data) async => ByteData(0)), throwsFlutterError); }); });