1
0
mirror of https://github.com/flutter/samples.git synced 2026-04-04 02:32:25 +00:00

Replace navigation_and_routing with a new sample (#832)

* move snippets into old_snippets directory

* add new navigation_and_routing sample

* add copyright headers

* Apply #827 to old_snippets/ directory and upgrade them to null safety

* Code review comments

- Move Guard class into parser.dart
- Move usage of guards from Delegate to RouteInformationParser
- Rename delegate to SimpleRouterDelegate

* clean up imports

* refactor settings screen, fix bug

* avoid conflicting paths /books/new and /books/1 - rename to book/1

* dispose fields in _BookstoreState class

* remove /books path

This was causing problems

* add comment

* Change BookstoreAuthScope and BookstoreAuthScope to InheritedNotifier

* fix warnings

* Make the initial route configurable, set to '/signin'

* Enable deep linking

https://flutter.dev/docs/development/ui/navigation/deep-linking

* use path URL strategy on the web.

* remove TODO, add comment
This commit is contained in:
John Ryan
2021-07-08 07:48:17 -07:00
committed by GitHub
parent 8573269c03
commit ae3c4e3c47
96 changed files with 3560 additions and 278 deletions

View File

@@ -0,0 +1,52 @@
// Copyright 2021, the Flutter project authors. Please see the AUTHORS file
// for details. 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 'parsed_route.dart';
import 'route_state.dart';
class SimpleRouterDelegate extends RouterDelegate<ParsedRoute>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<ParsedRoute> {
final RouteState routeState;
final WidgetBuilder builder;
@override
final GlobalKey<NavigatorState> navigatorKey;
SimpleRouterDelegate({
required this.routeState,
required this.builder,
required this.navigatorKey,
// ignore: prefer_initializing_formals
}) {
routeState.addListener(notifyListeners);
}
@override
Widget build(BuildContext context) {
return builder(context);
}
@override
Future<void> setNewRoutePath(ParsedRoute configuration) async {
routeState.route = configuration;
return SynchronousFuture(null);
}
@override
ParsedRoute get currentConfiguration {
return routeState.route;
}
@override
void dispose() {
routeState.removeListener(notifyListeners);
routeState.dispose();
super.dispose();
}
}

View File

@@ -0,0 +1,45 @@
// Copyright 2021, the Flutter project authors. Please see the AUTHORS file
// for details. 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:collection/collection.dart';
import 'package:quiver/core.dart';
import 'parser.dart';
/// A route path that has been parsed by [TemplateRouteParser].
class ParsedRoute {
final String path;
final String pathTemplate;
final Map<String, String> parameters;
final Map<String, String> queryParameters;
static const _mapEquality = MapEquality<String, String>();
ParsedRoute(
this.path, this.pathTemplate, this.parameters, this.queryParameters);
@override
bool operator ==(Object other) {
return other is ParsedRoute &&
other.pathTemplate == pathTemplate &&
other.path == path &&
_mapEquality.equals(parameters, other.parameters) &&
_mapEquality.equals(queryParameters, other.queryParameters);
}
@override
int get hashCode => hash4(
path,
pathTemplate,
_mapEquality.hash(parameters),
_mapEquality.hash(queryParameters),
);
@override
String toString() => '<ParsedRoute '
'template: $pathTemplate '
'path: $path '
'parameters: $parameters '
'query parameters: $queryParameters>';
}

View File

@@ -0,0 +1,68 @@
// Copyright 2021, the Flutter project authors. Please see the AUTHORS file
// for details. 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/widgets.dart';
import 'package:path_to_regexp/path_to_regexp.dart';
import 'parsed_route.dart';
abstract class RouteGuard<T> {
Future<T> redirect(T from);
}
/// Parses the URI path into a [ParsedRoute].
class TemplateRouteParser extends RouteInformationParser<ParsedRoute> {
final List<String> _pathTemplates = [];
RouteGuard<ParsedRoute>? guard;
final ParsedRoute initialRoute;
TemplateRouteParser(List<String> pathTemplates,
{String? initialRoute = '/', this.guard})
: initialRoute =
ParsedRoute(initialRoute ?? '/', initialRoute ?? '/', {}, {}) {
for (var template in pathTemplates) {
_addRoute(template);
}
}
void _addRoute(String pathTemplate) {
_pathTemplates.add(pathTemplate);
}
@override
Future<ParsedRoute> parseRouteInformation(
RouteInformation routeInformation) async {
return await _parse(routeInformation);
}
Future<ParsedRoute> _parse(RouteInformation routeInformation) async {
final path = routeInformation.location!;
final queryParams = Uri.parse(path).queryParameters;
var parsedRoute = initialRoute;
for (var pathTemplate in _pathTemplates) {
final parameters = <String>[];
var pathRegExp = pathToRegExp(pathTemplate, parameters: parameters);
if (pathRegExp.hasMatch(path)) {
final match = pathRegExp.matchAsPrefix(path);
if (match == null) continue;
final params = extract(parameters, match);
parsedRoute = ParsedRoute(path, pathTemplate, params, queryParams);
}
}
// Redirect if a guard is present
var guard = this.guard;
if (guard != null) {
return guard.redirect(parsedRoute);
}
return parsedRoute;
}
@override
RouteInformation restoreRouteInformation(ParsedRoute configuration) {
return RouteInformation(location: configuration.path);
}
}

View File

@@ -0,0 +1,50 @@
// Copyright 2021, the Flutter project authors. Please see the AUTHORS file
// for details. 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 'package:flutter/widgets.dart';
import 'parsed_route.dart';
import 'parser.dart';
/// The current route state. To change the current route, call obtain the state using
/// `RouteState.of(context)` and call `go()`:
///
/// ```
/// RouteState.of(context).go('/book/2');
/// ```
class RouteState extends ChangeNotifier {
TemplateRouteParser parser;
ParsedRoute _route;
RouteState(this.parser)
: _route = parser.initialRoute;
ParsedRoute get route => _route;
set route(ParsedRoute route) {
_route = route;
notifyListeners();
}
Future<void> go(String route) async {
this.route =
await parser.parseRouteInformation(RouteInformation(location: route));
}
}
/// Provides the current [RouteState] to descendent widgets in the tree.
class RouteStateScope extends InheritedNotifier<RouteState> {
const RouteStateScope({
required RouteState notifier,
required Widget child,
Key? key,
}) : super(key: key, notifier: notifier, child: child);
static RouteState? of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<RouteStateScope>()
?.notifier;
}
}