Skip to content

Commit c65c6d1

Browse files
dnfieldgspencergoog
authored andcommitted
Revert "[web] Support custom url strategies (flutter#19134)" (flutter#21687)
This reverts commit 0232499.
1 parent a855026 commit c65c6d1

File tree

12 files changed

+533
-740
lines changed

12 files changed

+533
-740
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/alarm_clock.dart
426426
FILE: ../../../flutter/lib/web_ui/lib/src/engine/assets.dart
427427
FILE: ../../../flutter/lib/web_ui/lib/src/engine/bitmap_canvas.dart
428428
FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_detection.dart
429+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_location.dart
429430
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvas_pool.dart
430431
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart
431432
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
@@ -462,9 +463,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/dom_canvas.dart
462463
FILE: ../../../flutter/lib/web_ui/lib/src/engine/dom_renderer.dart
463464
FILE: ../../../flutter/lib/web_ui/lib/src/engine/engine_canvas.dart
464465
FILE: ../../../flutter/lib/web_ui/lib/src/engine/frame_reference.dart
465-
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history/history.dart
466-
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history/js_url_strategy.dart
467-
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history/url_strategy.dart
466+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history.dart
468467
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/backdrop_filter.dart
469468
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/canvas.dart
470469
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/clip.dart

lib/web_ui/lib/src/engine.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ part 'engine/alarm_clock.dart';
2626
part 'engine/assets.dart';
2727
part 'engine/bitmap_canvas.dart';
2828
part 'engine/browser_detection.dart';
29+
part 'engine/browser_location.dart';
2930
part 'engine/canvaskit/canvas.dart';
3031
part 'engine/canvaskit/canvaskit_canvas.dart';
3132
part 'engine/canvaskit/canvaskit_api.dart';
@@ -62,9 +63,7 @@ part 'engine/dom_canvas.dart';
6263
part 'engine/dom_renderer.dart';
6364
part 'engine/engine_canvas.dart';
6465
part 'engine/frame_reference.dart';
65-
part 'engine/navigation/history.dart';
66-
part 'engine/navigation/js_url_strategy.dart';
67-
part 'engine/navigation/url_strategy.dart';
66+
part 'engine/history.dart';
6867
part 'engine/html/backdrop_filter.dart';
6968
part 'engine/html/canvas.dart';
7069
part 'engine/html/clip.dart';
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// @dart = 2.10
6+
part of engine;
7+
8+
// TODO(mdebbar): add other strategies.
9+
10+
// Some parts of this file were inspired/copied from the AngularDart router.
11+
12+
/// [LocationStrategy] is responsible for representing and reading route state
13+
/// from the browser's URL.
14+
///
15+
/// At the moment, only one strategy is implemented: [HashLocationStrategy].
16+
///
17+
/// This is used by [BrowserHistory] to interact with browser history APIs.
18+
abstract class LocationStrategy {
19+
const LocationStrategy();
20+
21+
/// Subscribes to popstate events and returns a function that could be used to
22+
/// unsubscribe from popstate events.
23+
ui.VoidCallback onPopState(html.EventListener fn);
24+
25+
/// The active path in the browser history.
26+
String get path;
27+
28+
/// The state of the current browser history entry.
29+
dynamic get state;
30+
31+
/// Given a path that's internal to the app, create the external url that
32+
/// will be used in the browser.
33+
String prepareExternalUrl(String internalUrl);
34+
35+
/// Push a new history entry.
36+
void pushState(dynamic state, String title, String url);
37+
38+
/// Replace the currently active history entry.
39+
void replaceState(dynamic state, String title, String url);
40+
41+
/// Go to the previous history entry.
42+
Future<void> back({int count = 1});
43+
}
44+
45+
/// This is an implementation of [LocationStrategy] that uses the browser URL's
46+
/// [hash fragments](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
47+
/// to represent its state.
48+
///
49+
/// In order to use this [LocationStrategy] for an app, it needs to be set in
50+
/// [ui.window.locationStrategy]:
51+
///
52+
/// ```dart
53+
/// import 'package:flutter_web/material.dart';
54+
/// import 'package:flutter_web/ui.dart' as ui;
55+
///
56+
/// void main() {
57+
/// ui.window.locationStrategy = const ui.HashLocationStrategy();
58+
/// runApp(MyApp());
59+
/// }
60+
/// ```
61+
class HashLocationStrategy extends LocationStrategy {
62+
final PlatformLocation _platformLocation;
63+
64+
const HashLocationStrategy(
65+
[this._platformLocation = const BrowserPlatformLocation()]);
66+
67+
@override
68+
ui.VoidCallback onPopState(html.EventListener fn) {
69+
_platformLocation.onPopState(fn);
70+
return () => _platformLocation.offPopState(fn);
71+
}
72+
73+
@override
74+
String get path {
75+
// the hash value is always prefixed with a `#`
76+
// and if it is empty then it will stay empty
77+
String path = _platformLocation.hash ?? '';
78+
assert(path.isEmpty || path.startsWith('#'));
79+
80+
// We don't want to return an empty string as a path. Instead we default to "/".
81+
if (path.isEmpty || path == '#') {
82+
return '/';
83+
}
84+
// At this point, we know [path] starts with "#" and isn't empty.
85+
return path.substring(1);
86+
}
87+
88+
@override
89+
dynamic get state => _platformLocation.state;
90+
91+
@override
92+
String prepareExternalUrl(String internalUrl) {
93+
// It's convention that if the hash path is empty, we omit the `#`; however,
94+
// if the empty URL is pushed it won't replace any existing fragment. So
95+
// when the hash path is empty, we instead return the location's path and
96+
// query.
97+
return internalUrl.isEmpty
98+
? '${_platformLocation.pathname}${_platformLocation.search}'
99+
: '#$internalUrl';
100+
}
101+
102+
@override
103+
void pushState(dynamic state, String title, String url) {
104+
_platformLocation.pushState(state, title, prepareExternalUrl(url));
105+
}
106+
107+
@override
108+
void replaceState(dynamic state, String title, String url) {
109+
_platformLocation.replaceState(state, title, prepareExternalUrl(url));
110+
}
111+
112+
@override
113+
Future<void> back({int count = 1}) {
114+
_platformLocation.back(count);
115+
return _waitForPopState();
116+
}
117+
118+
/// Waits until the next popstate event is fired.
119+
///
120+
/// This is useful for example to wait until the browser has handled the
121+
/// `history.back` transition.
122+
Future<void> _waitForPopState() {
123+
final Completer<void> completer = Completer<void>();
124+
late ui.VoidCallback unsubscribe;
125+
unsubscribe = onPopState((_) {
126+
unsubscribe();
127+
completer.complete();
128+
});
129+
return completer.future;
130+
}
131+
}
132+
133+
/// [PlatformLocation] encapsulates all calls to DOM apis, which allows the
134+
/// [LocationStrategy] classes to be platform agnostic and testable.
135+
///
136+
/// The [PlatformLocation] class is used directly by all implementations of
137+
/// [LocationStrategy] when they need to interact with the DOM apis like
138+
/// pushState, popState, etc...
139+
abstract class PlatformLocation {
140+
const PlatformLocation();
141+
142+
void onPopState(html.EventListener fn);
143+
void offPopState(html.EventListener fn);
144+
145+
void onHashChange(html.EventListener fn);
146+
void offHashChange(html.EventListener fn);
147+
148+
String get pathname;
149+
String get search;
150+
String? get hash;
151+
dynamic get state;
152+
153+
void pushState(dynamic state, String title, String url);
154+
void replaceState(dynamic state, String title, String url);
155+
void back(int count);
156+
}
157+
158+
/// An implementation of [PlatformLocation] for the browser.
159+
class BrowserPlatformLocation extends PlatformLocation {
160+
html.Location get _location => html.window.location;
161+
html.History get _history => html.window.history;
162+
163+
const BrowserPlatformLocation();
164+
165+
@override
166+
void onPopState(html.EventListener fn) {
167+
html.window.addEventListener('popstate', fn);
168+
}
169+
170+
@override
171+
void offPopState(html.EventListener fn) {
172+
html.window.removeEventListener('popstate', fn);
173+
}
174+
175+
@override
176+
void onHashChange(html.EventListener fn) {
177+
html.window.addEventListener('hashchange', fn);
178+
}
179+
180+
@override
181+
void offHashChange(html.EventListener fn) {
182+
html.window.removeEventListener('hashchange', fn);
183+
}
184+
185+
@override
186+
String get pathname => _location.pathname!;
187+
188+
@override
189+
String get search => _location.search!;
190+
191+
@override
192+
String get hash => _location.hash;
193+
194+
@override
195+
dynamic get state => _history.state;
196+
197+
@override
198+
void pushState(dynamic state, String title, String url) {
199+
_history.pushState(state, title, url);
200+
}
201+
202+
@override
203+
void replaceState(dynamic state, String title, String url) {
204+
_history.replaceState(state, title, url);
205+
}
206+
207+
@override
208+
void back(int count) {
209+
_history.go(-count);
210+
}
211+
}

0 commit comments

Comments
 (0)