From 1980492997e27108451090ac53732c8c4caa1f39 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Tue, 2 Nov 2021 19:13:02 +0000 Subject: [PATCH] [web] Allow the usage of url strategies without conditional imports (#77103) --- .../lib/flutter_web_plugins.dart | 1 + .../lib/src/navigation/js_url_strategy.dart | 4 +- .../lib/src/navigation/url_strategy.dart | 123 +---------------- .../src/navigation_common/url_strategy.dart | 130 ++++++++++++++++++ .../src/navigation_non_web/url_strategy.dart | 103 ++++++++++++++ .../flutter_web_plugins/lib/url_strategy.dart | 8 ++ .../test/navigation/url_strategy_test.dart | 2 - 7 files changed, 249 insertions(+), 122 deletions(-) create mode 100644 packages/flutter_web_plugins/lib/src/navigation_common/url_strategy.dart create mode 100644 packages/flutter_web_plugins/lib/src/navigation_non_web/url_strategy.dart create mode 100644 packages/flutter_web_plugins/lib/url_strategy.dart diff --git a/packages/flutter_web_plugins/lib/flutter_web_plugins.dart b/packages/flutter_web_plugins/lib/flutter_web_plugins.dart index 97a079d2bd..dda02e2404 100644 --- a/packages/flutter_web_plugins/lib/flutter_web_plugins.dart +++ b/packages/flutter_web_plugins/lib/flutter_web_plugins.dart @@ -18,5 +18,6 @@ library flutter_web_plugins; export 'src/navigation/js_url_strategy.dart'; export 'src/navigation/url_strategy.dart'; export 'src/navigation/utils.dart'; +export 'src/navigation_common/url_strategy.dart'; export 'src/plugin_event_channel.dart'; export 'src/plugin_registry.dart'; 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 30fba83602..5e68c310e8 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 @@ -16,7 +16,7 @@ import 'dart:ui' as ui; import 'package:js/js.dart'; import 'package:meta/meta.dart'; -import 'url_strategy.dart'; +import '../navigation_common/url_strategy.dart'; typedef _JsSetUrlStrategy = void Function(JsUrlStrategy?); @@ -33,7 +33,7 @@ typedef _PathGetter = String Function(); typedef _StateGetter = Object? Function(); -typedef _AddPopStateListener = ui.VoidCallback Function(html.EventListener); +typedef _AddPopStateListener = ui.VoidCallback Function(EventListener); typedef _StringToString = String Function(String); 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 27ee4874ba..dbcdd34d67 100644 --- a/packages/flutter_web_plugins/lib/src/navigation/url_strategy.dart +++ b/packages/flutter_web_plugins/lib/src/navigation/url_strategy.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:html' as html; import 'dart:ui' as ui; +import '../navigation_common/url_strategy.dart'; import 'js_url_strategy.dart'; import 'utils.dart'; @@ -40,53 +41,9 @@ void setUrlStrategy(UrlStrategy? strategy) { jsSetUrlStrategy(jsUrlStrategy); } -/// Represents and reads route state from the browser's URL. -/// -/// By default, the [HashUrlStrategy] subclass is used if the app doesn't -/// specify one. -abstract class UrlStrategy { - /// Abstract const constructor. This constructor enables subclasses to provide - /// const constructors so that they can be used in const expressions. - const UrlStrategy(); - - /// Adds a listener to the `popstate` event and returns a function that, when - /// invoked, removes the listener. - ui.VoidCallback addPopStateListener(html.EventListener fn); - - /// Returns the active path in the browser. - String getPath(); - - /// The state of the current browser history entry. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state - Object? getState(); - - /// Given a path that's internal to the app, create the external url that - /// will be used in the browser. - String prepareExternalUrl(String internalUrl); - - /// Push a new history entry. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState - void pushState(Object? state, String title, String url); - - /// Replace the currently active history entry. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState - void replaceState(Object? state, String title, String url); - - /// Moves forwards or backwards through the history stack. - /// - /// A negative [count] value causes a backward move in the history stack. And - /// a positive [count] value causes a forward move. - /// - /// Examples: - /// - /// * `go(-2)` moves back 2 steps in history. - /// * `go(3)` moves forward 3 steps in history. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/go - Future go(int count); +/// Use the [PathUrlStrategy] to handle the browser URL. +void usePathUrlStrategy() { + setUrlStrategy(PathUrlStrategy()); } /// Uses the browser URL's [hash fragments](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) @@ -114,7 +71,7 @@ class HashUrlStrategy extends UrlStrategy { final PlatformLocation _platformLocation; @override - ui.VoidCallback addPopStateListener(html.EventListener fn) { + ui.VoidCallback addPopStateListener(EventListener fn) { _platformLocation.addPopStateListener(fn); return () => _platformLocation.removePopStateListener(fn); } @@ -221,76 +178,6 @@ class PathUrlStrategy extends HashUrlStrategy { } } -/// Encapsulates all calls to DOM apis, which allows the [UrlStrategy] classes -/// to be platform agnostic and testable. -/// -/// For convenience, the [PlatformLocation] class can be used by implementations -/// of [UrlStrategy] to interact with DOM apis like pushState, popState, etc. -abstract class PlatformLocation { - /// Abstract const constructor. This constructor enables subclasses to provide - /// const constructors so that they can be used in const expressions. - const PlatformLocation(); - - /// Registers an event listener for the `popstate` event. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate - void addPopStateListener(html.EventListener fn); - - /// Unregisters the given listener (added by [addPopStateListener]) from the - /// `popstate` event. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate - void removePopStateListener(html.EventListener fn); - - /// The `pathname` part of the URL in the browser address bar. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname - String get pathname; - - /// The `query` part of the URL in the browser address bar. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/search - String get search; - - /// The `hash` part of the URL in the browser address bar. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/hash - String get hash; - - /// The `state` in the current history entry. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state - Object? get state; - - /// Adds a new entry to the browser history stack. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState - void pushState(Object? state, String title, String url); - - /// Replaces the current entry in the browser history stack. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState - void replaceState(Object? state, String title, String url); - - /// Moves forwards or backwards through the history stack. - /// - /// A negative [count] value causes a backward move in the history stack. And - /// a positive [count] value causes a forward move. - /// - /// Examples: - /// - /// * `go(-2)` moves back 2 steps in history. - /// * `go(3)` moves forward 3 steps in history. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/go - void go(int count); - - /// The base href where the Flutter app is being served. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base - String? getBaseHref(); -} - /// Delegates to real browser APIs to provide platform location functionality. class BrowserPlatformLocation extends PlatformLocation { /// Default constructor for [BrowserPlatformLocation]. diff --git a/packages/flutter_web_plugins/lib/src/navigation_common/url_strategy.dart b/packages/flutter_web_plugins/lib/src/navigation_common/url_strategy.dart new file mode 100644 index 0000000000..6e5364ab0d --- /dev/null +++ b/packages/flutter_web_plugins/lib/src/navigation_common/url_strategy.dart @@ -0,0 +1,130 @@ +// 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:async'; +import 'dart:ui' as ui; + +/// Signature of an html event listener. +/// +/// We have to redefine it because non-web platforms can't import dart:html. +typedef EventListener = dynamic Function(Object event); + +/// Represents and reads route state from the browser's URL. +/// +/// By default, the [HashUrlStrategy] subclass is used if the app doesn't +/// specify one. +abstract class UrlStrategy { + /// Abstract const constructor. This constructor enables subclasses to provide + /// const constructors so that they can be used in const expressions. + const UrlStrategy(); + + /// Adds a listener to the `popstate` event and returns a function that, when + /// invoked, removes the listener. + ui.VoidCallback addPopStateListener(EventListener fn); + + /// Returns the active path in the browser. + String getPath(); + + /// The state of the current browser history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state + Object? getState(); + + /// Given a path that's internal to the app, create the external url that + /// will be used in the browser. + String prepareExternalUrl(String internalUrl); + + /// Push a new history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState + void pushState(Object? state, String title, String url); + + /// Replace the currently active history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState + void replaceState(Object? state, String title, String url); + + /// Moves forwards or backwards through the history stack. + /// + /// A negative [count] value causes a backward move in the history stack. And + /// a positive [count] value causes a forward move. + /// + /// Examples: + /// + /// * `go(-2)` moves back 2 steps in history. + /// * `go(3)` moves forward 3 steps in history. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/go + Future go(int count); +} + +/// Encapsulates all calls to DOM apis, which allows the [UrlStrategy] classes +/// to be platform agnostic and testable. +/// +/// For convenience, the [PlatformLocation] class can be used by implementations +/// of [UrlStrategy] to interact with DOM apis like pushState, popState, etc. +abstract class PlatformLocation { + /// Abstract const constructor. This constructor enables subclasses to provide + /// const constructors so that they can be used in const expressions. + const PlatformLocation(); + + /// Registers an event listener for the `popstate` event. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate + void addPopStateListener(EventListener fn); + + /// Unregisters the given listener (added by [addPopStateListener]) from the + /// `popstate` event. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate + void removePopStateListener(EventListener fn); + + /// The `pathname` part of the URL in the browser address bar. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname + String get pathname; + + /// The `query` part of the URL in the browser address bar. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/search + String get search; + + /// The `hash` part of the URL in the browser address bar. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/hash + String get hash; + + /// The `state` in the current history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state + Object? get state; + + /// Adds a new entry to the browser history stack. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState + void pushState(Object? state, String title, String url); + + /// Replaces the current entry in the browser history stack. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState + void replaceState(Object? state, String title, String url); + + /// Moves forwards or backwards through the history stack. + /// + /// A negative [count] value causes a backward move in the history stack. And + /// a positive [count] value causes a forward move. + /// + /// Examples: + /// + /// * `go(-2)` moves back 2 steps in history. + /// * `go(3)` moves forward 3 steps in history. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/go + void go(int count); + + /// The base href where the Flutter app is being served. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base + String? getBaseHref(); +} diff --git a/packages/flutter_web_plugins/lib/src/navigation_non_web/url_strategy.dart b/packages/flutter_web_plugins/lib/src/navigation_non_web/url_strategy.dart new file mode 100644 index 0000000000..76b31baf68 --- /dev/null +++ b/packages/flutter_web_plugins/lib/src/navigation_non_web/url_strategy.dart @@ -0,0 +1,103 @@ +// 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:async'; +import 'dart:ui' as ui; + +import '../navigation_common/url_strategy.dart'; + +/// Returns the present [UrlStrategy] for handling the browser URL. +/// +/// In case null is returned, the browser integration has been manually +/// disabled by [setUrlStrategy]. +UrlStrategy? get urlStrategy => null; + +/// Change the strategy to use for handling browser URL. +/// +/// Setting this to null disables all integration with the browser history. +void setUrlStrategy(UrlStrategy? strategy) { + // No-op in non-web platforms. +} + +/// Use the [PathUrlStrategy] to handle the browser URL. +void usePathUrlStrategy() { + // No-op in non-web platforms. +} + +/// Uses the browser URL's [hash fragments](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) +/// to represent its state. +/// +/// By default, this class is used as the URL strategy for the app. However, +/// this class is still useful for apps that want to extend it. +/// +/// In order to use [HashUrlStrategy] for an app, it needs to be set like this: +/// +/// ```dart +/// import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +/// +/// // Somewhere before calling `runApp()` do: +/// setUrlStrategy(const HashUrlStrategy()); +/// ``` +class HashUrlStrategy extends UrlStrategy { + /// Creates an instance of [HashUrlStrategy]. + /// + /// The [PlatformLocation] parameter is useful for testing to mock out browser + /// interations. + const HashUrlStrategy([PlatformLocation? _]); + + @override + ui.VoidCallback addPopStateListener(EventListener fn) { + // No-op. + return () {}; + } + + @override + String getPath() => ''; + + @override + Object? getState() => null; + + @override + String prepareExternalUrl(String internalUrl) => ''; + + @override + void pushState(Object? state, String title, String url) { + // No-op. + } + + @override + void replaceState(Object? state, String title, String url) { + // No-op. + } + + @override + Future go(int count) async { + // No-op. + } +} + +/// Uses the browser URL's pathname to represent Flutter's route name. +/// +/// In order to use [PathUrlStrategy] for an app, it needs to be set like this: +/// +/// ```dart +/// import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +/// +/// // Somewhere before calling `runApp()` do: +/// setUrlStrategy(PathUrlStrategy()); +/// ``` +class PathUrlStrategy extends HashUrlStrategy { + /// Creates an instance of [PathUrlStrategy]. + /// + /// The [PlatformLocation] parameter is useful for testing to mock out browser + /// interations. + PathUrlStrategy([PlatformLocation? _platformLocation]) + : super(_platformLocation); + + @override + String getPath() => ''; + + @override + String prepareExternalUrl(String internalUrl) => ''; +} diff --git a/packages/flutter_web_plugins/lib/url_strategy.dart b/packages/flutter_web_plugins/lib/url_strategy.dart new file mode 100644 index 0000000000..c40b9a2baf --- /dev/null +++ b/packages/flutter_web_plugins/lib/url_strategy.dart @@ -0,0 +1,8 @@ +// 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. + +export 'src/navigation_common/url_strategy.dart'; + +export 'src/navigation_non_web/url_strategy.dart' + if (dart.library.html) 'src/navigation/url_strategy.dart'; 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 aa919d48cb..d5d1022a54 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. -import 'dart:html'; - @TestOn('chrome') // Uses web-only Flutter SDK import 'package:flutter_test/flutter_test.dart';