Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added uri construction for queryParameters #563

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package/lib/src/beam_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ class BeamState with RouteInformationSerializable<BeamState> {
);

@override
int get hashCode => hashValues(uri, json.encode(routeState));
int get hashCode => Object.hash(uri, json.encode(routeState));

@override
bool operator ==(Object other) {
Expand Down
10 changes: 7 additions & 3 deletions package/lib/src/beamer.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:beamer/beamer.dart';
import 'package:beamer/src/utils.dart';
import 'package:flutter/widgets.dart';

import 'package:beamer/src/path_url_strategy_nonweb.dart'
Expand Down Expand Up @@ -153,6 +154,7 @@ extension BeamerExtensions on BuildContext {
String uri, {
Object? routeState,
Object? data,
Map<String, dynamic>? queryParameters,
String? popToNamed,
TransitionDelegate? transitionDelegate,
bool beamBackOnPop = false,
Expand All @@ -161,7 +163,7 @@ extension BeamerExtensions on BuildContext {
bool replaceRouteInformation = false,
}) {
Beamer.of(this).beamToNamed(
uri,
constructUri(uri, queryParameters),
routeState: routeState,
data: data,
popToNamed: popToNamed,
Expand All @@ -178,14 +180,15 @@ extension BeamerExtensions on BuildContext {
String uri, {
Object? routeState,
Object? data,
Map<String, dynamic>? queryParameters,
String? popToNamed,
TransitionDelegate? transitionDelegate,
bool beamBackOnPop = false,
bool popBeamLocationOnPop = false,
bool stacked = true,
}) {
Beamer.of(this).beamToReplacementNamed(
uri,
constructUri(uri, queryParameters),
routeState: routeState,
data: data,
popToNamed: popToNamed,
Expand All @@ -201,14 +204,15 @@ extension BeamerExtensions on BuildContext {
String uri, {
Object? routeState,
Object? data,
Map<String, dynamic>? queryParameters,
String? popToNamed,
bool beamBackOnPop = false,
bool popBeamLocationOnPop = false,
bool stacked = true,
bool replaceRouteInformation = false,
}) {
Beamer.of(this).popToNamed(
uri,
constructUri(uri, queryParameters),
routeState: routeState,
data: data,
popToNamed: popToNamed,
Expand Down
15 changes: 11 additions & 4 deletions package/lib/src/beamer_delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -546,9 +546,13 @@ class BeamerDelegate extends RouterDelegate<RouteInformation>
bool popBeamLocationOnPop = false,
bool stacked = true,
bool replaceRouteInformation = false,
Map<String, dynamic>? queryParameters,
}) {
update(
configuration: RouteInformation(location: uri, state: routeState),
configuration: RouteInformation(
location: constructUri(uri, queryParameters),
state: routeState,
),
beamParameters: _currentBeamParameters.copyWith(
popConfiguration:
popToNamed != null ? RouteInformation(location: popToNamed) : null,
Expand All @@ -573,10 +577,11 @@ class BeamerDelegate extends RouterDelegate<RouteInformation>
bool beamBackOnPop = false,
bool popBeamLocationOnPop = false,
bool stacked = true,
Map<String, dynamic>? queryParameters,
}) {
removeLastHistoryElement();
beamToNamed(
uri,
constructUri(uri, queryParameters),
routeState: routeState,
data: data,
popToNamed: popToNamed,
Expand Down Expand Up @@ -604,10 +609,12 @@ class BeamerDelegate extends RouterDelegate<RouteInformation>
bool popBeamLocationOnPop = false,
bool stacked = true,
bool replaceRouteInformation = false,
Map<String, dynamic>? queryParameters,
}) {
final fullUri = constructUri(uri, queryParameters);
while (beamingHistory.isNotEmpty) {
final index = beamingHistory.last.history.lastIndexWhere(
(element) => element.routeInformation.location == uri,
(element) => element.routeInformation.location == fullUri,
);
if (index == -1) {
_disposeBeamLocation(beamingHistory.last);
Expand All @@ -620,7 +627,7 @@ class BeamerDelegate extends RouterDelegate<RouteInformation>
}
}
beamToNamed(
uri,
fullUri,
routeState: routeState,
data: data,
popToNamed: popToNamed,
Expand Down
18 changes: 18 additions & 0 deletions package/lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,21 @@ extension BeamerRouteInformationExtension on RouteInformation {
return location == other.location && state == other.state;
}
}

/// Returns the provided `String uri` with the constructed `Map<String, dynamic>? queryParameters`
///
/// If `Map<String, dynamic>? queryParameters` is provided, `String uri` should not containt query parameters.
///
/// When `queryParameters` is used the query is built from the provided map.
/// Each key and value in the map is percent-encoded and joined using equal and ampersand characters.
/// A value in the map must be either a `String`, or an `Iterable<String>`, where the latter corresponds to multiple values for the same key.
String constructUri(String uri, Map<String, dynamic>? queryParameters) {
final _inputQueryParameters = Uri.parse(uri).queryParameters;
assert(_inputQueryParameters.isEmpty || (queryParameters?.isEmpty ?? true),
'Avoid passing an uri that already contains query Parameters and a non-empty `queryParameters`');

if (queryParameters?.isEmpty ?? true) return uri;
if (_inputQueryParameters.isNotEmpty) return uri;

return uri + '?' + Uri(queryParameters: queryParameters).query;
}
17 changes: 17 additions & 0 deletions package/test/beamer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void main() {
key: beamerKey,
routerDelegate: BeamerProvider.of(context)!.routerDelegate,
),
'/test3': (context, state, data) => Container(),
},
),
),
Expand Down Expand Up @@ -170,4 +171,20 @@ void main() {
await tester.pumpWidget(app);
expect(testContext!.beamingHistory.length, greaterThan(0));
});

testWidgets('Beaming updates location state with queryParameters',
(tester) async {
const queryParams = {'testParam': 'test'};
await tester.pumpWidget(app);
testContext!.beamToReplacementNamed('/test3', queryParameters: queryParams);
await tester.pump();
expect(testContext!.currentBeamLocation.state.routeInformation.location,
'/test3?testParam=test');
expect(
(testContext!.currentBeamLocation.state as BeamState)
.uriBlueprint
.toString(),
'/test3?testParam=test');
expect(testContext!.currentBeamPages.length, 2);
});
}
27 changes: 27 additions & 0 deletions package/test/path_url_query_parameters_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:beamer/src/utils.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
test(
'Only URI was passed',
() {
expect(constructUri('/test', null), '/test');
},
);

test(
'URI without queryParameters and queryParameters Map were passed',
() {
expect(constructUri('/test', {'test': 'true'}), '/test?test=true');
},
);

test(
'URI wit queryParameters and queryParameters Map were passed',
() {
expect(() {
constructUri('/test?test=true', {'test': 'true'});
}, throwsAssertionError);
},
);
}