blob: a888daf86b257b282ecc551b2539463124be154f [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 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'configuration.dart';
import 'delegate.dart';
import 'information_provider.dart';
import 'logging.dart';
import 'match.dart';
import 'matching.dart';
import 'path_utils.dart';
import 'redirection.dart';
/// Converts between incoming URLs and a [RouteMatchList] using [RouteMatcher].
/// Also performs redirection using [RouteRedirector].
class GoRouteInformationParser extends RouteInformationParser<RouteMatchList> {
/// Creates a [GoRouteInformationParser].
GoRouteInformationParser({
required this.configuration,
this.debugRequireGoRouteInformationProvider = false,
}) : matcher = RouteMatcher(configuration),
redirector = redirect;
/// The route configuration for the app.
final RouteConfiguration configuration;
/// The route matcher.
final RouteMatcher matcher;
/// The route redirector.
final RouteRedirector redirector;
/// A debug property to assert [GoRouteInformationProvider] is in use along
/// with this parser.
///
/// An assertion error will be thrown if this property set to true and the
/// [GoRouteInformationProvider] is not in use.
///
/// Defaults to false.
final bool debugRequireGoRouteInformationProvider;
/// The future of current route parsing.
///
/// This is used for testing asynchronous redirection.
@visibleForTesting
Future<RouteMatchList>? debugParserFuture;
/// Called by the [Router]. The
@override
Future<RouteMatchList> parseRouteInformationWithDependencies(
RouteInformation routeInformation,
BuildContext context,
) {
late final RouteMatchList initialMatches;
try {
// TODO(chunhtai): remove this ignore and migrate the code
// https://github.com/flutter/flutter/issues/124045.
// ignore: deprecated_member_use, unnecessary_non_null_assertion
initialMatches = matcher.findMatch(routeInformation.location!,
extra: routeInformation.state);
} on MatcherError {
// TODO(chunhtai): remove this ignore and migrate the code
// https://github.com/flutter/flutter/issues/124045.
// ignore: deprecated_member_use
log.info('No initial matches: ${routeInformation.location}');
// If there is a matching error for the initial location, we should
// still try to process the top-level redirects.
initialMatches = RouteMatchList(
<RouteMatch>[],
// TODO(chunhtai): remove this ignore and migrate the code
// https://github.com/flutter/flutter/issues/124045.
// ignore: deprecated_member_use, unnecessary_non_null_assertion
Uri.parse(canonicalUri(routeInformation.location!)),
const <String, String>{},
);
}
Future<RouteMatchList> processRedirectorResult(RouteMatchList matches) {
if (matches.isEmpty) {
return SynchronousFuture<RouteMatchList>(errorScreen(
// TODO(chunhtai): remove this ignore and migrate the code
// https://github.com/flutter/flutter/issues/124045.
// ignore: deprecated_member_use, unnecessary_non_null_assertion
Uri.parse(routeInformation.location!),
// TODO(chunhtai): remove this ignore and migrate the code
// https://github.com/flutter/flutter/issues/124045.
// ignore: deprecated_member_use, unnecessary_non_null_assertion
MatcherError('no routes for location', routeInformation.location!)
.toString()));
}
return SynchronousFuture<RouteMatchList>(matches);
}
final FutureOr<RouteMatchList> redirectorResult = redirector(
context,
SynchronousFuture<RouteMatchList>(initialMatches),
configuration,
matcher,
extra: routeInformation.state,
);
if (redirectorResult is RouteMatchList) {
return processRedirectorResult(redirectorResult);
}
return debugParserFuture = redirectorResult.then(processRedirectorResult);
}
@override
Future<RouteMatchList> parseRouteInformation(
RouteInformation routeInformation) {
throw UnimplementedError(
'use parseRouteInformationWithDependencies instead');
}
/// for use by the Router architecture as part of the RouteInformationParser
@override
RouteInformation? restoreRouteInformation(RouteMatchList configuration) {
if (configuration.isEmpty) {
return null;
}
if (configuration.matches.last is ImperativeRouteMatch) {
configuration =
(configuration.matches.last as ImperativeRouteMatch<Object?>).matches;
}
return RouteInformation(
// TODO(chunhtai): remove this ignore and migrate the code
// https://github.com/flutter/flutter/issues/124045.
// ignore: deprecated_member_use
location: configuration.uri.toString(),
state: configuration.extra,
);
}
}