blob: 345aa5dfccad5de3e22487321548e3002882194c [file] [log] [blame]
// Copyright 2013 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 'package:flutter/foundation.dart';
import 'go_route.dart';
import 'go_router_delegate.dart';
import 'path_parser.dart';
/// Each GoRouteMatch instance represents an instance of a GoRoute for a
/// specific portion of a location.
class GoRouteMatch {
/// Constructor for GoRouteMatch, each instance represents an instance of a
/// GoRoute for a specific portion of a location.
GoRouteMatch({
required this.route,
required this.subloc,
required this.fullpath,
required this.encodedParams,
required this.queryParams,
required this.extra,
required this.error,
this.pageKey,
}) : assert(subloc.startsWith('/')),
assert(Uri.parse(subloc).queryParameters.isEmpty),
assert(fullpath.startsWith('/')),
assert(Uri.parse(fullpath).queryParameters.isEmpty),
assert(() {
for (final MapEntry<String, String> p in encodedParams.entries) {
assert(p.value == Uri.encodeComponent(Uri.decodeComponent(p.value)),
'encodedParams[${p.key}] is not encoded properly: "${p.value}"');
}
return true;
}());
// ignore: public_member_api_docs
factory GoRouteMatch.matchNamed({
required GoRoute route,
required String name, // e.g. person
required String fullpath, // e.g. /family/:fid/person/:pid
required Map<String, String> params, // e.g. {'fid': 'f2', 'pid': 'p1'}
required Map<String, String> queryParams, // e.g. {'from': '/family/f2'}
required Object? extra,
}) {
assert(route.name != null);
assert(route.name!.toLowerCase() == name.toLowerCase());
assert(() {
// check that we have all the params we need
final List<String> paramNames = <String>[];
patternToRegExp(fullpath, paramNames);
for (final String paramName in paramNames) {
assert(params.containsKey(paramName),
'missing param "$paramName" for $fullpath');
}
// check that we have don't have extra params
for (final String key in params.keys) {
assert(paramNames.contains(key), 'unknown param "$key" for $fullpath');
}
return true;
}());
final Map<String, String> encodedParams = <String, String>{
for (final MapEntry<String, String> param in params.entries)
param.key: Uri.encodeComponent(param.value)
};
final String subloc = _locationFor(fullpath, encodedParams);
return GoRouteMatch(
route: route,
subloc: subloc,
fullpath: fullpath,
encodedParams: encodedParams,
queryParams: queryParams,
extra: extra,
error: null,
);
}
// ignore: public_member_api_docs
static GoRouteMatch? match({
required GoRoute route,
required String restLoc, // e.g. person/p1
required String parentSubloc, // e.g. /family/f2
required String path, // e.g. person/:pid
required String fullpath, // e.g. /family/:fid/person/:pid
required Map<String, String> queryParams,
required Object? extra,
}) {
assert(!path.contains('//'));
final RegExpMatch? match = route.matchPatternAsPrefix(restLoc);
if (match == null) {
return null;
}
final Map<String, String> encodedParams = route.extractPathParams(match);
final String pathLoc = _locationFor(path, encodedParams);
final String subloc = GoRouterDelegate.fullLocFor(parentSubloc, pathLoc);
return GoRouteMatch(
route: route,
subloc: subloc,
fullpath: fullpath,
encodedParams: encodedParams,
queryParams: queryParams,
extra: extra,
error: null,
);
}
/// The matched route.
final GoRoute route;
/// Matched sub-location.
final String subloc; // e.g. /family/f2
/// Matched full path.
final String fullpath; // e.g. /family/:fid
/// Parameters for the matched route, URI-encoded.
final Map<String, String> encodedParams;
/// Query parameters for the matched route.
final Map<String, String> queryParams;
/// An extra object to pass along with the navigation.
final Object? extra;
/// An exception if there was an error during matching.
final Exception? error;
/// Optional value key of type string, to hold a unique reference to a page.
final ValueKey<String>? pageKey;
/// Parameters for the matched route, URI-decoded.
Map<String, String> get decodedParams => <String, String>{
for (final MapEntry<String, String> param in encodedParams.entries)
param.key: Uri.decodeComponent(param.value)
};
/// for use by the Router architecture as part of the GoRouteMatch
@override
String toString() => 'GoRouteMatch($fullpath, $encodedParams)';
/// expand a path w/ param slots using params, e.g. family/:fid => family/f1
static String _locationFor(String pattern, Map<String, String> params) =>
patternToPath(pattern, params);
}