[go_router] Applies flutter package lints (#1017)
diff --git a/.cirrus.yml b/.cirrus.yml
index fd58d13..ce62ba0 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -48,8 +48,7 @@
always:
format_script: ./script/tool_runner.sh format --fail-on-change
license_script: dart pub global run flutter_plugin_tools license-check
- # TODO(chunhtai): Remove go_router custom-analysis https://github.com/flutter/flutter/issues/98711
- analyze_script: ./script/tool_runner.sh analyze --custom-analysis=go_router,web_benchmarks/testing/test_app,flutter_lints/example,rfw/example
+ analyze_script: ./script/tool_runner.sh analyze --custom-analysis=web_benchmarks/testing/test_app,flutter_lints/example,rfw/example
pubspec_script: ./script/tool_runner.sh pubspec-check
- name: publishable
env:
diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md
index e08a182..bae35ca 100644
--- a/packages/go_router/CHANGELOG.md
+++ b/packages/go_router/CHANGELOG.md
@@ -1,6 +1,10 @@
+## 3.0.4
+
+- Updates code for stricter analysis options.
+
## 3.0.3
-- Fixed a bug where params disappear when pushing a nested route.
+- Fixes a bug where params disappear when pushing a nested route.
## 3.0.2
diff --git a/packages/go_router/README.md b/packages/go_router/README.md
index 4c66a6e..85f0712 100644
--- a/packages/go_router/README.md
+++ b/packages/go_router/README.md
@@ -20,15 +20,15 @@
title: 'GoRouter Example',
);
- final _router = GoRouter(
- routes: [
+ final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) => const Page1Screen(),
),
GoRoute(
path: '/page2',
- builder: (context, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) => const Page2Screen(),
),
],
);
diff --git a/packages/go_router/analysis_options.yaml b/packages/go_router/analysis_options.yaml
deleted file mode 100644
index 53897be..0000000
--- a/packages/go_router/analysis_options.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-# This file is temporary and should be removed as a part of
-# https://github.com/flutter/flutter/issues/98711.
-analyzer:
- exclude:
- - "**/*.g.dart"
- - "**/*.freezed.dart"
- - "test/.test_coverage.dart"
- - "bin/cache/**"
- - "lib/generated_plugin_registrant.dart"
- strong-mode:
- implicit-casts: false
- implicit-dynamic: false
- errors:
- included_file_warning: ignore
- missing_required_param: error
- missing_return: error
- parameter_assignments: error
-
-linter:
- rules:
- prefer_double_quotes: false
- unnecessary_final: false
- always_specify_types: false
- prefer_final_parameters: false
- prefer_asserts_with_message: false
- require_trailing_commas: false
- avoid_classes_with_only_static_members: false
- always_put_control_body_on_new_line: false
- always_use_package_imports: false
- avoid_annotating_with_dynamic: false
- avoid_redundant_argument_values: false
- one_member_abstracts: false
- flutter_style_todos: false
diff --git a/packages/go_router/example/analysis_options.yaml b/packages/go_router/example/analysis_options.yaml
deleted file mode 100644
index 6cb1fba..0000000
--- a/packages/go_router/example/analysis_options.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
-# This file is temporary and should be removed as a part of
-# https://github.com/flutter/flutter/issues/98711.
-
-include: package:all_lint_rules_community/all.yaml
-
-analyzer:
- exclude:
- - "**/*.g.dart"
- - "**/*.freezed.dart"
- - "test/.test_coverage.dart"
- - "bin/cache/**"
- - "lib/generated_plugin_registrant.dart"
- strong-mode:
- implicit-casts: false
- implicit-dynamic: false
- errors:
- included_file_warning: ignore
- missing_required_param: error
- missing_return: error
- parameter_assignments: error
-
-linter:
- rules:
- prefer_double_quotes: false
- unnecessary_final: false
- always_specify_types: false
- prefer_final_parameters: false
- prefer_asserts_with_message: false
- require_trailing_commas: false
- public_member_api_docs: false
- avoid_classes_with_only_static_members: false
- always_put_control_body_on_new_line: false
- always_use_package_imports: false
- avoid_annotating_with_dynamic: false
- avoid_redundant_argument_values: false
- one_member_abstracts: false
- flutter_style_todos: false
- diagnostic_describe_all_properties: false
- library_private_types_in_public_api: false
diff --git a/packages/go_router/example/lib/async_data.dart b/packages/go_router/example/lib/async_data.dart
index 1ab14c2..2a87a83 100644
--- a/packages/go_router/example/lib/async_data.dart
+++ b/packages/go_router/example/lib/async_data.dart
@@ -13,11 +13,16 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates a [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Async Data';
- static final repo = Repository();
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Async Data';
+
+ /// Repository to query data from.
+ static final Repository repo = Repository();
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -26,21 +31,24 @@
title: title,
);
- late final _router = GoRouter(
- routes: [
+ late final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
path: 'family/:fid',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(
fid: state.params['fid']!,
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- builder: (context, state) => PersonScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ PersonScreen(
fid: state.params['fid']!,
pid: state.params['pid']!,
),
@@ -53,7 +61,9 @@
);
}
+/// The home screen.
class HomeScreen extends StatefulWidget {
+ /// Creates a [HomeScreen].
const HomeScreen({Key? key}) : super(key: key);
@override
@@ -82,7 +92,7 @@
@override
Widget build(BuildContext context) => FutureBuilder<List<Family>>(
future: _future,
- builder: (context, snapshot) {
+ builder: (BuildContext context, AsyncSnapshot<List<Family>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
appBar: AppBar(title: const Text('${App.title}: Loading...')),
@@ -98,14 +108,14 @@
}
assert(snapshot.hasData);
- final families = snapshot.data!;
+ final List<Family> families = snapshot.data!;
return Scaffold(
appBar: AppBar(
title: Text('${App.title}: ${families.length} families'),
),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
onTap: () => context.go('/family/${f.id}'),
@@ -117,8 +127,12 @@
);
}
+/// The family screen.
class FamilyScreen extends StatefulWidget {
+ /// Creates a [FamilyScreen].
const FamilyScreen({required this.fid, Key? key}) : super(key: key);
+
+ /// The id of the family.
final String fid;
@override
@@ -139,7 +153,9 @@
super.didUpdateWidget(oldWidget);
// refresh cached data
- if (oldWidget.fid != widget.fid) _fetch();
+ if (oldWidget.fid != widget.fid) {
+ _fetch();
+ }
}
void _fetch() => _future = App.repo.getFamily(widget.fid);
@@ -147,7 +163,7 @@
@override
Widget build(BuildContext context) => FutureBuilder<Family>(
future: _future,
- builder: (context, snapshot) {
+ builder: (BuildContext context, AsyncSnapshot<Family> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
appBar: AppBar(title: const Text('Loading...')),
@@ -163,12 +179,12 @@
}
assert(snapshot.hasData);
- final family = snapshot.data!;
+ final Family family = snapshot.data!;
return Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
- children: [
- for (final p in family.people)
+ children: <Widget>[
+ for (final Person p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go(
@@ -182,11 +198,16 @@
);
}
+/// The person screen.
class PersonScreen extends StatefulWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.fid, required this.pid, Key? key})
: super(key: key);
+ /// The id of family the person is in.
final String fid;
+
+ /// The id of the person.
final String pid;
@override
@@ -207,7 +228,9 @@
super.didUpdateWidget(oldWidget);
// refresh cached data
- if (oldWidget.fid != widget.fid || oldWidget.pid != widget.pid) _fetch();
+ if (oldWidget.fid != widget.fid || oldWidget.pid != widget.pid) {
+ _fetch();
+ }
}
void _fetch() => _future = App.repo.getPerson(widget.fid, widget.pid);
@@ -215,7 +238,7 @@
@override
Widget build(BuildContext context) => FutureBuilder<FamilyPerson>(
future: _future,
- builder: (context, snapshot) {
+ builder: (BuildContext context, AsyncSnapshot<FamilyPerson> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
appBar: AppBar(title: const Text('Loading...')),
@@ -231,7 +254,7 @@
}
assert(snapshot.hasData);
- final famper = snapshot.data!;
+ final FamilyPerson famper = snapshot.data!;
return Scaffold(
appBar: AppBar(title: Text(famper.person.name)),
body: Text(
@@ -243,17 +266,21 @@
);
}
+/// The Error page.
class SnapshotError extends StatelessWidget {
+ /// Creates an error page.
SnapshotError(Object error, {Key? key})
: error = error is Exception ? error : Exception(error),
super(key: key);
+
+ /// The error.
final Exception error;
@override
Widget build(BuildContext context) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
SelectableText(error.toString()),
TextButton(
onPressed: () => context.go('/'),
diff --git a/packages/go_router/example/lib/books/main.dart b/packages/go_router/example/lib/books/main.dart
index f5af909..7f20594 100644
--- a/packages/go_router/example/lib/books/main.dart
+++ b/packages/go_router/example/lib/books/main.dart
@@ -7,6 +7,8 @@
import 'package:go_router/go_router.dart';
import 'src/auth.dart';
+import 'src/data/author.dart';
+import 'src/data/book.dart';
import 'src/data/library.dart';
import 'src/screens/author_details.dart';
import 'src/screens/authors.dart';
@@ -18,10 +20,12 @@
void main() => runApp(Bookstore());
+/// The book store view.
class Bookstore extends StatelessWidget {
+ /// Creates a [Bookstore].
Bookstore({Key? key}) : super(key: key);
- final _scaffoldKey = const ValueKey<String>('App scaffold');
+ final ValueKey<String> _scaffoldKey = const ValueKey<String>('App scaffold');
@override
Widget build(BuildContext context) => BookstoreAuthScope(
@@ -32,20 +36,21 @@
),
);
- final _auth = BookstoreAuth();
+ final BookstoreAuth _auth = BookstoreAuth();
- late final _router = GoRouter(
- routes: [
+ late final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
redirect: (_) => '/books',
),
GoRoute(
path: '/signin',
- pageBuilder: (context, state) => FadeTransitionPage(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ FadeTransitionPage(
key: state.pageKey,
child: SignInScreen(
- onSignIn: (credentials) {
+ onSignIn: (Credentials credentials) {
BookstoreAuthScope.of(context)
.signIn(credentials.username, credentials.password);
},
@@ -58,24 +63,26 @@
),
GoRoute(
path: '/book/:bookId',
- redirect: (state) => '/books/all/${state.params['bookId']}',
+ redirect: (GoRouterState state) =>
+ '/books/all/${state.params['bookId']}',
),
GoRoute(
path: '/books/:kind(new|all|popular)',
- pageBuilder: (context, state) => FadeTransitionPage(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ FadeTransitionPage(
key: _scaffoldKey,
child: BookstoreScaffold(
selectedTab: ScaffoldTab.books,
child: BooksScreen(state.params['kind']!),
),
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: ':bookId',
- builder: (context, state) {
- final bookId = state.params['bookId']!;
- final selectedBook = libraryInstance.allBooks
- .firstWhereOrNull((b) => b.id.toString() == bookId);
+ builder: (BuildContext context, GoRouterState state) {
+ final String bookId = state.params['bookId']!;
+ final Book? selectedBook = libraryInstance.allBooks
+ .firstWhereOrNull((Book b) => b.id.toString() == bookId);
return BookDetailsScreen(book: selectedBook);
},
@@ -84,24 +91,26 @@
),
GoRoute(
path: '/author/:authorId',
- redirect: (state) => '/authors/${state.params['authorId']}',
+ redirect: (GoRouterState state) =>
+ '/authors/${state.params['authorId']}',
),
GoRoute(
path: '/authors',
- pageBuilder: (context, state) => FadeTransitionPage(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ FadeTransitionPage(
key: _scaffoldKey,
child: const BookstoreScaffold(
selectedTab: ScaffoldTab.authors,
child: AuthorsScreen(),
),
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: ':authorId',
- builder: (context, state) {
- final authorId = int.parse(state.params['authorId']!);
- final selectedAuthor = libraryInstance.allAuthors
- .firstWhereOrNull((a) => a.id == authorId);
+ builder: (BuildContext context, GoRouterState state) {
+ final int authorId = int.parse(state.params['authorId']!);
+ final Author? selectedAuthor = libraryInstance.allAuthors
+ .firstWhereOrNull((Author a) => a.id == authorId);
return AuthorDetailsScreen(author: selectedAuthor);
},
@@ -110,7 +119,8 @@
),
GoRoute(
path: '/settings',
- pageBuilder: (context, state) => FadeTransitionPage(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ FadeTransitionPage(
key: _scaffoldKey,
child: const BookstoreScaffold(
selectedTab: ScaffoldTab.settings,
@@ -125,8 +135,8 @@
);
String? _guard(GoRouterState state) {
- final signedIn = _auth.signedIn;
- final signingIn = state.subloc == '/signin';
+ final bool signedIn = _auth.signedIn;
+ final bool signingIn = state.subloc == '/signin';
// Go to /signin if the user is not signed in
if (!signedIn && !signingIn) {
@@ -142,17 +152,23 @@
}
}
+/// A page that fades in an out.
class FadeTransitionPage extends CustomTransitionPage<void> {
+ /// Creates a [FadeTransitionPage].
FadeTransitionPage({
required LocalKey key,
required Widget child,
}) : super(
key: key,
- transitionsBuilder: (c, animation, a2, child) => FadeTransition(
+ transitionsBuilder: (BuildContext context,
+ Animation<double> animation,
+ Animation<double> secondaryAnimation,
+ Widget child) =>
+ FadeTransition(
opacity: animation.drive(_curveTween),
child: child,
),
child: child);
- static final _curveTween = CurveTween(curve: Curves.easeIn);
+ static final CurveTween _curveTween = CurveTween(curve: Curves.easeIn);
}
diff --git a/packages/go_router/example/lib/books/src/auth.dart b/packages/go_router/example/lib/books/src/auth.dart
index f848149..2b71e4c 100644
--- a/packages/go_router/example/lib/books/src/auth.dart
+++ b/packages/go_router/example/lib/books/src/auth.dart
@@ -8,8 +8,10 @@
class BookstoreAuth extends ChangeNotifier {
bool _signedIn = false;
+ /// Whether user has signed in.
bool get signedIn => _signedIn;
+ /// Signs out the current user.
Future<void> signOut() async {
await Future<void>.delayed(const Duration(milliseconds: 200));
// Sign out.
@@ -17,6 +19,7 @@
notifyListeners();
}
+ /// Signs in a user.
Future<bool> signIn(String username, String password) async {
await Future<void>.delayed(const Duration(milliseconds: 200));
@@ -27,13 +30,16 @@
}
}
+/// An inherited notifier to host [BookstoreAuth] for the subtree.
class BookstoreAuthScope extends InheritedNotifier<BookstoreAuth> {
+ /// Creates a [BookstoreAuthScope].
const BookstoreAuthScope({
required BookstoreAuth notifier,
required Widget child,
Key? key,
}) : super(key: key, notifier: notifier, child: child);
+ /// Gets the [BookstoreAuth] above the context.
static BookstoreAuth of(BuildContext context) => context
.dependOnInheritedWidgetOfExactType<BookstoreAuthScope>()!
.notifier!;
diff --git a/packages/go_router/example/lib/books/src/data/author.dart b/packages/go_router/example/lib/books/src/data/author.dart
index 51138ea..b58db11 100644
--- a/packages/go_router/example/lib/books/src/data/author.dart
+++ b/packages/go_router/example/lib/books/src/data/author.dart
@@ -4,13 +4,20 @@
import 'book.dart';
+/// Author data class.
class Author {
+ /// Creates an author data object.
Author({
required this.id,
required this.name,
});
+ /// The id of the author.
final int id;
+
+ /// The name of the author.
final String name;
+
+ /// The books of the author.
final List<Book> books = <Book>[];
}
diff --git a/packages/go_router/example/lib/books/src/data/book.dart b/packages/go_router/example/lib/books/src/data/book.dart
index 036bbaa..cd2c94f 100644
--- a/packages/go_router/example/lib/books/src/data/book.dart
+++ b/packages/go_router/example/lib/books/src/data/book.dart
@@ -4,7 +4,9 @@
import 'author.dart';
+/// Book data class.
class Book {
+ /// Creates a book data object.
Book({
required this.id,
required this.title,
@@ -13,9 +15,18 @@
required this.author,
});
+ /// The id of the book.
final int id;
+
+ /// The title of the book.
final String title;
+
+ /// The author of the book.
final Author author;
+
+ /// Whether the book is popular.
final bool isPopular;
+
+ /// Whether the book is new.
final bool isNew;
}
diff --git a/packages/go_router/example/lib/books/src/data/library.dart b/packages/go_router/example/lib/books/src/data/library.dart
index d58e5e2..075b825 100644
--- a/packages/go_router/example/lib/books/src/data/library.dart
+++ b/packages/go_router/example/lib/books/src/data/library.dart
@@ -5,7 +5,8 @@
import 'author.dart';
import 'book.dart';
-final libraryInstance = Library()
+/// Library data mock.
+final Library libraryInstance = Library()
..addBook(
title: 'Left Hand of Darkness',
authorName: 'Ursula K. Le Guin',
@@ -27,26 +28,31 @@
isPopular: false,
isNew: false);
+/// A library that contains books and authors.
class Library {
- final List<Book> allBooks = [];
- final List<Author> allAuthors = [];
+ /// The books in the library.
+ final List<Book> allBooks = <Book>[];
+ /// The authors in the library.
+ final List<Author> allAuthors = <Author>[];
+
+ /// Adds a book into the library.
void addBook({
required String title,
required String authorName,
required bool isPopular,
required bool isNew,
}) {
- final author = allAuthors.firstWhere(
- (author) => author.name == authorName,
+ final Author author = allAuthors.firstWhere(
+ (Author author) => author.name == authorName,
orElse: () {
- final value = Author(id: allAuthors.length, name: authorName);
+ final Author value = Author(id: allAuthors.length, name: authorName);
allAuthors.add(value);
return value;
},
);
- final book = Book(
+ final Book book = Book(
id: allBooks.length,
title: title,
isPopular: isPopular,
@@ -58,11 +64,13 @@
allBooks.add(book);
}
- List<Book> get popularBooks => [
- ...allBooks.where((book) => book.isPopular),
+ /// The list of popular books in the library.
+ List<Book> get popularBooks => <Book>[
+ ...allBooks.where((Book book) => book.isPopular),
];
- List<Book> get newBooks => [
- ...allBooks.where((book) => book.isNew),
+ /// The list of new books in the library.
+ List<Book> get newBooks => <Book>[
+ ...allBooks.where((Book book) => book.isNew),
];
}
diff --git a/packages/go_router/example/lib/books/src/screens/author_details.dart b/packages/go_router/example/lib/books/src/screens/author_details.dart
index 0cde5a2..d03ae63 100644
--- a/packages/go_router/example/lib/books/src/screens/author_details.dart
+++ b/packages/go_router/example/lib/books/src/screens/author_details.dart
@@ -8,12 +8,15 @@
import '../data.dart';
import '../widgets/book_list.dart';
+/// The author detail screen.
class AuthorDetailsScreen extends StatelessWidget {
+ /// Creates an author detail screen.
const AuthorDetailsScreen({
required this.author,
Key? key,
}) : super(key: key);
+ /// The author to be displayed.
final Author? author;
@override
@@ -31,11 +34,11 @@
),
body: Center(
child: Column(
- children: [
+ children: <Widget>[
Expanded(
child: BookList(
books: author!.books,
- onTap: (book) => context.go('/book/${book.id}'),
+ onTap: (Book book) => context.go('/book/${book.id}'),
),
),
],
diff --git a/packages/go_router/example/lib/books/src/screens/authors.dart b/packages/go_router/example/lib/books/src/screens/authors.dart
index 9726996..893a3c0 100644
--- a/packages/go_router/example/lib/books/src/screens/authors.dart
+++ b/packages/go_router/example/lib/books/src/screens/authors.dart
@@ -5,13 +5,16 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
-import '../data/library.dart';
+import '../data.dart';
import '../widgets/author_list.dart';
+/// A screen that displays a list of authors.
class AuthorsScreen extends StatelessWidget {
+ /// Creates an [AuthorsScreen].
const AuthorsScreen({Key? key}) : super(key: key);
- static const title = 'Authors';
+ /// The title of the screen.
+ static const String title = 'Authors';
@override
Widget build(BuildContext context) => Scaffold(
@@ -20,7 +23,7 @@
),
body: AuthorList(
authors: libraryInstance.allAuthors,
- onTap: (author) {
+ onTap: (Author author) {
context.go('/author/${author.id}');
},
),
diff --git a/packages/go_router/example/lib/books/src/screens/book_details.dart b/packages/go_router/example/lib/books/src/screens/book_details.dart
index 0bdce99..d26ff71 100644
--- a/packages/go_router/example/lib/books/src/screens/book_details.dart
+++ b/packages/go_router/example/lib/books/src/screens/book_details.dart
@@ -9,12 +9,15 @@
import '../data.dart';
import 'author_details.dart';
+/// A screen to display book details.
class BookDetailsScreen extends StatelessWidget {
+ /// Creates a [BookDetailsScreen].
const BookDetailsScreen({
Key? key,
this.book,
}) : super(key: key);
+ /// The book to be displayed.
final Book? book;
@override
@@ -32,7 +35,7 @@
),
body: Center(
child: Column(
- children: [
+ children: <Widget>[
Text(
book!.title,
style: Theme.of(context).textTheme.headline4,
@@ -45,7 +48,7 @@
onPressed: () {
Navigator.of(context).push<void>(
MaterialPageRoute<void>(
- builder: (context) =>
+ builder: (BuildContext context) =>
AuthorDetailsScreen(author: book!.author),
),
);
@@ -54,7 +57,8 @@
),
Link(
uri: Uri.parse('/author/${book!.author.id}'),
- builder: (context, followLink) => TextButton(
+ builder: (BuildContext context, FollowLink? followLink) =>
+ TextButton(
onPressed: followLink,
child: const Text('View author (Link)'),
),
diff --git a/packages/go_router/example/lib/books/src/screens/books.dart b/packages/go_router/example/lib/books/src/screens/books.dart
index 156d140..76f77b4 100644
--- a/packages/go_router/example/lib/books/src/screens/books.dart
+++ b/packages/go_router/example/lib/books/src/screens/books.dart
@@ -8,9 +8,12 @@
import '../data.dart';
import '../widgets/book_list.dart';
+/// A screen that displays a list of books.
class BooksScreen extends StatefulWidget {
+ /// Creates a [BooksScreen].
const BooksScreen(this.kind, {Key? key}) : super(key: key);
+ /// Which tab to display.
final String kind;
@override
@@ -59,7 +62,7 @@
bottom: TabBar(
controller: _tabController,
onTap: _handleTabTapped,
- tabs: const [
+ tabs: const <Tab>[
Tab(
text: 'Popular',
icon: Icon(Icons.people),
@@ -77,7 +80,7 @@
),
body: TabBarView(
controller: _tabController,
- children: [
+ children: <Widget>[
BookList(
books: libraryInstance.popularBooks,
onTap: _handleBookTapped,
diff --git a/packages/go_router/example/lib/books/src/screens/scaffold.dart b/packages/go_router/example/lib/books/src/screens/scaffold.dart
index ce67e37..a3f8134 100644
--- a/packages/go_router/example/lib/books/src/screens/scaffold.dart
+++ b/packages/go_router/example/lib/books/src/screens/scaffold.dart
@@ -7,16 +7,31 @@
import 'package:go_router/go_router.dart';
-enum ScaffoldTab { books, authors, settings }
+/// The enum for scaffold tab
+enum ScaffoldTab {
+ /// The books tab.
+ books,
+ /// The authors tab.
+ authors,
+
+ /// The settings tab.
+ settings
+}
+
+/// The scaffold for the book store.
class BookstoreScaffold extends StatelessWidget {
+ /// Creates a [BookstoreScaffold].
const BookstoreScaffold({
required this.selectedTab,
required this.child,
Key? key,
}) : super(key: key);
+ /// Which tab of the scaffold to display.
final ScaffoldTab selectedTab;
+
+ /// The scaffold body.
final Widget child;
@override
@@ -24,7 +39,7 @@
body: AdaptiveNavigationScaffold(
selectedIndex: selectedTab.index,
body: child,
- onDestinationSelected: (idx) {
+ onDestinationSelected: (int idx) {
switch (ScaffoldTab.values[idx]) {
case ScaffoldTab.books:
context.go('/books');
@@ -37,7 +52,7 @@
break;
}
},
- destinations: const [
+ destinations: const <AdaptiveScaffoldDestination>[
AdaptiveScaffoldDestination(
title: 'Books',
icon: Icons.book,
diff --git a/packages/go_router/example/lib/books/src/screens/settings.dart b/packages/go_router/example/lib/books/src/screens/settings.dart
index d6dcced..5634bc2 100644
--- a/packages/go_router/example/lib/books/src/screens/settings.dart
+++ b/packages/go_router/example/lib/books/src/screens/settings.dart
@@ -8,7 +8,9 @@
import '../auth.dart';
+/// The settings screen.
class SettingsScreen extends StatefulWidget {
+ /// Creates a [SettingsScreen].
const SettingsScreen({Key? key}) : super(key: key);
@override
@@ -37,15 +39,17 @@
);
}
+/// The content of a [SettingsScreen].
class SettingsContent extends StatelessWidget {
+ /// Creates a [SettingsContent].
const SettingsContent({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) => Column(
- children: [
- ...[
+ children: <Widget>[
+ ...<Widget>[
Text(
'Settings',
style: Theme.of(context).textTheme.headline4,
@@ -58,7 +62,8 @@
),
Link(
uri: Uri.parse('/book/0'),
- builder: (context, followLink) => TextButton(
+ builder: (BuildContext context, FollowLink? followLink) =>
+ TextButton(
onPressed: followLink,
child: const Text('Go directly to /book/0 (Link)'),
),
@@ -69,11 +74,12 @@
},
child: const Text('Go directly to /book/0 (GoRouter)'),
),
- ].map((w) => Padding(padding: const EdgeInsets.all(8), child: w)),
+ ].map<Widget>((Widget w) =>
+ Padding(padding: const EdgeInsets.all(8), child: w)),
TextButton(
onPressed: () => showDialog<String>(
context: context,
- builder: (context) => AlertDialog(
+ builder: (BuildContext context) => AlertDialog(
title: const Text('Alert!'),
content: const Text('The alert description goes here.'),
actions: <Widget>[
diff --git a/packages/go_router/example/lib/books/src/screens/sign_in.dart b/packages/go_router/example/lib/books/src/screens/sign_in.dart
index 56b478b..808bc15 100644
--- a/packages/go_router/example/lib/books/src/screens/sign_in.dart
+++ b/packages/go_router/example/lib/books/src/screens/sign_in.dart
@@ -4,19 +4,27 @@
import 'package:flutter/material.dart';
+/// Credential data class.
class Credentials {
+ /// Creates a credential data object.
Credentials(this.username, this.password);
+ /// The username of the credentials.
final String username;
+
+ /// The password of the credentials.
final String password;
}
+/// The sign-in screen.
class SignInScreen extends StatefulWidget {
+ /// Creates a sign-in screen.
const SignInScreen({
required this.onSignIn,
Key? key,
}) : super(key: key);
+ /// Called when users sign in with [Credentials].
final ValueChanged<Credentials> onSignIn;
@override
@@ -24,8 +32,8 @@
}
class _SignInScreenState extends State<SignInScreen> {
- final _usernameController = TextEditingController();
- final _passwordController = TextEditingController();
+ final TextEditingController _usernameController = TextEditingController();
+ final TextEditingController _passwordController = TextEditingController();
@override
Widget build(BuildContext context) => Scaffold(
@@ -37,7 +45,7 @@
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
- children: [
+ children: <Widget>[
Text('Sign in', style: Theme.of(context).textTheme.headline4),
TextField(
decoration: const InputDecoration(labelText: 'Username'),
diff --git a/packages/go_router/example/lib/books/src/widgets/author_list.dart b/packages/go_router/example/lib/books/src/widgets/author_list.dart
index 91dd163..d749631 100644
--- a/packages/go_router/example/lib/books/src/widgets/author_list.dart
+++ b/packages/go_router/example/lib/books/src/widgets/author_list.dart
@@ -6,20 +6,25 @@
import '../data.dart';
+/// The author list view.
class AuthorList extends StatelessWidget {
+ /// Creates an [AuthorList].
const AuthorList({
required this.authors,
this.onTap,
Key? key,
}) : super(key: key);
+ /// The list of authors to be shown.
final List<Author> authors;
+
+ /// Called when the user taps an author.
final ValueChanged<Author>? onTap;
@override
Widget build(BuildContext context) => ListView.builder(
itemCount: authors.length,
- itemBuilder: (context, index) => ListTile(
+ itemBuilder: (BuildContext context, int index) => ListTile(
title: Text(
authors[index].name,
),
diff --git a/packages/go_router/example/lib/books/src/widgets/book_list.dart b/packages/go_router/example/lib/books/src/widgets/book_list.dart
index 7f07206..af37e15 100644
--- a/packages/go_router/example/lib/books/src/widgets/book_list.dart
+++ b/packages/go_router/example/lib/books/src/widgets/book_list.dart
@@ -6,20 +6,25 @@
import '../data.dart';
+/// The book list view.
class BookList extends StatelessWidget {
+ /// Creates an [BookList].
const BookList({
required this.books,
this.onTap,
Key? key,
}) : super(key: key);
+ /// The list of books to be displayed.
final List<Book> books;
+
+ /// Called when the user taps a book.
final ValueChanged<Book>? onTap;
@override
Widget build(BuildContext context) => ListView.builder(
itemCount: books.length,
- itemBuilder: (context, index) => ListTile(
+ itemBuilder: (BuildContext context, int index) => ListTile(
title: Text(
books[index].title,
),
diff --git a/packages/go_router/example/lib/cupertino.dart b/packages/go_router/example/lib/cupertino.dart
index 77fd28d..3511295 100644
--- a/packages/go_router/example/lib/cupertino.dart
+++ b/packages/go_router/example/lib/cupertino.dart
@@ -7,10 +7,13 @@
void main() => runApp(App());
+/// The main app
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Cupertino App';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Cupertino App';
@override
Widget build(BuildContext context) => CupertinoApp.router(
@@ -19,21 +22,25 @@
title: title,
);
- final _router = GoRouter(
- routes: [
+ final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
),
GoRoute(
path: '/page2',
- builder: (context, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
),
],
);
}
+/// The screen of the first page.
class Page1Screen extends StatelessWidget {
+ /// Creates a [Page1Screen].
const Page1Screen({Key? key}) : super(key: key);
@override
@@ -42,7 +49,7 @@
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
CupertinoButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
@@ -53,7 +60,9 @@
);
}
+/// The screen of the second page.
class Page2Screen extends StatelessWidget {
+ /// Creates a [Page2Screen].
const Page2Screen({Key? key}) : super(key: key);
@override
@@ -62,7 +71,7 @@
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
CupertinoButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
diff --git a/packages/go_router/example/lib/error_screen.dart b/packages/go_router/example/lib/error_screen.dart
index b872fed..984441b 100644
--- a/packages/go_router/example/lib/error_screen.dart
+++ b/packages/go_router/example/lib/error_screen.dart
@@ -7,10 +7,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Custom Error Screen';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Custom Error Screen';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -19,22 +22,27 @@
title: title,
);
- final _router = GoRouter(
- routes: [
+ final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
),
GoRoute(
path: '/page2',
- builder: (context, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
),
],
- errorBuilder: (context, state) => ErrorScreen(state.error!),
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ ErrorScreen(state.error!),
);
}
+/// The screen of the first page.
class Page1Screen extends StatelessWidget {
+ /// Creates a [Page1Screen].
const Page1Screen({Key? key}) : super(key: key);
@override
@@ -43,7 +51,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
@@ -54,7 +62,9 @@
);
}
+/// The screen of the second page.
class Page2Screen extends StatelessWidget {
+ /// Creates a [Page2Screen].
const Page2Screen({Key? key}) : super(key: key);
@override
@@ -63,7 +73,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
@@ -74,8 +84,12 @@
);
}
+/// The screen of the error page
class ErrorScreen extends StatelessWidget {
+ /// Creates an [ErrorScreen].
const ErrorScreen(this.error, {Key? key}) : super(key: key);
+
+ /// The error to display.
final Exception error;
@override
@@ -84,7 +98,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
SelectableText(error.toString()),
TextButton(
onPressed: () => context.go('/'),
diff --git a/packages/go_router/example/lib/extra_param.dart b/packages/go_router/example/lib/extra_param.dart
index 660a49b..5e9141a 100644
--- a/packages/go_router/example/lib/extra_param.dart
+++ b/packages/go_router/example/lib/extra_param.dart
@@ -10,14 +10,18 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Extra Parameter';
- static const alertOnWeb = true;
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Extra Parameter';
+
+ static const bool _alertOnWeb = true;
@override
- Widget build(BuildContext context) => alertOnWeb && kIsWeb
+ Widget build(BuildContext context) => _alertOnWeb && kIsWeb
? const MaterialApp(
title: title,
home: NoExtraParamOnWebScreen(),
@@ -28,29 +32,32 @@
title: title,
);
- late final _router = GoRouter(
- routes: [
+ late final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (context, state) => HomeScreen(families: Families.data),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ HomeScreen(families: Families.data),
+ routes: <GoRoute>[
GoRoute(
name: 'family',
path: 'family',
- builder: (context, state) {
- final params = state.extra! as Map<String, Object>;
- final family = params['family']! as Family;
+ builder: (BuildContext context, GoRouterState state) {
+ final Map<String, Object> params =
+ state.extra! as Map<String, Object>;
+ final Family family = params['family']! as Family;
return FamilyScreen(family: family);
},
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'person',
path: 'person',
- builder: (context, state) {
- final params = state.extra! as Map<String, Object>;
- final family = params['family']! as Family;
- final person = params['person']! as Person;
+ builder: (BuildContext context, GoRouterState state) {
+ final Map<String, Object> params =
+ state.extra! as Map<String, Object>;
+ final Family family = params['family']! as Family;
+ final Person person = params['person']! as Person;
return PersonScreen(family: family, person: person);
},
),
@@ -62,40 +69,49 @@
);
}
+/// The home screen that shows a list of families.
class HomeScreen extends StatelessWidget {
+ /// Creates a [HomeScreen].
const HomeScreen({required this.families, Key? key}) : super(key: key);
+
+ /// The list of families.
final List<Family> families;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text(App.title)),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
- onTap: () => context.goNamed('family', extra: {'family': f}),
+ onTap: () => context
+ .goNamed('family', extra: <String, Object?>{'family': f}),
)
],
),
);
}
+/// The screen that shows a list of persons in a family.
class FamilyScreen extends StatelessWidget {
+ /// Creates a [FamilyScreen].
const FamilyScreen({required this.family, Key? key}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
- children: [
- for (final p in family.people)
+ children: <Widget>[
+ for (final Person p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go(
context.namedLocation('person'),
- extra: {'family': family, 'person': p},
+ extra: <String, Object>{'family': family, 'person': p},
),
),
],
@@ -103,11 +119,16 @@
);
}
+/// The person screen.
class PersonScreen extends StatelessWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.family, required this.person, Key? key})
: super(key: key);
+ /// The family this person belong to.
final Family family;
+
+ /// The person to be displayed.
final Person person;
@override
@@ -117,7 +138,9 @@
);
}
+/// A screen that explains this example does not work on web platform.
class NoExtraParamOnWebScreen extends StatelessWidget {
+ /// Creates a [NoExtraParamOnWebScreen].
const NoExtraParamOnWebScreen({Key? key}) : super(key: key);
@override
@@ -126,7 +149,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: const [
+ children: const <Widget>[
Text("The `extra` param doesn't mix with the web:"),
Text("There's no support for the brower's Back button or"
' deep linking'),
diff --git a/packages/go_router/example/lib/init_loc.dart b/packages/go_router/example/lib/init_loc.dart
index 07442ff..4bef656 100644
--- a/packages/go_router/example/lib/init_loc.dart
+++ b/packages/go_router/example/lib/init_loc.dart
@@ -7,10 +7,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Initial Location';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Initial Location';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -19,26 +22,31 @@
title: title,
);
- final _router = GoRouter(
+ final GoRouter _router = GoRouter(
initialLocation: '/page3',
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
),
GoRoute(
path: '/page2',
- builder: (context, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
),
GoRoute(
path: '/page3',
- builder: (context, state) => const Page3Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page3Screen(),
),
],
);
}
+/// The screen of the first page.
class Page1Screen extends StatelessWidget {
+ /// Creates a [Page1Screen].
const Page1Screen({Key? key}) : super(key: key);
@override
@@ -47,7 +55,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
@@ -58,7 +66,9 @@
);
}
+/// The screen of the second page.
class Page2Screen extends StatelessWidget {
+ /// Creates a [Page2Screen].
const Page2Screen({Key? key}) : super(key: key);
@override
@@ -67,7 +77,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
@@ -78,7 +88,9 @@
);
}
+/// The screen of the third page.
class Page3Screen extends StatelessWidget {
+ /// Creates a [Page3Screen].
const Page3Screen({Key? key}) : super(key: key);
@override
@@ -87,7 +99,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
diff --git a/packages/go_router/example/lib/loading_page.dart b/packages/go_router/example/lib/loading_page.dart
index d6d4bb7..2afb21e 100644
--- a/packages/go_router/example/lib/loading_page.dart
+++ b/packages/go_router/example/lib/loading_page.dart
@@ -10,15 +10,21 @@
void main() => runApp(App());
+/// The app state data class.
class AppState extends ChangeNotifier {
+ /// Creates an [AppState].
AppState() {
loginInfo.addListener(loginChange);
repo.addListener(notifyListeners);
}
- final loginInfo = LoginInfo2();
- final repo = ValueNotifier<Repository2?>(null);
+ /// The login status.
+ final LoginInfo2 loginInfo = LoginInfo2();
+ /// The repository to query data from.
+ final ValueNotifier<Repository2?> repo = ValueNotifier<Repository2?>(null);
+
+ /// Called when login status changed.
Future<void> loginChange() async {
notifyListeners();
@@ -35,15 +41,19 @@
}
}
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Loading Page';
- final appState = AppState();
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Loading Page';
+
+ final AppState _appState = AppState();
@override
Widget build(BuildContext context) => ChangeNotifierProvider<AppState>.value(
- value: appState,
+ value: _appState,
child: MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
@@ -52,29 +62,34 @@
),
);
- late final _router = GoRouter(
- routes: [
+ late final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/login',
- builder: (context, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
GoRoute(
path: '/loading',
- builder: (context, state) => const LoadingScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoadingScreen(),
),
GoRoute(
path: '/',
- builder: (context, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
path: 'family/:fid',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(
fid: state.params['fid']!,
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- builder: (context, state) => PersonScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ PersonScreen(
fid: state.params['fid']!,
pid: state.params['pid']!,
),
@@ -84,46 +99,56 @@
],
),
],
- redirect: (state) {
+ redirect: (GoRouterState state) {
// if the user is not logged in, they need to login
- final loggedIn = appState.loginInfo.loggedIn;
- final loggingIn = state.subloc == '/login';
- final subloc = state.subloc;
- final fromp1 = subloc == '/' ? '' : '?from=$subloc';
- if (!loggedIn) return loggingIn ? null : '/login$fromp1';
+ final bool loggedIn = _appState.loginInfo.loggedIn;
+ final bool loggingIn = state.subloc == '/login';
+ final String subloc = state.subloc;
+ final String fromp1 = subloc == '/' ? '' : '?from=$subloc';
+ if (!loggedIn) {
+ return loggingIn ? null : '/login$fromp1';
+ }
// if the user is logged in but the repository is not loaded, they need to
// wait while it's loaded
- final loaded = appState.repo.value != null;
- final loading = state.subloc == '/loading';
- final from = state.queryParams['from'];
- final fromp2 = from == null ? '' : '?from=$from';
- if (!loaded) return loading ? null : '/loading$fromp2';
+ final bool loaded = _appState.repo.value != null;
+ final bool loading = state.subloc == '/loading';
+ final String? from = state.queryParams['from'];
+ final String fromp2 = from == null ? '' : '?from=$from';
+ if (!loaded) {
+ return loading ? null : '/loading$fromp2';
+ }
// if the user is logged in and the repository is loaded, send them where
// they were going before (or home if they weren't going anywhere)
- if (loggingIn || loading) return from ?? '/';
+ if (loggingIn || loading) {
+ return from ?? '/';
+ }
// no need to redirect at all
return null;
},
- refreshListenable: appState,
- navigatorBuilder: (context, state, child) =>
- appState.loginInfo.loggedIn ? AuthOverlay(child: child) : child,
+ refreshListenable: _appState,
+ navigatorBuilder:
+ (BuildContext context, GoRouterState state, Widget child) =>
+ _appState.loginInfo.loggedIn ? AuthOverlay(child: child) : child,
);
}
+/// A simple class for placing an exit button on top of all screens.
class AuthOverlay extends StatelessWidget {
+ /// Creates an [AuthOverlay].
const AuthOverlay({
required this.child,
Key? key,
}) : super(key: key);
+ /// The child subtree.
final Widget child;
@override
Widget build(BuildContext context) => Stack(
- children: [
+ children: <Widget>[
child,
Positioned(
top: 90,
@@ -142,7 +167,9 @@
);
}
+/// The login screen.
class LoginScreen extends StatefulWidget {
+ /// Creates a [LoginScreen].
const LoginScreen({Key? key}) : super(key: key);
@override
@@ -156,7 +183,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () async {
// ignore: unawaited_futures
@@ -170,9 +197,10 @@
);
}
+/// The loading screen.
class LoadingScreen extends StatelessWidget {
- const LoadingScreen({this.from, Key? key}) : super(key: key);
- final String? from;
+ /// Creates a [LoadingScreen].
+ const LoadingScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => Scaffold(
@@ -180,7 +208,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: const [
+ children: const <Widget>[
CircularProgressIndicator(),
Text('loading repository...'),
],
@@ -189,7 +217,9 @@
);
}
+/// The home screen.
class HomeScreen extends StatefulWidget {
+ /// Creates a [HomeScreen].
const HomeScreen({Key? key}) : super(key: key);
@override
@@ -219,13 +249,13 @@
@override
Widget build(BuildContext context) => MyFutureBuilder<List<Family>>(
future: _future,
- builder: (context, families) => Scaffold(
+ builder: (BuildContext context, List<Family> families) => Scaffold(
appBar: AppBar(
title: Text('${App.title}: ${families.length} families'),
),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
onTap: () => context.go('/family/${f.id}'),
@@ -236,8 +266,12 @@
);
}
+/// The family screen.
class FamilyScreen extends StatefulWidget {
+ /// Creates a [FamilyScreen].
const FamilyScreen({required this.fid, Key? key}) : super(key: key);
+
+ /// The family id.
final String fid;
@override
@@ -258,7 +292,9 @@
super.didUpdateWidget(oldWidget);
// refresh cached data
- if (oldWidget.fid != widget.fid) _fetch();
+ if (oldWidget.fid != widget.fid) {
+ _fetch();
+ }
}
void _fetch() => _future = _repo.getFamily(widget.fid);
@@ -267,11 +303,11 @@
@override
Widget build(BuildContext context) => MyFutureBuilder<Family>(
future: _future,
- builder: (context, family) => Scaffold(
+ builder: (BuildContext context, Family family) => Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
- children: [
- for (final p in family.people)
+ children: <Widget>[
+ for (final Person p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go(
@@ -284,11 +320,16 @@
);
}
+/// The person screen.
class PersonScreen extends StatefulWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.fid, required this.pid, Key? key})
: super(key: key);
+ /// The id of family the person belongs to.
final String fid;
+
+ /// The person id.
final String pid;
@override
@@ -309,7 +350,9 @@
super.didUpdateWidget(oldWidget);
// refresh cached data
- if (oldWidget.fid != widget.fid || oldWidget.pid != widget.pid) _fetch();
+ if (oldWidget.fid != widget.fid || oldWidget.pid != widget.pid) {
+ _fetch();
+ }
}
void _fetch() => _future = _repo.getPerson(widget.fid, widget.pid);
@@ -318,7 +361,7 @@
@override
Widget build(BuildContext context) => MyFutureBuilder<FamilyPerson>(
future: _future,
- builder: (context, famper) => Scaffold(
+ builder: (BuildContext context, FamilyPerson famper) => Scaffold(
appBar: AppBar(title: Text(famper.person.name)),
body: Text(
'${famper.person.name} ${famper.family.name} is '
@@ -328,17 +371,22 @@
);
}
+/// A custom [Future] builder.
class MyFutureBuilder<T extends Object> extends StatelessWidget {
+ /// Creates a [MyFutureBuilder].
const MyFutureBuilder({required this.future, required this.builder, Key? key})
: super(key: key);
+ /// The [Future] to depend on.
final Future<T>? future;
+
+ /// The builder that builds the widget subtree.
final Widget Function(BuildContext context, T data) builder;
@override
Widget build(BuildContext context) => FutureBuilder<T>(
future: future,
- builder: (context, snapshot) {
+ builder: (BuildContext context, AsyncSnapshot<T> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
appBar: AppBar(title: const Text('Loading...')),
@@ -359,17 +407,21 @@
);
}
+/// The error widget.
class SnapshotError extends StatelessWidget {
+ /// Creates a [SnapshotError].
SnapshotError(Object error, {Key? key})
: error = error is Exception ? error : Exception(error),
super(key: key);
+
+ /// The error to display.
final Exception error;
@override
Widget build(BuildContext context) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
SelectableText(error.toString()),
TextButton(
onPressed: () => context.go('/'),
diff --git a/packages/go_router/example/lib/main.dart b/packages/go_router/example/lib/main.dart
index bf2c5d4..ef33494 100644
--- a/packages/go_router/example/lib/main.dart
+++ b/packages/go_router/example/lib/main.dart
@@ -7,10 +7,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Declarative Routes';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Declarative Routes';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -19,21 +22,25 @@
title: title,
);
- final _router = GoRouter(
- routes: [
+ final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
),
GoRoute(
path: '/page2',
- builder: (context, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
),
],
);
}
+/// The screen of the first page.
class Page1Screen extends StatelessWidget {
+ /// Creates a [Page1Screen].
const Page1Screen({Key? key}) : super(key: key);
@override
@@ -42,7 +49,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
@@ -53,7 +60,9 @@
);
}
+/// The screen of the second page.
class Page2Screen extends StatelessWidget {
+ /// Creates a [Page2Screen].
const Page2Screen({Key? key}) : super(key: key);
@override
@@ -62,7 +71,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
diff --git a/packages/go_router/example/lib/named_routes.dart b/packages/go_router/example/lib/named_routes.dart
index 34bc16a..648610d 100644
--- a/packages/go_router/example/lib/named_routes.dart
+++ b/packages/go_router/example/lib/named_routes.dart
@@ -10,15 +10,19 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- final loginInfo = LoginInfo();
- static const title = 'GoRouter Example: Named Routes';
+ final LoginInfo _loginInfo = LoginInfo();
+
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Named Routes';
@override
Widget build(BuildContext context) => ChangeNotifierProvider<LoginInfo>.value(
- value: loginInfo,
+ value: _loginInfo,
child: MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
@@ -27,27 +31,29 @@
),
);
- late final _router = GoRouter(
+ late final GoRouter _router = GoRouter(
debugLogDiagnostics: true,
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (context, state) => HomeScreen(families: Families.data),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ HomeScreen(families: Families.data),
+ routes: <GoRoute>[
GoRoute(
name: 'family',
path: 'family/:fid',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(
family: Families.family(state.params['fid']!),
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'person',
path: 'person/:pid',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
- final person = family.person(state.params['pid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
+ final Person person = family.person(state.params['pid']!);
return PersonScreen(family: family, person: person);
},
),
@@ -58,54 +64,63 @@
GoRoute(
name: 'login',
path: '/login',
- builder: (context, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
// redirect to the login page if the user is not logged in
- redirect: (state) {
+ redirect: (GoRouterState state) {
// if the user is not logged in, they need to login
- final loggedIn = loginInfo.loggedIn;
- final loginloc = state.namedLocation('login');
- final loggingIn = state.subloc == loginloc;
+ final bool loggedIn = _loginInfo.loggedIn;
+ final String loginloc = state.namedLocation('login');
+ final bool loggingIn = state.subloc == loginloc;
// bundle the location the user is coming from into a query parameter
- final homeloc = state.namedLocation('home');
- final fromloc = state.subloc == homeloc ? '' : state.subloc;
+ final String homeloc = state.namedLocation('home');
+ final String fromloc = state.subloc == homeloc ? '' : state.subloc;
if (!loggedIn) {
return loggingIn
? null
: state.namedLocation(
'login',
- queryParams: {if (fromloc.isNotEmpty) 'from': fromloc},
+ queryParams: <String, String>{
+ if (fromloc.isNotEmpty) 'from': fromloc
+ },
);
}
// if the user is logged in, send them where they were going before (or
// home if they weren't going anywhere)
- if (loggingIn) return state.queryParams['from'] ?? homeloc;
+ if (loggingIn) {
+ return state.queryParams['from'] ?? homeloc;
+ }
// no need to redirect at all
return null;
},
// changes on the listenable will cause the router to refresh it's route
- refreshListenable: loginInfo,
+ refreshListenable: _loginInfo,
);
}
+/// The home screen that shows a list of families.
class HomeScreen extends StatelessWidget {
+ /// Creates a [HomeScreen].
const HomeScreen({required this.families, Key? key}) : super(key: key);
+
+ /// The list of families.
final List<Family> families;
@override
Widget build(BuildContext context) {
- final info = context.read<LoginInfo>();
+ final LoginInfo info = context.read<LoginInfo>();
return Scaffold(
appBar: AppBar(
title: const Text(App.title),
- actions: [
+ actions: <Widget>[
IconButton(
onPressed: info.logout,
tooltip: 'Logout: ${info.userName}',
@@ -114,11 +129,12 @@
],
),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
- onTap: () => context.goNamed('family', params: {'fid': f.id}),
+ onTap: () => context
+ .goNamed('family', params: <String, String>{'fid': f.id}),
)
],
),
@@ -126,22 +142,26 @@
}
}
+/// The screen that shows a list of persons in a family.
class FamilyScreen extends StatelessWidget {
+ /// Creates a [FamilyScreen].
const FamilyScreen({required this.family, Key? key}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
- children: [
- for (final p in family.people)
+ children: <Widget>[
+ for (final Person p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go(context.namedLocation(
'person',
- params: {'fid': family.id, 'pid': p.id},
- queryParams: {'qid': 'quid'},
+ params: <String, String>{'fid': family.id, 'pid': p.id},
+ queryParams: <String, String>{'qid': 'quid'},
)),
),
],
@@ -149,11 +169,16 @@
);
}
+/// The person screen.
class PersonScreen extends StatelessWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.family, required this.person, Key? key})
: super(key: key);
+ /// The family this person belong to.
final Family family;
+
+ /// The person to be displayed.
final Person person;
@override
@@ -163,7 +188,9 @@
);
}
+/// The login screen.
class LoginScreen extends StatelessWidget {
+ /// Creates a [LoginScreen].
const LoginScreen({Key? key}) : super(key: key);
@override
@@ -172,7 +199,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () {
// log a user in, letting all the listeners know
diff --git a/packages/go_router/example/lib/nav_builder.dart b/packages/go_router/example/lib/nav_builder.dart
index 2d532b3..ba8818f 100644
--- a/packages/go_router/example/lib/nav_builder.dart
+++ b/packages/go_router/example/lib/nav_builder.dart
@@ -10,11 +10,15 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- final loginInfo = LoginInfo();
- static const title = 'GoRouter Example: Navigator Builder';
+ final LoginInfo _loginInfo = LoginInfo();
+
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Navigator Builder';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -23,29 +27,29 @@
title: title,
);
- late final _router = GoRouter(
+ late final GoRouter _router = GoRouter(
debugLogDiagnostics: true,
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (context, state) =>
+ builder: (BuildContext context, GoRouterState state) =>
HomeScreenNoLogout(families: Families.data),
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'family',
path: 'family/:fid',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
return FamilyScreen(family: family);
},
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'person',
path: 'person/:pid',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
- final person = family.person(state.params['pid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
+ final Person person = family.person(state.params['pid']!);
return PersonScreen(family: family, person: person);
},
),
@@ -56,63 +60,71 @@
GoRoute(
name: 'login',
path: '/login',
- builder: (context, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
// redirect to the login page if the user is not logged in
- redirect: (state) {
+ redirect: (GoRouterState state) {
// if the user is not logged in, they need to login
- final loggedIn = loginInfo.loggedIn;
- final loginloc = state.namedLocation('login');
- final loggingIn = state.subloc == loginloc;
+ final bool loggedIn = _loginInfo.loggedIn;
+ final String loginloc = state.namedLocation('login');
+ final bool loggingIn = state.subloc == loginloc;
// bundle the location the user is coming from into a query parameter
- final homeloc = state.namedLocation('home');
- final fromloc = state.subloc == homeloc ? '' : state.subloc;
+ final String homeloc = state.namedLocation('home');
+ final String fromloc = state.subloc == homeloc ? '' : state.subloc;
if (!loggedIn) {
return loggingIn
? null
: state.namedLocation(
'login',
- queryParams: {if (fromloc.isNotEmpty) 'from': fromloc},
+ queryParams: <String, String>{
+ if (fromloc.isNotEmpty) 'from': fromloc
+ },
);
}
// if the user is logged in, send them where they were going before (or
// home if they weren't going anywhere)
- if (loggingIn) return state.queryParams['from'] ?? homeloc;
+ if (loggingIn) {
+ return state.queryParams['from'] ?? homeloc;
+ }
// no need to redirect at all
return null;
},
// changes on the listenable will cause the router to refresh it's route
- refreshListenable: loginInfo,
+ refreshListenable: _loginInfo,
// add a wrapper around the navigator to:
// - put loginInfo into the widget tree, and to
// - add an overlay to show a logout option
- navigatorBuilder: (context, state, child) =>
- ChangeNotifierProvider<LoginInfo>.value(
- value: loginInfo,
- builder: (context, _) {
+ navigatorBuilder:
+ (BuildContext context, GoRouterState state, Widget child) =>
+ ChangeNotifierProvider<LoginInfo>.value(
+ value: _loginInfo,
+ builder: (BuildContext context, Widget? _) {
debugPrint('navigatorBuilder: ${state.subloc}');
- return loginInfo.loggedIn ? AuthOverlay(child: child) : child;
+ return _loginInfo.loggedIn ? AuthOverlay(child: child) : child;
},
),
);
}
-// A simple class for placing an exit button on top of all screens
+/// A simple class for placing an exit button on top of all screens.
class AuthOverlay extends StatelessWidget {
+ /// Creates an [AuthOverlay].
const AuthOverlay({required this.child, Key? key}) : super(key: key);
+ /// The child subtree.
final Widget child;
@override
Widget build(BuildContext context) => Stack(
- children: [
+ children: <Widget>[
child,
Positioned(
top: 90,
@@ -129,36 +141,45 @@
);
}
+/// The home screen without a logout button.
class HomeScreenNoLogout extends StatelessWidget {
+ /// Creates a [HomeScreenNoLogout].
const HomeScreenNoLogout({required this.families, Key? key})
: super(key: key);
+
+ /// The list of families.
final List<Family> families;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text(App.title)),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
- onTap: () => context.goNamed('family', params: {'fid': f.id}),
+ onTap: () => context
+ .goNamed('family', params: <String, String>{'fid': f.id}),
)
],
),
);
}
+/// The screen that shows a list of persons in a family.
class FamilyScreen extends StatelessWidget {
+ /// Creates a [FamilyScreen].
const FamilyScreen({required this.family, Key? key}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
- children: [
- for (final p in family.people)
+ children: <Widget>[
+ for (final Person p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go('/family/${family.id}/person/${p.id}'),
@@ -168,11 +189,16 @@
);
}
+/// The person screen.
class PersonScreen extends StatelessWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.family, required this.person, Key? key})
: super(key: key);
+ /// The family this person belong to.
final Family family;
+
+ /// The person to be displayed.
final Person person;
@override
@@ -182,7 +208,9 @@
);
}
+/// The login screen.
class LoginScreen extends StatelessWidget {
+ /// Creates a [LoginScreen].
const LoginScreen({Key? key}) : super(key: key);
@override
@@ -191,7 +219,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () {
// log a user in, letting all the listeners know
diff --git a/packages/go_router/example/lib/nav_observer.dart b/packages/go_router/example/lib/nav_observer.dart
index c3dc7a7..51cf45a 100644
--- a/packages/go_router/example/lib/nav_observer.dart
+++ b/packages/go_router/example/lib/nav_observer.dart
@@ -8,10 +8,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Navigator Observer';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Navigator Observer';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -20,23 +23,26 @@
title: title,
);
- final _router = GoRouter(
- observers: [MyNavObserver()],
- routes: [
+ final GoRouter _router = GoRouter(
+ observers: <NavigatorObserver>[MyNavObserver()],
+ routes: <GoRoute>[
GoRoute(
// if there's no name, path will be used as name for observers
path: '/',
- builder: (context, state) => const Page1Screen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
+ routes: <GoRoute>[
GoRoute(
name: 'page2',
path: 'page2/:p1',
- builder: (context, state) => const Page2Screen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
+ routes: <GoRoute>[
GoRoute(
name: 'page3',
path: 'page3',
- builder: (context, state) => const Page3Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page3Screen(),
),
],
),
@@ -46,12 +52,15 @@
);
}
+/// The Navigator observer.
class MyNavObserver extends NavigatorObserver {
+ /// Creates a [MyNavObserver].
MyNavObserver() {
- log.onRecord.listen((e) => debugPrint('$e'));
+ log.onRecord.listen((LogRecord e) => debugPrint('$e'));
}
- final log = Logger('MyNavObserver');
+ /// The logged message.
+ final Logger log = Logger('MyNavObserver');
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) =>
@@ -85,7 +94,9 @@
String get str => 'route(${settings.name}: ${settings.arguments})';
}
+/// The screen of the first page.
class Page1Screen extends StatelessWidget {
+ /// Creates a [Page1Screen].
const Page1Screen({Key? key}) : super(key: key);
@override
@@ -94,12 +105,12 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.goNamed(
'page2',
- params: {'p1': 'pv1'},
- queryParams: {'q1': 'qv1'},
+ params: <String, String>{'p1': 'pv1'},
+ queryParams: <String, String>{'q1': 'qv1'},
),
child: const Text('Go to page 2'),
),
@@ -109,7 +120,9 @@
);
}
+/// The screen of the second page.
class Page2Screen extends StatelessWidget {
+ /// Creates a [Page2Screen].
const Page2Screen({Key? key}) : super(key: key);
@override
@@ -118,11 +131,11 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.goNamed(
'page3',
- params: {'p1': 'pv2'},
+ params: <String, String>{'p1': 'pv2'},
),
child: const Text('Go to page 3'),
),
@@ -132,7 +145,9 @@
);
}
+/// The screen of the third page.
class Page3Screen extends StatelessWidget {
+ /// Creates a [Page3Screen].
const Page3Screen({Key? key}) : super(key: key);
@override
@@ -141,7 +156,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
diff --git a/packages/go_router/example/lib/nested_nav.dart b/packages/go_router/example/lib/nested_nav.dart
index 45846b0..5940f73 100644
--- a/packages/go_router/example/lib/nested_nav.dart
+++ b/packages/go_router/example/lib/nested_nav.dart
@@ -9,10 +9,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Nested Navigation';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Nested Navigation';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -21,24 +24,25 @@
title: title,
);
- late final _router = GoRouter(
- routes: [
+ late final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
redirect: (_) => '/family/${Families.data[0].id}',
),
GoRoute(
path: '/family/:fid',
- builder: (context, state) => FamilyTabsScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyTabsScreen(
key: state.pageKey,
selectedFamily: Families.family(state.params['fid']!),
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
- final person = family.person(state.params['pid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
+ final Person person = family.person(state.params['pid']!);
return PersonScreen(family: family, person: person);
},
@@ -50,9 +54,10 @@
// show the current router location as the user navigates page to page; note
// that this is not required for nested navigation but it is useful to show
// the location as it changes
- navigatorBuilder: (context, state, child) => Material(
+ navigatorBuilder:
+ (BuildContext context, GoRouterState state, Widget child) => Material(
child: Column(
- children: [
+ children: <Widget>[
Expanded(child: child),
Padding(
padding: const EdgeInsets.all(8),
@@ -64,13 +69,17 @@
);
}
+/// The family tabs screen.
class FamilyTabsScreen extends StatefulWidget {
+ /// Creates a [FamilyTabsScreen].
FamilyTabsScreen({required Family selectedFamily, Key? key})
- : index = Families.data.indexWhere((f) => f.id == selectedFamily.id),
+ : index =
+ Families.data.indexWhere((Family f) => f.id == selectedFamily.id),
super(key: key) {
assert(index != -1);
}
+ /// The tab index.
final int index;
@override
@@ -109,13 +118,17 @@
title: const Text(App.title),
bottom: TabBar(
controller: _controller,
- tabs: [for (final f in Families.data) Tab(text: f.name)],
- onTap: (index) => _tap(context, index),
+ tabs: <Tab>[
+ for (final Family f in Families.data) Tab(text: f.name)
+ ],
+ onTap: (int index) => _tap(context, index),
),
),
body: TabBarView(
controller: _controller,
- children: [for (final f in Families.data) FamilyView(family: f)],
+ children: <Widget>[
+ for (final Family f in Families.data) FamilyView(family: f)
+ ],
),
);
@@ -123,8 +136,12 @@
context.go('/family/${Families.data[index].id}');
}
+/// The family view.
class FamilyView extends StatefulWidget {
+ /// Creates a [FamilyView].
const FamilyView({required this.family, Key? key}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
@@ -151,8 +168,8 @@
// Call `super.build` when using `AutomaticKeepAliveClientMixin`.
super.build(context);
return ListView(
- children: [
- for (final p in widget.family.people)
+ children: <Widget>[
+ for (final Person p in widget.family.people)
ListTile(
title: Text(p.name),
onTap: () =>
@@ -163,11 +180,16 @@
}
}
+/// The person screen.
class PersonScreen extends StatelessWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.family, required this.person, Key? key})
: super(key: key);
+ /// The family this person belong to.
final Family family;
+
+ /// The person to be displayed.
final Person person;
@override
diff --git a/packages/go_router/example/lib/push.dart b/packages/go_router/example/lib/push.dart
index 97cd03c..9d24495 100644
--- a/packages/go_router/example/lib/push.dart
+++ b/packages/go_router/example/lib/push.dart
@@ -7,10 +7,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Push';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Push';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -19,15 +22,17 @@
title: title,
);
- late final _router = GoRouter(
- routes: [
+ late final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const Page1ScreenWithPush(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1ScreenWithPush(),
),
GoRoute(
path: '/page2',
- builder: (context, state) => Page2ScreenWithPush(
+ builder: (BuildContext context, GoRouterState state) =>
+ Page2ScreenWithPush(
int.parse(state.queryParams['push-count']!),
),
),
@@ -35,7 +40,9 @@
);
}
+/// The screen of the first page.
class Page1ScreenWithPush extends StatelessWidget {
+ /// Creates a [Page1ScreenWithPush].
const Page1ScreenWithPush({Key? key}) : super(key: key);
@override
@@ -44,7 +51,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.push('/page2?push-count=1'),
child: const Text('Push page 2'),
@@ -55,8 +62,12 @@
);
}
+/// The screen of the second page.
class Page2ScreenWithPush extends StatelessWidget {
+ /// Creates a [Page2ScreenWithPush].
const Page2ScreenWithPush(this.pushCount, {Key? key}) : super(key: key);
+
+ /// The push count.
final int pushCount;
@override
@@ -67,7 +78,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
Padding(
padding: const EdgeInsets.all(8),
child: ElevatedButton(
diff --git a/packages/go_router/example/lib/query_params.dart b/packages/go_router/example/lib/query_params.dart
index f3fc748..9a660ae 100644
--- a/packages/go_router/example/lib/query_params.dart
+++ b/packages/go_router/example/lib/query_params.dart
@@ -10,16 +10,20 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- final loginInfo = LoginInfo();
- static const title = 'GoRouter Example: Query Parameters';
+ final LoginInfo _loginInfo = LoginInfo();
+
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Query Parameters';
// add the login info into the tree as app state that can change over time
@override
Widget build(BuildContext context) => ChangeNotifierProvider<LoginInfo>.value(
- value: loginInfo,
+ value: _loginInfo,
child: MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
@@ -28,23 +32,25 @@
),
);
- late final _router = GoRouter(
- routes: [
+ late final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => HomeScreen(families: Families.data),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ HomeScreen(families: Families.data),
+ routes: <GoRoute>[
GoRoute(
path: 'family/:fid',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(
family: Families.family(state.params['fid']!),
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
- final person = family.person(state.params['pid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
+ final Person person = family.person(state.params['pid']!);
return PersonScreen(family: family, person: person);
},
),
@@ -54,45 +60,54 @@
),
GoRoute(
path: '/login',
- builder: (context, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
// redirect to the login page if the user is not logged in
- redirect: (state) {
+ redirect: (GoRouterState state) {
// if the user is not logged in, they need to login
- final loggedIn = loginInfo.loggedIn;
- final loggingIn = state.subloc == '/login';
+ final bool loggedIn = _loginInfo.loggedIn;
+ final bool loggingIn = state.subloc == '/login';
// bundle the location the user is coming from into a query parameter
- final fromp = state.subloc == '/' ? '' : '?from=${state.subloc}';
- if (!loggedIn) return loggingIn ? null : '/login$fromp';
+ final String fromp = state.subloc == '/' ? '' : '?from=${state.subloc}';
+ if (!loggedIn) {
+ return loggingIn ? null : '/login$fromp';
+ }
// if the user is logged in, send them where they were going before (or
// home if they weren't going anywhere)
- if (loggingIn) return state.queryParams['from'] ?? '/';
+ if (loggingIn) {
+ return state.queryParams['from'] ?? '/';
+ }
// no need to redirect at all
return null;
},
// changes on the listenable will cause the router to refresh it's route
- refreshListenable: loginInfo,
+ refreshListenable: _loginInfo,
);
}
+/// The home screen that shows a list of families.
class HomeScreen extends StatelessWidget {
+ /// Creates a [HomeScreen].
const HomeScreen({required this.families, Key? key}) : super(key: key);
+
+ /// The list of families.
final List<Family> families;
@override
Widget build(BuildContext context) {
- final info = context.read<LoginInfo>();
+ final LoginInfo info = context.read<LoginInfo>();
return Scaffold(
appBar: AppBar(
title: const Text(App.title),
- actions: [
+ actions: <Widget>[
IconButton(
onPressed: () {
info.logout();
@@ -104,8 +119,8 @@
],
),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
onTap: () => context.go('/family/${f.id}'),
@@ -116,16 +131,20 @@
}
}
+/// The screen that shows a list of persons in a family.
class FamilyScreen extends StatelessWidget {
+ /// Creates a [FamilyScreen].
const FamilyScreen({required this.family, Key? key}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
- children: [
- for (final p in family.people)
+ children: <Widget>[
+ for (final Person p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go('/family/${family.id}/person/${p.id}'),
@@ -135,11 +154,16 @@
);
}
+/// The person screen.
class PersonScreen extends StatelessWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.family, required this.person, Key? key})
: super(key: key);
+ /// The family this person belong to.
final Family family;
+
+ /// The person to be displayed.
final Person person;
@override
@@ -149,7 +173,9 @@
);
}
+/// The login screen.
class LoginScreen extends StatelessWidget {
+ /// Creates a [LoginScreen].
const LoginScreen({Key? key}) : super(key: key);
@override
@@ -158,7 +184,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () {
// log a user in, letting all the listeners know
diff --git a/packages/go_router/example/lib/redirection.dart b/packages/go_router/example/lib/redirection.dart
index c358554..395e727 100644
--- a/packages/go_router/example/lib/redirection.dart
+++ b/packages/go_router/example/lib/redirection.dart
@@ -10,16 +10,20 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- final loginInfo = LoginInfo();
- static const title = 'GoRouter Example: Redirection';
+ final LoginInfo _loginInfo = LoginInfo();
+
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Redirection';
// add the login info into the tree as app state that can change over time
@override
Widget build(BuildContext context) => ChangeNotifierProvider<LoginInfo>.value(
- value: loginInfo,
+ value: _loginInfo,
child: MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
@@ -28,23 +32,25 @@
),
);
- late final _router = GoRouter(
- routes: [
+ late final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => HomeScreen(families: Families.data),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ HomeScreen(families: Families.data),
+ routes: <GoRoute>[
GoRoute(
path: 'family/:fid',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(
family: Families.family(state.params['fid']!),
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
- final person = family.person(state.params['pid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
+ final Person person = family.person(state.params['pid']!);
return PersonScreen(family: family, person: person);
},
),
@@ -54,31 +60,38 @@
),
GoRoute(
path: '/login',
- builder: (context, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
// redirect to the login page if the user is not logged in
- redirect: (state) {
+ redirect: (GoRouterState state) {
// if the user is not logged in, they need to login
- final loggedIn = loginInfo.loggedIn;
- final loggingIn = state.subloc == '/login';
- if (!loggedIn) return loggingIn ? null : '/login';
+ final bool loggedIn = _loginInfo.loggedIn;
+ final bool loggingIn = state.subloc == '/login';
+ if (!loggedIn) {
+ return loggingIn ? null : '/login';
+ }
// if the user is logged in but still on the login page, send them to
// the home page
- if (loggingIn) return '/';
+ if (loggingIn) {
+ return '/';
+ }
// no need to redirect at all
return null;
},
// changes on the listenable will cause the router to refresh it's route
- refreshListenable: loginInfo,
+ refreshListenable: _loginInfo,
);
}
+/// The login screen.
class LoginScreen extends StatelessWidget {
+ /// Creates a [LoginScreen].
const LoginScreen({Key? key}) : super(key: key);
@override
@@ -87,7 +100,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () {
// log a user in, letting all the listeners know
@@ -105,18 +118,22 @@
);
}
+/// The home screen that shows a list of families.
class HomeScreen extends StatelessWidget {
+ /// Creates a [HomeScreen].
const HomeScreen({required this.families, Key? key}) : super(key: key);
+
+ /// The list of families.
final List<Family> families;
@override
Widget build(BuildContext context) {
- final info = context.read<LoginInfo>();
+ final LoginInfo info = context.read<LoginInfo>();
return Scaffold(
appBar: AppBar(
title: const Text(App.title),
- actions: [
+ actions: <Widget>[
IconButton(
onPressed: info.logout,
tooltip: 'Logout: ${info.userName}',
@@ -125,8 +142,8 @@
],
),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
onTap: () => context.go('/family/${f.id}'),
@@ -137,16 +154,20 @@
}
}
+/// The screen that shows a list of persons in a family.
class FamilyScreen extends StatelessWidget {
+ /// Creates a [FamilyScreen].
const FamilyScreen({required this.family, Key? key}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
- children: [
- for (final p in family.people)
+ children: <Widget>[
+ for (final Person p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go('/family/${family.id}/person/${p.id}'),
@@ -156,11 +177,16 @@
);
}
+/// The person screen.
class PersonScreen extends StatelessWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.family, required this.person, Key? key})
: super(key: key);
+ /// The family this person belong to.
final Family family;
+
+ /// The person to be displayed.
final Person person;
@override
diff --git a/packages/go_router/example/lib/router_neglect.dart b/packages/go_router/example/lib/router_neglect.dart
index 119d922..0e3ebac 100644
--- a/packages/go_router/example/lib/router_neglect.dart
+++ b/packages/go_router/example/lib/router_neglect.dart
@@ -7,10 +7,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Router neglect';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Router neglect';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -19,23 +22,27 @@
title: title,
);
- final _router = GoRouter(
+ final GoRouter _router = GoRouter(
// turn off history tracking in the browser for this navigation
routerNeglect: true,
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
),
GoRoute(
path: '/page2',
- builder: (context, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
),
],
);
}
+/// The screen of the first page.
class Page1Screen extends StatelessWidget {
+ /// Creates a [Page1Screen].
const Page1Screen({Key? key}) : super(key: key);
@override
@@ -44,7 +51,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
@@ -66,7 +73,9 @@
);
}
+/// The screen of the second page.
class Page2Screen extends StatelessWidget {
+ /// Creates a [Page2Screen].
const Page2Screen({Key? key}) : super(key: key);
@override
@@ -75,7 +84,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
diff --git a/packages/go_router/example/lib/router_stream_refresh.dart b/packages/go_router/example/lib/router_stream_refresh.dart
index 8271a36..cb4607e 100644
--- a/packages/go_router/example/lib/router_stream_refresh.dart
+++ b/packages/go_router/example/lib/router_stream_refresh.dart
@@ -10,10 +10,13 @@
void main() => runApp(const App());
+/// The main app.
class App extends StatefulWidget {
+ /// Creates an [App].
const App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Stream Refresh';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Stream Refresh';
@override
State<App> createState() => _AppState();
@@ -27,22 +30,24 @@
void initState() {
loggedInState = LoggedInState.seeded(false);
router = GoRouter(
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => HomeScreen(families: Families.data),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ HomeScreen(families: Families.data),
+ routes: <GoRoute>[
GoRoute(
path: 'family/:fid',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(
family: Families.family(state.params['fid']!),
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
- final person = family.person(state.params['pid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
+ final Person person = family.person(state.params['pid']!);
return PersonScreen(family: family, person: person);
},
),
@@ -52,23 +57,28 @@
),
GoRoute(
path: '/login',
- builder: (context, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
// redirect to the login page if the user is not logged in
- redirect: (state) {
+ redirect: (GoRouterState state) {
// if the user is not logged in, they need to login
- final loggedIn = loggedInState.state;
- final loggingIn = state.subloc == '/login';
+ final bool loggedIn = loggedInState.state;
+ final bool loggingIn = state.subloc == '/login';
// bundle the location the user is coming from into a query parameter
- final fromp = state.subloc == '/' ? '' : '?from=${state.subloc}';
- if (!loggedIn) return loggingIn ? null : '/login$fromp';
+ final String fromp = state.subloc == '/' ? '' : '?from=${state.subloc}';
+ if (!loggedIn) {
+ return loggingIn ? null : '/login$fromp';
+ }
// if the user is logged in, send them where they were going before (or
// home if they weren't going anywhere)
- if (loggingIn) return state.queryParams['from'] ?? '/';
+ if (loggingIn) {
+ return state.queryParams['from'] ?? '/';
+ }
// no need to redirect at all
return null;
@@ -98,22 +108,25 @@
}
}
+/// The home screen that shows a list of families.
class HomeScreen extends StatelessWidget {
+ /// Creates a [HomeScreen].
const HomeScreen({
required this.families,
Key? key,
}) : super(key: key);
+ /// The list of families.
final List<Family> families;
@override
Widget build(BuildContext context) {
- final info = context.read<LoggedInState>();
+ final LoggedInState info = context.read<LoggedInState>();
return Scaffold(
appBar: AppBar(
title: const Text(App.title),
- actions: [
+ actions: <Widget>[
IconButton(
onPressed: () => info.emit(false),
icon: const Icon(Icons.logout),
@@ -121,8 +134,8 @@
],
),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
onTap: () => context.go('/family/${f.id}'),
@@ -133,19 +146,23 @@
}
}
+/// The screen that shows a list of persons in a family.
class FamilyScreen extends StatelessWidget {
+ /// Creates a [FamilyScreen].
const FamilyScreen({
required this.family,
Key? key,
}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
- children: [
- for (final p in family.people)
+ children: <Widget>[
+ for (final Person p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go('/family/${family.id}/person/${p.id}'),
@@ -155,14 +172,19 @@
);
}
+/// The person screen.
class PersonScreen extends StatelessWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({
required this.family,
required this.person,
Key? key,
}) : super(key: key);
+ /// The family this person belong to.
final Family family;
+
+ /// The person to be displayed.
final Person person;
@override
@@ -172,7 +194,9 @@
);
}
+/// The login screen.
class LoginScreen extends StatelessWidget {
+ /// Creates a [LoginScreen].
const LoginScreen({Key? key}) : super(key: key);
@override
@@ -181,7 +205,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () {
// log a user in, letting all the listeners know
diff --git a/packages/go_router/example/lib/shared/data.dart b/packages/go_router/example/lib/shared/data.dart
index db7dff4..a9a84b7 100644
--- a/packages/go_router/example/lib/shared/data.dart
+++ b/packages/go_router/example/lib/shared/data.dart
@@ -7,33 +7,52 @@
import 'package:flutter/foundation.dart';
+/// Person data class.
class Person {
+ /// Creates a [Person].
Person({required this.id, required this.name, required this.age});
+ /// The id of the person.
final String id;
+
+ /// The name of the person.
final String name;
+
+ /// The age of the person.
final int age;
}
+/// Family data class.
class Family {
+ /// Creates a [Family].
Family({required this.id, required this.name, required this.people});
+ /// The id of the family.
final String id;
+
+ /// The name of the family.
final String name;
+
+ /// The list of [Person]s in the family.
final List<Person> people;
+ /// Gets the [Person] with the given id in this family.
Person person(String pid) => people.singleWhere(
- (p) => p.id == pid,
+ (Person p) => p.id == pid,
orElse: () => throw Exception('unknown person $pid for family $id'),
);
}
+/// The mock of families data.
class Families {
- static final data = [
+ Families._();
+
+ /// The data.
+ static final List<Family> data = <Family>[
Family(
id: 'f1',
name: 'Sells',
- people: [
+ people: <Person>[
Person(id: 'p1', name: 'Chris', age: 52),
Person(id: 'p2', name: 'John', age: 27),
Person(id: 'p3', name: 'Tom', age: 26),
@@ -42,7 +61,7 @@
Family(
id: 'f2',
name: 'Addams',
- people: [
+ people: <Person>[
Person(id: 'p1', name: 'Gomez', age: 55),
Person(id: 'p2', name: 'Morticia', age: 50),
Person(id: 'p3', name: 'Pugsley', age: 10),
@@ -52,7 +71,7 @@
Family(
id: 'f3',
name: 'Hunting',
- people: [
+ people: <Person>[
Person(id: 'p1', name: 'Mom', age: 54),
Person(id: 'p2', name: 'Dad', age: 55),
Person(id: 'p3', name: 'Will', age: 20),
@@ -72,43 +91,56 @@
),
];
+ /// Looks up a family in the data.
static Family family(String fid) => data.family(fid);
}
extension on List<Family> {
Family family(String fid) => singleWhere(
- (f) => f.id == fid,
+ (Family f) => f.id == fid,
orElse: () => throw Exception('unknown family $fid'),
);
}
+/// The login information.
class LoginInfo extends ChangeNotifier {
- var _userName = '';
+ /// The username of login.
String get userName => _userName;
+ String _userName = '';
+
+ /// Whether a user has logged in.
bool get loggedIn => _userName.isNotEmpty;
+ /// Logs in a user.
void login(String userName) {
_userName = userName;
notifyListeners();
}
+ /// Logs out the current user.
void logout() {
_userName = '';
notifyListeners();
}
}
+/// The login information.
class LoginInfo2 extends ChangeNotifier {
- var _userName = '';
+ /// The username of login.
String get userName => _userName;
+ String _userName = '';
+
+ /// Whether a user has logged in.
bool get loggedIn => _userName.isNotEmpty;
+ /// Logs in a user.
Future<void> login(String userName) async {
_userName = userName;
notifyListeners();
await Future<void>.delayed(const Duration(microseconds: 2500));
}
+ /// Logs out the current user.
Future<void> logout() async {
_userName = '';
notifyListeners();
@@ -116,16 +148,24 @@
}
}
+/// The relation of a person in a family.
class FamilyPerson {
+ /// Creates a [FamilyPerson].
FamilyPerson({required this.family, required this.person});
+ /// The family.
final Family family;
+
+ /// the person.
final Person person;
}
+/// The repository.
class Repository {
- static final rnd = Random();
+ /// A random number generator.
+ static final Random rnd = Random();
+ /// Gets the families data.
Future<List<Family>> getFamilies() async {
// simulate network delay
await Future<void>.delayed(const Duration(seconds: 1));
@@ -137,27 +177,35 @@
return Families.data;
}
+ /// Gets a family from the repository.
Future<Family> getFamily(String fid) async =>
(await getFamilies()).family(fid);
+ /// Gets a person from the repository.
Future<FamilyPerson> getPerson(String fid, String pid) async {
- final family = await getFamily(fid);
+ final Family family = await getFamily(fid);
return FamilyPerson(family: family, person: family.person(pid));
}
}
+/// The repository.
class Repository2 {
Repository2._(this.userName);
+
+ /// The username.
final String userName;
+ /// Gets a repository with the username.
static Future<Repository2> get(String userName) async {
// simulate network delay
await Future<void>.delayed(const Duration(seconds: 1));
return Repository2._(userName);
}
- static final rnd = Random();
+ /// A random number generator.
+ static final Random rnd = Random();
+ /// Gets the families data.
Future<List<Family>> getFamilies() async {
// simulate network delay
await Future<void>.delayed(const Duration(seconds: 1));
@@ -169,40 +217,52 @@
return Families.data;
}
+ /// Gets a family from the repository.
Future<Family> getFamily(String fid) async =>
(await getFamilies()).family(fid);
+ /// Gets a person from the repository.
Future<FamilyPerson> getPerson(String fid, String pid) async {
- final family = await getFamily(fid);
+ final Family family = await getFamily(fid);
return FamilyPerson(family: family, person: family.person(pid));
}
}
+/// A state stream.
abstract class StateStream<T> {
+ /// Creates a [StateStream].
StateStream();
+ /// Creates a [StateStream] with an initial value.
StateStream.seeded(T value) : state = value {
_controller.add(value);
}
final StreamController<T> _controller = StreamController<T>();
+
+ /// The state.
late T state;
+ /// The [Stream] object.
Stream<T> get stream => _controller.stream;
+ /// Pipes a new state into the stream.
void emit(T state) {
this.state = state;
_controller.add(state);
}
+ /// Disposes the stream.
void dispose() {
_controller.close();
}
}
+/// Stream for whether the user is currently logged in.
class LoggedInState extends StateStream<bool> {
+ /// Creates a [LoggedInState].
LoggedInState();
- // ignore: avoid_positional_boolean_parameters
+ /// Creates a [LoggedInState] with an initial value.
LoggedInState.seeded(bool value) : super.seeded(value);
}
diff --git a/packages/go_router/example/lib/shared_scaffold.dart b/packages/go_router/example/lib/shared_scaffold.dart
index 2675ff6..8699f67 100644
--- a/packages/go_router/example/lib/shared_scaffold.dart
+++ b/packages/go_router/example/lib/shared_scaffold.dart
@@ -9,10 +9,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Shared Scaffold';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Shared Scaffold';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -21,30 +24,34 @@
title: title,
);
- late final _router = GoRouter(
+ late final GoRouter _router = GoRouter(
debugLogDiagnostics: true,
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => _build(const Page1View()),
+ builder: (BuildContext context, GoRouterState state) =>
+ _build(const Page1View()),
),
GoRoute(
path: '/page2',
- builder: (context, state) => _build(const Page2View()),
+ builder: (BuildContext context, GoRouterState state) =>
+ _build(const Page2View()),
),
],
- errorBuilder: (context, state) => _build(ErrorView(state.error!)),
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ _build(ErrorView(state.error!)),
// use the navigatorBuilder to keep the SharedScaffold from being animated
// as new pages as shown; wrappiong that in single-page Navigator at the
// root provides an Overlay needed for the adaptive navigation scaffold and
// a root Navigator to show the About box
- navigatorBuilder: (context, state, child) => Navigator(
- onPopPage: (route, dynamic result) {
+ navigatorBuilder:
+ (BuildContext context, GoRouterState state, Widget child) => Navigator(
+ onPopPage: (Route<dynamic> route, dynamic result) {
route.didPop(result);
return false; // don't pop the single page on the root navigator
},
- pages: [
+ pages: <Page<dynamic>>[
MaterialPage<void>(
child: state.error != null
? ErrorScaffold(body: child)
@@ -62,14 +69,19 @@
Widget _build(Widget child) => Scaffold(body: child);
}
+/// A scaffold with multiple pages.
class SharedScaffold extends StatefulWidget {
+ /// Creates a shared scaffold.
const SharedScaffold({
required this.selectedIndex,
required this.body,
Key? key,
}) : super(key: key);
+ /// The selected index
final int selectedIndex;
+
+ /// The body of the page.
final Widget body;
@override
@@ -80,17 +92,19 @@
@override
Widget build(BuildContext context) => AdaptiveNavigationScaffold(
selectedIndex: widget.selectedIndex,
- destinations: const [
+ destinations: const <AdaptiveScaffoldDestination>[
AdaptiveScaffoldDestination(title: 'Page 1', icon: Icons.first_page),
AdaptiveScaffoldDestination(title: 'Page 2', icon: Icons.last_page),
AdaptiveScaffoldDestination(title: 'About', icon: Icons.info),
],
appBar: AdaptiveAppBar(title: const Text(App.title)),
- navigationTypeResolver: (context) =>
+ navigationTypeResolver: (BuildContext context) =>
_drawerSize ? NavigationType.drawer : NavigationType.bottom,
- onDestinationSelected: (index) async {
+ onDestinationSelected: (int index) async {
// if there's a drawer, close it
- if (_drawerSize) Navigator.pop(context);
+ if (_drawerSize) {
+ Navigator.pop(context);
+ }
switch (index) {
case 0:
@@ -100,7 +114,7 @@
context.go('/page2');
break;
case 2:
- final packageInfo = await PackageInfo.fromPlatform();
+ final PackageInfo packageInfo = await PackageInfo.fromPlatform();
showAboutDialog(
context: context,
applicationName: packageInfo.appName,
@@ -118,14 +132,16 @@
bool get _drawerSize => MediaQuery.of(context).size.width >= 600;
}
+/// The content of the first page.
class Page1View extends StatelessWidget {
+ /// Creates a [Page1View].
const Page1View({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
@@ -135,14 +151,16 @@
);
}
+/// The content of the second page.
class Page2View extends StatelessWidget {
+ /// Creates a [Page2View].
const Page2View({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
@@ -152,13 +170,17 @@
);
}
+/// The error scaffold.
class ErrorScaffold extends StatelessWidget {
+ /// Creates an [ErrorScaffold]
const ErrorScaffold({
required this.body,
Key? key,
}) : super(key: key);
+ /// The body of this scaffold.
final Widget body;
+
@override
Widget build(BuildContext context) => Scaffold(
appBar: AdaptiveAppBar(title: const Text('Page Not Found')),
@@ -166,15 +188,19 @@
);
}
+/// A view to display error message.
class ErrorView extends StatelessWidget {
+ /// Creates an [ErrorView].
const ErrorView(this.error, {Key? key}) : super(key: key);
+
+ /// The error to display.
final Exception error;
@override
Widget build(BuildContext context) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
SelectableText(error.toString()),
TextButton(
onPressed: () => context.go('/'),
diff --git a/packages/go_router/example/lib/state_restoration.dart b/packages/go_router/example/lib/state_restoration.dart
index bff77aa..695fa86 100644
--- a/packages/go_router/example/lib/state_restoration.dart
+++ b/packages/go_router/example/lib/state_restoration.dart
@@ -9,10 +9,13 @@
const RootRestorationScope(restorationId: 'root', child: App()),
);
+/// The main app.
class App extends StatefulWidget {
+ /// Creates an [App].
const App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: State Restoration';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: State Restoration';
@override
State<App> createState() => _AppState();
@@ -35,25 +38,29 @@
restorationScopeId: 'app',
);
- final _router = GoRouter(
- routes: [
+ final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
// restorationId set for the route automatically
GoRoute(
path: '/',
- builder: (context, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
),
// restorationId set for the route automatically
GoRoute(
path: '/page2',
- builder: (context, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
),
],
restorationScopeId: 'router',
);
}
+/// The screen of the first page.
class Page1Screen extends StatelessWidget {
+ /// Creates a [Page1Screen].
const Page1Screen({Key? key}) : super(key: key);
@override
@@ -62,7 +69,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
@@ -73,7 +80,9 @@
);
}
+/// The screen of the second page.
class Page2Screen extends StatelessWidget {
+ /// Creates a [Page2Screen].
const Page2Screen({Key? key}) : super(key: key);
@override
@@ -82,7 +91,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
diff --git a/packages/go_router/example/lib/sub_routes.dart b/packages/go_router/example/lib/sub_routes.dart
index 956876a..204fc30 100644
--- a/packages/go_router/example/lib/sub_routes.dart
+++ b/packages/go_router/example/lib/sub_routes.dart
@@ -9,10 +9,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Sub-routes';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Sub-routes';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -21,23 +24,25 @@
title: title,
);
- final _router = GoRouter(
- routes: [
+ final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => HomeScreen(families: Families.data),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ HomeScreen(families: Families.data),
+ routes: <GoRoute>[
GoRoute(
path: 'family/:fid',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(
family: Families.family(state.params['fid']!),
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
- final person = family.person(state.params['pid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
+ final Person person = family.person(state.params['pid']!);
return PersonScreen(family: family, person: person);
},
@@ -50,16 +55,20 @@
);
}
+/// The home screen that shows a list of families.
class HomeScreen extends StatelessWidget {
+ /// Creates a [HomeScreen].
const HomeScreen({required this.families, Key? key}) : super(key: key);
+
+ /// The list of families.
final List<Family> families;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text(App.title)),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
onTap: () => context.go('/family/${f.id}'),
@@ -69,16 +78,20 @@
);
}
+/// The screen that shows a list of persons in a family.
class FamilyScreen extends StatelessWidget {
+ /// Creates a [FamilyScreen].
const FamilyScreen({required this.family, Key? key}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
- children: [
- for (final p in family.people)
+ children: <Widget>[
+ for (final Person p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go('/family/${family.id}/person/${p.id}'),
@@ -88,11 +101,16 @@
);
}
+/// The person screen.
class PersonScreen extends StatelessWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.family, required this.person, Key? key})
: super(key: key);
+ /// The family this person belong to.
final Family family;
+
+ /// The person to be displayed.
final Person person;
@override
diff --git a/packages/go_router/example/lib/transitions.dart b/packages/go_router/example/lib/transitions.dart
index 4750a03..2ccbcef 100644
--- a/packages/go_router/example/lib/transitions.dart
+++ b/packages/go_router/example/lib/transitions.dart
@@ -7,10 +7,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Custom Transitions';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Custom Transitions';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -19,70 +22,88 @@
title: title,
);
- final _router = GoRouter(
- routes: [
+ final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
redirect: (_) => '/none',
),
GoRoute(
path: '/fade',
- pageBuilder: (context, state) => CustomTransitionPage<void>(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ CustomTransitionPage<void>(
key: state.pageKey,
child: const ExampleTransitionsScreen(
kind: 'fade',
color: Colors.red,
),
- transitionsBuilder: (context, animation, secondaryAnimation, child) =>
+ transitionsBuilder: (BuildContext context,
+ Animation<double> animation,
+ Animation<double> secondaryAnimation,
+ Widget child) =>
FadeTransition(opacity: animation, child: child),
),
),
GoRoute(
path: '/scale',
- pageBuilder: (context, state) => CustomTransitionPage<void>(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ CustomTransitionPage<void>(
key: state.pageKey,
child: const ExampleTransitionsScreen(
kind: 'scale',
color: Colors.green,
),
- transitionsBuilder: (context, animation, secondaryAnimation, child) =>
+ transitionsBuilder: (BuildContext context,
+ Animation<double> animation,
+ Animation<double> secondaryAnimation,
+ Widget child) =>
ScaleTransition(scale: animation, child: child),
),
),
GoRoute(
path: '/slide',
- pageBuilder: (context, state) => CustomTransitionPage<void>(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ CustomTransitionPage<void>(
key: state.pageKey,
child: const ExampleTransitionsScreen(
kind: 'slide',
color: Colors.yellow,
),
- transitionsBuilder: (context, animation, secondaryAnimation, child) =>
+ transitionsBuilder: (BuildContext context,
+ Animation<double> animation,
+ Animation<double> secondaryAnimation,
+ Widget child) =>
SlideTransition(
- position: animation.drive(
- Tween<Offset>(
- begin: const Offset(0.25, 0.25),
- end: Offset.zero,
- ).chain(CurveTween(curve: Curves.easeIn)),
- ),
- child: child),
+ position: animation.drive(
+ Tween<Offset>(
+ begin: const Offset(0.25, 0.25),
+ end: Offset.zero,
+ ).chain(CurveTween(curve: Curves.easeIn)),
+ ),
+ child: child,
+ ),
),
),
GoRoute(
path: '/rotation',
- pageBuilder: (context, state) => CustomTransitionPage<void>(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ CustomTransitionPage<void>(
key: state.pageKey,
child: const ExampleTransitionsScreen(
kind: 'rotation',
color: Colors.purple,
),
- transitionsBuilder: (context, animation, secondaryAnimation, child) =>
+ transitionsBuilder: (BuildContext context,
+ Animation<double> animation,
+ Animation<double> secondaryAnimation,
+ Widget child) =>
RotationTransition(turns: animation, child: child),
),
),
GoRoute(
path: '/none',
- pageBuilder: (context, state) => NoTransitionPage<void>(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ NoTransitionPage<void>(
key: state.pageKey,
child: const ExampleTransitionsScreen(
kind: 'none',
@@ -94,15 +115,28 @@
);
}
+/// An Example transitions screen.
class ExampleTransitionsScreen extends StatelessWidget {
+ /// Creates an [ExampleTransitionsScreen].
const ExampleTransitionsScreen({
required this.color,
required this.kind,
Key? key,
}) : super(key: key);
- static final kinds = ['fade', 'scale', 'slide', 'rotation', 'none'];
+ /// The available transition kinds.
+ static final List<String> kinds = <String>[
+ 'fade',
+ 'scale',
+ 'slide',
+ 'rotation',
+ 'none'
+ ];
+
+ /// The color of the container.
final Color color;
+
+ /// The transition kind
final String kind;
@override
@@ -113,8 +147,8 @@
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
- for (final kind in kinds)
+ children: <Widget>[
+ for (final String kind in kinds)
Padding(
padding: const EdgeInsets.all(8),
child: ElevatedButton(
diff --git a/packages/go_router/example/lib/url_strategy.dart b/packages/go_router/example/lib/url_strategy.dart
index 91fbcc1..f3b618a 100644
--- a/packages/go_router/example/lib/url_strategy.dart
+++ b/packages/go_router/example/lib/url_strategy.dart
@@ -15,10 +15,13 @@
runApp(App());
}
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: URL Path Strategy';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: URL Path Strategy';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -27,24 +30,28 @@
title: App.title,
);
- final _router = GoRouter(
+ final GoRouter _router = GoRouter(
// turn off the # in the URLs on the web
urlPathStrategy: UrlPathStrategy.path,
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
),
GoRoute(
path: '/page2',
- builder: (context, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
),
],
);
}
+/// The screen of the first page.
class Page1Screen extends StatelessWidget {
+ /// Creates a [Page1Screen].
const Page1Screen({Key? key}) : super(key: key);
@override
@@ -53,7 +60,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
@@ -64,7 +71,9 @@
);
}
+/// The screen of the second page.
class Page2Screen extends StatelessWidget {
+ /// Creates a [Page2Screen].
const Page2Screen({Key? key}) : super(key: key);
@override
@@ -73,7 +82,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
diff --git a/packages/go_router/example/lib/user_input.dart b/packages/go_router/example/lib/user_input.dart
index 26af9d2..3054cb7 100644
--- a/packages/go_router/example/lib/user_input.dart
+++ b/packages/go_router/example/lib/user_input.dart
@@ -10,10 +10,13 @@
void main() => runApp(App());
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: Navigator Integration';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: Navigator Integration';
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -23,34 +26,36 @@
debugShowCheckedModeBanner: false,
);
- late final _router = GoRouter(
- routes: [
+ late final GoRouter _router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (context, state) => HomeScreen(families: Families.data),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ HomeScreen(families: Families.data),
+ routes: <GoRoute>[
GoRoute(
name: 'family',
path: 'family/:fid',
- builder: (context, state) => FamilyScreenWithAdd(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreenWithAdd(
family: Families.family(state.params['fid']!),
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'person',
path: 'person/:pid',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
- final person = family.person(state.params['pid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
+ final Person person = family.person(state.params['pid']!);
return PersonScreen(family: family, person: person);
},
),
GoRoute(
name: 'new-person',
path: 'new-person',
- builder: (context, state) {
- final family = Families.family(state.params['fid']!);
+ builder: (BuildContext context, GoRouterState state) {
+ final Family family = Families.family(state.params['fid']!);
return NewPersonScreen2(family: family);
},
),
@@ -62,27 +67,36 @@
);
}
+/// The home screen that shows a list of families.
class HomeScreen extends StatelessWidget {
+ /// Creates a [HomeScreen].
const HomeScreen({required this.families, Key? key}) : super(key: key);
+
+ /// The list of families.
final List<Family> families;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text(App.title)),
body: ListView(
- children: [
- for (final f in families)
+ children: <Widget>[
+ for (final Family f in families)
ListTile(
title: Text(f.name),
- onTap: () => context.goNamed('family', params: {'fid': f.id}),
+ onTap: () => context
+ .goNamed('family', params: <String, String>{'fid': f.id}),
)
],
),
);
}
+/// The family screen.
class FamilyScreenWithAdd extends StatefulWidget {
+ /// Creates a [FamilyScreenWithAdd].
const FamilyScreenWithAdd({required this.family, Key? key}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
@@ -94,7 +108,7 @@
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(widget.family.name),
- actions: [
+ actions: <Widget>[
IconButton(
// onPressed: () => _addPerson1(context), // Navigator-style
onPressed: () => _addPerson2(context), // GoRouter-style
@@ -104,14 +118,17 @@
],
),
body: ListView(
- children: [
- for (final p in widget.family.people)
+ children: <Widget>[
+ for (final Person p in widget.family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go(context.namedLocation(
'person',
- params: {'fid': widget.family.id, 'pid': p.id},
- queryParams: {'qid': 'quid'},
+ params: <String, String>{
+ 'fid': widget.family.id,
+ 'pid': p.id
+ },
+ queryParams: <String, String>{'qid': 'quid'},
)),
),
],
@@ -121,10 +138,11 @@
// using a Navigator and a Navigator result
// ignore: unused_element
Future<void> _addPerson1(BuildContext context) async {
- final person = await Navigator.push<Person>(
+ final Person? person = await Navigator.push<Person>(
context,
- MaterialPageRoute(
- builder: (context) => NewPersonScreen1(family: widget.family),
+ MaterialPageRoute<Person>(
+ builder: (BuildContext context) =>
+ NewPersonScreen1(family: widget.family),
),
);
@@ -132,7 +150,7 @@
setState(() => widget.family.people.add(person));
// ignore: use_build_context_synchronously
- context.goNamed('person', params: {
+ context.goNamed('person', params: <String, String>{
'fid': widget.family.id,
'pid': person.id,
});
@@ -141,15 +159,21 @@
// using a GoRouter page
void _addPerson2(BuildContext context) {
- context.goNamed('new-person', params: {'fid': widget.family.id});
+ context.goNamed('new-person',
+ params: <String, String>{'fid': widget.family.id});
}
}
+/// The person screen.
class PersonScreen extends StatelessWidget {
+ /// Creates a [PersonScreen].
const PersonScreen({required this.family, required this.person, Key? key})
: super(key: key);
+ /// The family this person belong to.
final Family family;
+
+ /// The person to be displayed.
final Person person;
@override
@@ -160,8 +184,12 @@
}
// returning a Navigator result
+/// The screen to add a new person into the family.
class NewPersonScreen1 extends StatefulWidget {
+ /// Creates a [NewPersonScreen1].
const NewPersonScreen1({required this.family, Key? key}) : super(key: key);
+
+ /// The family to be added to.
final Family family;
@override
@@ -169,9 +197,9 @@
}
class _NewPersonScreen1State extends State<NewPersonScreen1> {
- final _formKey = GlobalKey<FormState>();
- final _nameController = TextEditingController();
- final _ageController = TextEditingController();
+ final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
+ final TextEditingController _nameController = TextEditingController();
+ final TextEditingController _ageController = TextEditingController();
@override
void dispose() {
@@ -195,24 +223,25 @@
width: 400,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
- children: [
+ children: <Widget>[
TextFormField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'name'),
- validator: (value) => value == null || value.isEmpty
- ? 'Please enter a name'
- : null,
+ validator: (String? value) =>
+ value == null || value.isEmpty
+ ? 'Please enter a name'
+ : null,
),
TextFormField(
controller: _ageController,
decoration: const InputDecoration(labelText: 'age'),
- validator: (value) => value == null ||
+ validator: (String? value) => value == null ||
value.isEmpty ||
int.tryParse(value) == null
? 'Please enter an age'
: null,
),
- ButtonBar(children: [
+ ButtonBar(children: <Widget>[
TextButton(
onPressed: () async {
// ask the user if they'd like to adandon their data
@@ -225,7 +254,7 @@
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
- final person = Person(
+ final Person person = Person(
id: 'p${widget.family.people.length + 1}',
name: _nameController.text,
age: int.parse(_ageController.text),
@@ -246,7 +275,7 @@
);
Future<bool> abandonNewPerson(BuildContext context) async {
- final result = await showOkCancelAlertDialog(
+ final OkCancelResult result = await showOkCancelAlertDialog(
context: context,
title: 'Abandon New Person',
message: 'Are you sure you abandon this new person?',
@@ -259,8 +288,12 @@
}
// adding the result to the data directly (GoRouter page)
+/// The screen to add a new person into the family.
class NewPersonScreen2 extends StatefulWidget {
+ /// Creates a [NewPersonScreen1].
const NewPersonScreen2({required this.family, Key? key}) : super(key: key);
+
+ /// The family to display.
final Family family;
@override
@@ -268,9 +301,9 @@
}
class _NewPersonScreen2State extends State<NewPersonScreen2> {
- final _formKey = GlobalKey<FormState>();
- final _nameController = TextEditingController();
- final _ageController = TextEditingController();
+ final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
+ final TextEditingController _nameController = TextEditingController();
+ final TextEditingController _ageController = TextEditingController();
@override
void dispose() {
@@ -294,24 +327,25 @@
width: 400,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
- children: [
+ children: <Widget>[
TextFormField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'name'),
- validator: (value) => value == null || value.isEmpty
- ? 'Please enter a name'
- : null,
+ validator: (String? value) =>
+ value == null || value.isEmpty
+ ? 'Please enter a name'
+ : null,
),
TextFormField(
controller: _ageController,
decoration: const InputDecoration(labelText: 'age'),
- validator: (value) => value == null ||
+ validator: (String? value) => value == null ||
value.isEmpty ||
int.tryParse(value) == null
? 'Please enter an age'
: null,
),
- ButtonBar(children: [
+ ButtonBar(children: <Widget>[
TextButton(
onPressed: () async {
// ask the user if they'd like to adandon their data
@@ -325,7 +359,7 @@
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
- final person = Person(
+ final Person person = Person(
id: 'p${widget.family.people.length + 1}',
name: _nameController.text,
age: int.parse(_ageController.text),
@@ -333,7 +367,7 @@
widget.family.people.add(person);
- context.goNamed('person', params: {
+ context.goNamed('person', params: <String, String>{
'fid': widget.family.id,
'pid': person.id,
});
@@ -351,7 +385,7 @@
);
Future<bool> abandonNewPerson(BuildContext context) async {
- final result = await showOkCancelAlertDialog(
+ final OkCancelResult result = await showOkCancelAlertDialog(
context: context,
title: 'Abandon New Person',
message: 'Are you sure you abandon this new person?',
diff --git a/packages/go_router/example/lib/widgets_app.dart b/packages/go_router/example/lib/widgets_app.dart
index 595bb66..6474f3c 100644
--- a/packages/go_router/example/lib/widgets_app.dart
+++ b/packages/go_router/example/lib/widgets_app.dart
@@ -8,39 +8,46 @@
void main() => runApp(App());
-const blue = Color(0xFF2196F3);
-const white = Color(0xFFFFFFFF);
+const Color _kBlue = Color(0xFF2196F3);
+const Color _kWhite = Color(0xFFFFFFFF);
+/// The main app.
class App extends StatelessWidget {
+ /// Creates an [App].
App({Key? key}) : super(key: key);
- static const title = 'GoRouter Example: WidgetsApp';
+ /// The title of the app.
+ static const String title = 'GoRouter Example: WidgetsApp';
@override
Widget build(BuildContext context) => WidgetsApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
title: title,
- color: blue,
- textStyle: const TextStyle(color: blue),
+ color: _kBlue,
+ textStyle: const TextStyle(color: _kBlue),
);
- final _router = GoRouter(
+ final GoRouter _router = GoRouter(
debugLogDiagnostics: true,
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
),
GoRoute(
path: '/page2',
- builder: (context, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
),
],
);
}
+/// The screen of the first page.
class Page1Screen extends StatelessWidget {
+ /// Creates a [Page1Screen].
const Page1Screen({Key? key}) : super(key: key);
@override
@@ -48,7 +55,7 @@
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
const Text(
App.title,
style: TextStyle(fontWeight: FontWeight.bold),
@@ -58,7 +65,7 @@
onPressed: () => context.go('/page2'),
child: const Text(
'Go to page 2',
- style: TextStyle(color: white),
+ style: TextStyle(color: _kWhite),
),
),
],
@@ -67,7 +74,9 @@
);
}
+/// The screen of the second page.
class Page2Screen extends StatelessWidget {
+ /// Creates a [Page2Screen].
const Page2Screen({Key? key}) : super(key: key);
@override
@@ -75,7 +84,7 @@
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
const Text(
App.title,
style: TextStyle(fontWeight: FontWeight.bold),
@@ -85,7 +94,7 @@
onPressed: () => context.go('/'),
child: const Text(
'Go to home page',
- style: TextStyle(color: white),
+ style: TextStyle(color: _kWhite),
),
),
],
@@ -94,14 +103,19 @@
);
}
+/// A custom button.
class Button extends StatelessWidget {
+ /// Creates a [Button].
const Button({
required this.onPressed,
required this.child,
Key? key,
}) : super(key: key);
+ /// Called when user pressed the button.
final VoidCallback onPressed;
+
+ /// The child subtree.
final Widget child;
@override
@@ -109,7 +123,7 @@
onTap: onPressed,
child: Container(
padding: const EdgeInsets.all(8),
- color: blue,
+ color: _kBlue,
child: child,
),
);
diff --git a/packages/go_router/lib/go_router.dart b/packages/go_router/lib/go_router.dart
index 5a3150e..b331c61 100644
--- a/packages/go_router/lib/go_router.dart
+++ b/packages/go_router/lib/go_router.dart
@@ -26,8 +26,8 @@
/// Get a location from route name and parameters.
String namedLocation(
String name, {
- Map<String, String> params = const {},
- Map<String, String> queryParams = const {},
+ Map<String, String> params = const <String, String>{},
+ Map<String, String> queryParams = const <String, String>{},
}) =>
GoRouter.of(this)
.namedLocation(name, params: params, queryParams: queryParams);
@@ -39,8 +39,8 @@
/// Navigate to a named route.
void goNamed(
String name, {
- Map<String, String> params = const {},
- Map<String, String> queryParams = const {},
+ Map<String, String> params = const <String, String>{},
+ Map<String, String> queryParams = const <String, String>{},
Object? extra,
}) =>
GoRouter.of(this).goNamed(
@@ -57,8 +57,8 @@
/// Navigate to a named route onto the page stack.
void pushNamed(
String name, {
- Map<String, String> params = const {},
- Map<String, String> queryParams = const {},
+ Map<String, String> params = const <String, String>{},
+ Map<String, String> queryParams = const <String, String>{},
Object? extra,
}) =>
GoRouter.of(this).pushNamed(
diff --git a/packages/go_router/lib/src/go_route.dart b/packages/go_router/lib/src/go_route.dart
index e58dbdf..43c9123 100644
--- a/packages/go_router/lib/src/go_route.dart
+++ b/packages/go_router/lib/src/go_route.dart
@@ -19,7 +19,7 @@
this.name,
this.pageBuilder,
this.builder = _builder,
- this.routes = const [],
+ this.routes = const <GoRoute>[],
this.redirect = _redirect,
}) {
if (path.isEmpty) {
@@ -34,9 +34,12 @@
_pathRE = patternToRegExp(path, _pathParams);
// check path params
- final groupedParams = _pathParams.groupListsBy((p) => p);
- final dupParams = Map<String, List<String>>.fromEntries(
- groupedParams.entries.where((e) => e.value.length > 1),
+ final Map<String, List<String>> groupedParams =
+ _pathParams.groupListsBy<String>((String p) => p);
+ final Map<String, List<String>> dupParams =
+ Map<String, List<String>>.fromEntries(
+ groupedParams.entries
+ .where((MapEntry<String, List<String>> e) => e.value.length > 1),
);
if (dupParams.isNotEmpty) {
throw Exception(
@@ -45,7 +48,7 @@
}
// check sub-routes
- for (final route in routes) {
+ for (final GoRoute route in routes) {
// check paths
if (route.path != '/' &&
(route.path.startsWith('/') || route.path.endsWith('/'))) {
@@ -56,7 +59,7 @@
}
}
- final _pathParams = <String>[];
+ final List<String> _pathParams = <String>[];
late final RegExp _pathRE;
/// Optional name of the route.
@@ -70,7 +73,7 @@
/// ```
/// GoRoute(
/// path: '/',
- /// pageBuilder: (context, state) => MaterialPage<void>(
+ /// pageBuilder: (BuildContext context, GoRouterState state) => MaterialPage<void>(
/// key: state.pageKey,
/// child: HomePage(families: Families.data),
/// ),
@@ -84,7 +87,7 @@
/// ```
/// GoRoute(
/// path: '/',
- /// pageBuilder: (context, state) => MaterialPage<void>(
+ /// pageBuilder: (BuildContext context, GoRouterState state) => MaterialPage<void>(
/// key: state.pageKey,
/// child: HomePage(families: Families.data),
/// ),
@@ -101,7 +104,7 @@
/// ```
/// GoRoute(
/// path: '/',
- /// builder: (context, state) => FamilyPage(
+ /// builder: (BuildContext context, GoRouterState state) => FamilyPage(
/// families: Families.family(
/// state.params['id'],
/// ),
@@ -126,30 +129,30 @@
/// Can be represented as:
///
/// ```
- /// final _router = GoRouter(
- /// routes: [
+ /// final GoRouter _router = GoRouter(
+ /// routes: <GoRoute>[
/// GoRoute(
/// path: '/',
- /// pageBuilder: (context, state) => MaterialPage<void>(
+ /// pageBuilder: (BuildContext context, GoRouterState state) => MaterialPage<void>(
/// key: state.pageKey,
/// child: HomePage(families: Families.data),
/// ),
- /// routes: [
+ /// routes: <GoRoute>[
/// GoRoute(
/// path: 'family/:fid',
- /// pageBuilder: (context, state) {
- /// final family = Families.family(state.params['fid']!);
+ /// pageBuilder: (BuildContext context, GoRouterState state) {
+ /// final Family family = Families.family(state.params['fid']!);
/// return MaterialPage<void>(
/// key: state.pageKey,
/// child: FamilyPage(family: family),
/// );
/// },
- /// routes: [
+ /// routes: <GoRoute>[
/// GoRoute(
/// path: 'person/:pid',
- /// pageBuilder: (context, state) {
- /// final family = Families.family(state.params['fid']!);
- /// final person = family.person(state.params['pid']!);
+ /// pageBuilder: (BuildContext context, GoRouterState state) {
+ /// final Family family = Families.family(state.params['fid']!);
+ /// final Person person = family.person(state.params['pid']!);
/// return MaterialPage<void>(
/// key: state.pageKey,
/// child: PersonPage(family: family, person: person),
@@ -173,15 +176,15 @@
///
/// For example:
/// ```
- /// final _router = GoRouter(
- /// routes: [
+ /// final GoRouter _router = GoRouter(
+ /// routes: <GoRoute>[
/// GoRoute(
/// path: '/',
/// redirect: (_) => '/family/${Families.data[0].id}',
/// ),
/// GoRoute(
/// path: '/family/:fid',
- /// pageBuilder: (context, state) => ...,
+ /// pageBuilder: (BuildContext context, GoRouterState state) => ...,
/// ),
/// ],
/// );
diff --git a/packages/go_router/lib/src/go_route_information_parser.dart b/packages/go_router/lib/src/go_route_information_parser.dart
index c38b559..d5a7b15 100644
--- a/packages/go_router/lib/src/go_route_information_parser.dart
+++ b/packages/go_router/lib/src/go_route_information_parser.dart
@@ -14,7 +14,7 @@
) =>
// Use [SynchronousFuture] so that the initial url is processed
// synchronously and remove unwanted initial animations on deep-linking
- SynchronousFuture(Uri.parse(routeInformation.location!));
+ SynchronousFuture<Uri>(Uri.parse(routeInformation.location!));
/// for use by the Router architecture as part of the RouteInformationParser
@override
diff --git a/packages/go_router/lib/src/go_route_match.dart b/packages/go_router/lib/src/go_route_match.dart
index d126fcb..e489043 100644
--- a/packages/go_router/lib/src/go_route_match.dart
+++ b/packages/go_router/lib/src/go_route_match.dart
@@ -27,7 +27,7 @@
assert(fullpath.startsWith('/')),
assert(Uri.parse(fullpath).queryParameters.isEmpty) {
if (kDebugMode) {
- for (final p in encodedParams.entries) {
+ 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}"');
}
@@ -47,27 +47,27 @@
assert(route.name!.toLowerCase() == name.toLowerCase());
// check that we have all the params we need
- final paramNames = <String>[];
+ final List<String> paramNames = <String>[];
patternToRegExp(fullpath, paramNames);
- for (final paramName in paramNames) {
+ for (final String paramName in paramNames) {
if (!params.containsKey(paramName)) {
throw Exception('missing param "$paramName" for $fullpath');
}
}
// check that we have don't have extra params
- for (final key in params.keys) {
+ for (final String key in params.keys) {
if (!paramNames.contains(key)) {
throw Exception('unknown param "$key" for $fullpath');
}
}
- final encodedParams = {
- for (final param in params.entries)
+ final Map<String, String> encodedParams = <String, String>{
+ for (final MapEntry<String, String> param in params.entries)
param.key: Uri.encodeComponent(param.value)
};
- final subloc = _locationFor(fullpath, encodedParams);
+ final String subloc = _locationFor(fullpath, encodedParams);
return GoRouteMatch(
route: route,
subloc: subloc,
@@ -91,12 +91,14 @@
}) {
assert(!path.contains('//'));
- final match = route.matchPatternAsPrefix(restLoc);
- if (match == null) return null;
+ final RegExpMatch? match = route.matchPatternAsPrefix(restLoc);
+ if (match == null) {
+ return null;
+ }
- final encodedParams = route.extractPathParams(match);
- final pathLoc = _locationFor(path, encodedParams);
- final subloc = GoRouterDelegate.fullLocFor(parentSubloc, pathLoc);
+ 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,
@@ -133,8 +135,8 @@
final ValueKey<String>? pageKey;
/// Parameters for the matched route, URI-decoded.
- Map<String, String> get decodedParams => {
- for (final param in encodedParams.entries)
+ Map<String, String> get decodedParams => <String, String>{
+ for (final MapEntry<String, String> param in encodedParams.entries)
param.key: Uri.decodeComponent(param.value)
};
diff --git a/packages/go_router/lib/src/go_router.dart b/packages/go_router/lib/src/go_router.dart
index ccc3b47..7ed7425 100644
--- a/packages/go_router/lib/src/go_router.dart
+++ b/packages/go_router/lib/src/go_router.dart
@@ -7,6 +7,7 @@
import 'go_route.dart';
import 'go_route_information_parser.dart';
import 'go_router_delegate.dart';
+import 'go_router_state.dart';
import 'inherited_go_router.dart';
import 'logging.dart';
import 'path_strategy_nonweb.dart'
@@ -36,7 +37,9 @@
GoRouterNavigatorBuilder? navigatorBuilder,
String? restorationScopeId,
}) {
- if (urlPathStrategy != null) setUrlPathStrategy(urlPathStrategy);
+ if (urlPathStrategy != null) {
+ setUrlPathStrategy(urlPathStrategy);
+ }
setLogging(enabled: debugLogDiagnostics);
@@ -49,12 +52,17 @@
refreshListenable: refreshListenable,
routerNeglect: routerNeglect,
initUri: Uri.parse(initialLocation),
- observers: [...observers ?? [], this],
+ observers: <NavigatorObserver>[
+ ...observers ?? <NavigatorObserver>[],
+ this
+ ],
debugLogDiagnostics: debugLogDiagnostics,
restorationScopeId: restorationScopeId,
// wrap the returned Navigator to enable GoRouter.of(context).go() et al,
// allowing the caller to wrap the navigator themselves
- builderWithNav: (context, state, nav) => InheritedGoRouter(
+ builderWithNav:
+ (BuildContext context, GoRouterState state, Navigator nav) =>
+ InheritedGoRouter(
goRouter: this,
child: navigatorBuilder?.call(context, state, nav) ?? nav,
),
@@ -62,7 +70,8 @@
}
/// The route information parser used by the go router.
- final routeInformationParser = GoRouteInformationParser();
+ final GoRouteInformationParser routeInformationParser =
+ GoRouteInformationParser();
/// The router delegate used by the go router.
late final GoRouterDelegate routerDelegate;
@@ -74,8 +83,8 @@
/// This is useful for redirecting to a named location.
String namedLocation(
String name, {
- Map<String, String> params = const {},
- Map<String, String> queryParams = const {},
+ Map<String, String> params = const <String, String>{},
+ Map<String, String> queryParams = const <String, String>{},
}) =>
routerDelegate.namedLocation(
name,
@@ -93,8 +102,8 @@
/// Navigate to the named route.
void goNamed(
String name, {
- Map<String, String> params = const {},
- Map<String, String> queryParams = const {},
+ Map<String, String> params = const <String, String>{},
+ Map<String, String> queryParams = const <String, String>{},
Object? extra,
}) =>
go(
@@ -111,8 +120,8 @@
/// `name='person', params={'fid': 'f2', 'pid': 'p1'}`
void pushNamed(
String name, {
- Map<String, String> params = const {},
- Map<String, String> queryParams = const {},
+ Map<String, String> params = const <String, String>{},
+ Map<String, String> queryParams = const <String, String>{},
Object? extra,
}) =>
push(
@@ -132,7 +141,7 @@
/// Find the current GoRouter in the widget tree.
static GoRouter of(BuildContext context) {
- final inherited =
+ final InheritedGoRouter? inherited =
context.dependOnInheritedWidgetOfExactType<InheritedGoRouter>();
assert(inherited != null, 'No GoRouter found in context');
return inherited!.goRouter;
diff --git a/packages/go_router/lib/src/go_router_cupertino.dart b/packages/go_router/lib/src/go_router_cupertino.dart
index a356ffb..0314718 100644
--- a/packages/go_router/lib/src/go_router_cupertino.dart
+++ b/packages/go_router/lib/src/go_router_cupertino.dart
@@ -42,7 +42,7 @@
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
Text(error?.toString() ?? 'page not found'),
CupertinoButton(
onPressed: () => context.go('/'),
diff --git a/packages/go_router/lib/src/go_router_delegate.dart b/packages/go_router/lib/src/go_router_delegate.dart
index 38d2fee..791fb77 100644
--- a/packages/go_router/lib/src/go_router_delegate.dart
+++ b/packages/go_router/lib/src/go_router_delegate.dart
@@ -40,7 +40,7 @@
this.restorationScopeId,
}) {
// check top-level route paths are valid
- for (final route in routes) {
+ for (final GoRoute route in routes) {
if (!route.path.startsWith('/')) {
throw Exception('top-level path must start with "/": ${route.path}');
}
@@ -95,21 +95,21 @@
/// its history.
final String? restorationScopeId;
- final _key = GlobalKey<NavigatorState>();
- final List<GoRouteMatch> _matches = [];
- final _namedMatches = <String, GoRouteMatch>{};
- final _pushCounts = <String, int>{};
+ final GlobalKey<NavigatorState> _key = GlobalKey<NavigatorState>();
+ final List<GoRouteMatch> _matches = <GoRouteMatch>[];
+ final Map<String, GoRouteMatch> _namedMatches = <String, GoRouteMatch>{};
+ final Map<String, int> _pushCounts = <String, int>{};
void _cacheNamedRoutes(
List<GoRoute> routes,
String parentFullpath,
Map<String, GoRouteMatch> namedFullpaths,
) {
- for (final route in routes) {
- final fullpath = fullLocFor(parentFullpath, route.path);
+ for (final GoRoute route in routes) {
+ final String fullpath = fullLocFor(parentFullpath, route.path);
if (route.name != null) {
- final name = route.name!.toLowerCase();
+ final String name = route.name!.toLowerCase();
if (namedFullpaths.containsKey(name)) {
throw Exception('duplication fullpaths for name "$name":'
'${namedFullpaths[name]!.fullpath}, $fullpath');
@@ -117,12 +117,12 @@
// we only have a partial match until we have a location;
// we're really only caching the route and fullpath at this point
- final match = GoRouteMatch(
+ final GoRouteMatch match = GoRouteMatch(
route: route,
subloc: '/TBD',
fullpath: fullpath,
- encodedParams: {},
- queryParams: {},
+ encodedParams: <String, String>{},
+ queryParams: <String, String>{},
extra: null,
error: null,
);
@@ -149,12 +149,14 @@
'${queryParams.isEmpty ? '' : ', queryParams: $queryParams'}');
// find route and build up the full path along the way
- final match = _getNameRouteMatch(
+ final GoRouteMatch? match = _getNameRouteMatch(
name.toLowerCase(), // case-insensitive name matching
params: params,
queryParams: queryParams,
);
- if (match == null) throw Exception('unknown route name: $name');
+ if (match == null) {
+ throw Exception('unknown route name: $name');
+ }
assert(identical(match.queryParams, queryParams));
return _addQueryParams(match.subloc, queryParams);
@@ -225,7 +227,7 @@
Future<void> setInitialRoutePath(Uri configuration) {
// if the initial location is /, then use the dev initial location;
// otherwise, we're cruising to a deep link, so ignore dev initial location
- final config = configuration.toString();
+ final String config = configuration.toString();
if (config == '/') {
_go(location);
} else {
@@ -235,19 +237,20 @@
// Use [SynchronousFuture] so that the initial url is processed
// synchronously and remove unwanted initial animations on deep-linking
- return SynchronousFuture(null);
+ return SynchronousFuture<void>(null);
}
/// For use by the Router architecture as part of the RouterDelegate.
@override
Future<void> setNewRoutePath(Uri configuration) async {
- final config = configuration.toString();
+ final String config = configuration.toString();
log.info('going to $config');
_go(config);
}
void _go(String location, {Object? extra}) {
- final matches = _getLocRouteMatchesWithRedirects(location, extra: extra);
+ final List<GoRouteMatch> matches =
+ _getLocRouteMatchesWithRedirects(location, extra: extra);
assert(matches.isNotEmpty);
// replace the stack of matches w/ the new ones
@@ -257,16 +260,17 @@
}
void _push(String location, {Object? extra}) {
- final matches = _getLocRouteMatchesWithRedirects(location, extra: extra);
+ final List<GoRouteMatch> matches =
+ _getLocRouteMatchesWithRedirects(location, extra: extra);
assert(matches.isNotEmpty);
- final top = matches.last;
+ final GoRouteMatch top = matches.last;
// remap the pageKey so allow any number of the same page on the stack
- final fullpath = top.fullpath;
- final count = (_pushCounts[fullpath] ?? 0) + 1;
+ final String fullpath = top.fullpath;
+ final int count = (_pushCounts[fullpath] ?? 0) + 1;
_pushCounts[fullpath] = count;
- final pageKey = ValueKey('$fullpath-p$count');
- final match = GoRouteMatch(
+ final ValueKey<String> pageKey = ValueKey<String>('$fullpath-p$count');
+ final GoRouteMatch match = GoRouteMatch(
route: top.route,
subloc: top.subloc,
fullpath: top.fullpath,
@@ -291,9 +295,11 @@
try {
// watch redirects for loops
- final redirects = [_canonicalUri(location)];
+ final List<String> redirects = <String>[_canonicalUri(location)];
bool redirected(String? redir) {
- if (redir == null) return false;
+ if (redir == null) {
+ return false;
+ }
if (Uri.tryParse(redir) == null) {
throw Exception('invalid redirect: $redir');
@@ -301,13 +307,14 @@
if (redirects.contains(redir)) {
redirects.add(redir);
- final msg = 'redirect loop detected: ${redirects.join(' => ')}';
+ final String msg =
+ 'redirect loop detected: ${redirects.join(' => ')}';
throw Exception(msg);
}
redirects.add(redir);
if (redirects.length - 1 > redirectLimit) {
- final msg = 'too many redirects: ${redirects.join(' => ')}';
+ final String msg = 'too many redirects: ${redirects.join(' => ')}';
throw Exception(msg);
}
@@ -317,10 +324,10 @@
// keep looping till we're done redirecting
for (;;) {
- final loc = redirects.last;
+ final String loc = redirects.last;
// check for top-level redirect
- final uri = Uri.parse(loc);
+ final Uri uri = Uri.parse(loc);
if (redirected(
topRedirect(
GoRouterState(
@@ -333,15 +340,17 @@
queryParams: uri.queryParameters,
),
),
- )) continue;
+ )) {
+ continue;
+ }
// get stack of route matches
matches = _getLocRouteMatches(loc, extra: extra);
// merge new params to keep params from previously matched paths, e.g.
// /family/:fid/person/:pid provides fid and pid to person/:pid
- var previouslyMatchedParams = <String, String>{};
- for (final match in matches) {
+ Map<String, String> previouslyMatchedParams = <String, String>{};
+ for (final GoRouteMatch match in matches) {
assert(
!previouslyMatchedParams.keys.any(match.encodedParams.containsKey),
'Duplicated parameter names',
@@ -351,7 +360,7 @@
}
// check top route for redirect
- final top = matches.last;
+ final GoRouteMatch top = matches.last;
if (redirected(
top.route.redirect(
GoRouterState(
@@ -366,11 +375,15 @@
extra: extra,
),
),
- )) continue;
+ )) {
+ continue;
+ }
// let Router know to update the address bar
// (the initial route is not a redirect)
- if (redirects.length > 1) notifyListeners();
+ if (redirects.length > 1) {
+ notifyListeners();
+ }
// no more redirects!
break;
@@ -384,19 +397,20 @@
log.severe('Exception during GoRouter navigation', err, stack);
// create a match that routes to the error page
- final error = err is Exception ? err : Exception(err);
- final uri = Uri.parse(location);
- matches = [
+ final Exception error = err is Exception ? err : Exception(err);
+ final Uri uri = Uri.parse(location);
+ matches = <GoRouteMatch>[
GoRouteMatch(
subloc: uri.path,
fullpath: uri.path,
- encodedParams: {},
+ encodedParams: <String, String>{},
queryParams: uri.queryParameters,
extra: null,
error: error,
route: GoRoute(
path: location,
- pageBuilder: (context, state) => _errorPageBuilder(
+ pageBuilder: (BuildContext context, GoRouterState state) =>
+ _errorPageBuilder(
context,
GoRouterState(
this,
@@ -424,8 +438,8 @@
String location, {
Object? extra,
}) {
- final uri = Uri.parse(location);
- final matchStacks = _getLocRouteMatchStacks(
+ final Uri uri = Uri.parse(location);
+ final List<List<GoRouteMatch>> matchStacks = _getLocRouteMatchStacks(
loc: uri.path,
restLoc: uri.path,
routes: routes,
@@ -440,11 +454,12 @@
}
if (matchStacks.length > 1) {
- final sb = StringBuffer()
+ final StringBuffer sb = StringBuffer()
..writeln('too many routes for location: $location');
- for (final stack in matchStacks) {
- sb.writeln('\t${stack.map((m) => m.route.path).join(' => ')}');
+ for (final List<GoRouteMatch> stack in matchStacks) {
+ sb.writeln(
+ '\t${stack.map((GoRouteMatch m) => m.route.path).join(' => ')}');
}
throw Exception(sb.toString());
@@ -452,10 +467,10 @@
if (kDebugMode) {
assert(matchStacks.length == 1);
- final match = matchStacks.first.last;
- final loc1 = _addQueryParams(match.subloc, match.queryParams);
- final uri2 = Uri.parse(location);
- final loc2 = _addQueryParams(uri2.path, uri2.queryParameters);
+ final GoRouteMatch match = matchStacks.first.last;
+ final String loc1 = _addQueryParams(match.subloc, match.queryParams);
+ final Uri uri2 = Uri.parse(location);
+ final String loc2 = _addQueryParams(uri2.path, uri2.queryParameters);
// NOTE: match the lower case, since subloc is canonicalized to match the
// path case whereas the location can be any case
@@ -466,7 +481,7 @@
}
/// turns a list of routes into a list of routes match stacks for the location
- /// e.g. routes: [
+ /// e.g. routes: <GoRoute>[
/// /
/// family/:fid
/// /login
@@ -519,9 +534,9 @@
required Object? extra,
}) sync* {
// find the set of matches at this level of the tree
- for (final route in routes) {
- final fullpath = fullLocFor(parentFullpath, route.path);
- final match = GoRouteMatch.match(
+ for (final GoRoute route in routes) {
+ final String fullpath = fullLocFor(parentFullpath, route.path);
+ final GoRouteMatch? match = GoRouteMatch.match(
route: route,
restLoc: restLoc,
parentSubloc: parentSubloc,
@@ -530,28 +545,33 @@
queryParams: queryParams,
extra: extra,
);
- if (match == null) continue;
+ if (match == null) {
+ continue;
+ }
// if we have a complete match, then return the matched route
// NOTE: need a lower case match because subloc is canonicalized to match
// the path case whereas the location can be of any case and still match
if (match.subloc.toLowerCase() == loc.toLowerCase()) {
- yield [match];
+ yield <GoRouteMatch>[match];
continue;
}
// if we have a partial match but no sub-routes, bail
- if (route.routes.isEmpty) continue;
+ if (route.routes.isEmpty) {
+ continue;
+ }
// otherwise recurse
- final childRestLoc =
+ final String childRestLoc =
loc.substring(match.subloc.length + (match.subloc == '/' ? 0 : 1));
assert(loc.startsWith(match.subloc));
assert(restLoc.isNotEmpty);
// if there's no sub-route matches, then we don't have a match for this
// location
- final subRouteMatchStacks = _getLocRouteMatchStacks(
+ final List<List<GoRouteMatch>> subRouteMatchStacks =
+ _getLocRouteMatchStacks(
loc: loc,
restLoc: childRestLoc,
parentSubloc: match.subloc,
@@ -560,22 +580,24 @@
queryParams: queryParams,
extra: extra,
).toList();
- if (subRouteMatchStacks.isEmpty) continue;
+ if (subRouteMatchStacks.isEmpty) {
+ continue;
+ }
// add the match to each of the sub-route match stacks and return them
- for (final stack in subRouteMatchStacks) {
- yield [match, ...stack];
+ for (final List<GoRouteMatch> stack in subRouteMatchStacks) {
+ yield <GoRouteMatch>[match, ...stack];
}
}
}
GoRouteMatch? _getNameRouteMatch(
String name, {
- Map<String, String> params = const {},
- Map<String, String> queryParams = const {},
+ Map<String, String> params = const <String, String>{},
+ Map<String, String> queryParams = const <String, String>{},
Object? extra,
}) {
- final partialMatch = _namedMatches[name];
+ final GoRouteMatch? partialMatch = _namedMatches[name];
return partialMatch == null
? null
: GoRouteMatch.matchNamed(
@@ -633,8 +655,8 @@
// if there's an error, show an error page
error = err is Exception ? err : Exception(err);
- final uri = Uri.parse(location);
- pages = [
+ final Uri uri = Uri.parse(location);
+ pages = <Page<dynamic>>[
_errorPageBuilder(
context,
GoRouterState(
@@ -659,7 +681,7 @@
}
// wrap the returned Navigator to enable GoRouter.of(context).go()
- final uri = Uri.parse(location);
+ final Uri uri = Uri.parse(location);
return builderWithNav(
context,
GoRouterState(
@@ -678,8 +700,10 @@
key: _key, // needed to enable Android system Back button
pages: pages!,
observers: observers,
- onPopPage: (route, dynamic result) {
- if (!route.didPop(result)) return false;
+ onPopPage: (Route<dynamic> route, dynamic result) {
+ if (!route.didPop(result)) {
+ return false;
+ }
pop();
return true;
},
@@ -689,7 +713,7 @@
/// Get the stack of sub-routes that matches the location and turn it into a
/// stack of pages, e.g.
- /// routes: [
+ /// routes: <GoRoute>[
/// /
/// family/:fid
/// person/:pid
@@ -714,14 +738,14 @@
) sync* {
assert(matches.isNotEmpty);
- var params = <String, String>{};
- for (final match in matches) {
+ Map<String, String> params = <String, String>{};
+ for (final GoRouteMatch match in matches) {
// merge new params to keep params from previously matched paths, e.g.
// /family/:fid/person/:pid provides fid and pid to person/:pid
- params = {...params, ...match.decodedParams};
+ params = <String, String>{...params, ...match.decodedParams};
// get a page from the builder and associate it with a sub-location
- final state = GoRouterState(
+ final GoRouterState state = GoRouterState(
this,
location: location,
subloc: match.subloc,
@@ -759,22 +783,23 @@
assert(_errorBuilderForAppType == null);
// can be null during testing
- final elem = context is Element ? context : null;
+ final Element? elem = context is Element ? context : null;
if (elem != null && isMaterialApp(elem)) {
log.info('MaterialApp found');
_pageBuilderForAppType = pageBuilderForMaterialApp;
- _errorBuilderForAppType =
- (c, s) => GoRouterMaterialErrorScreen(s.error);
+ _errorBuilderForAppType = (BuildContext c, GoRouterState s) =>
+ GoRouterMaterialErrorScreen(s.error);
} else if (elem != null && isCupertinoApp(elem)) {
log.info('CupertinoApp found');
_pageBuilderForAppType = pageBuilderForCupertinoApp;
- _errorBuilderForAppType =
- (c, s) => GoRouterCupertinoErrorScreen(s.error);
+ _errorBuilderForAppType = (BuildContext c, GoRouterState s) =>
+ GoRouterCupertinoErrorScreen(s.error);
} else {
log.info('WidgetsApp assumed');
_pageBuilderForAppType = pageBuilderForWidgetApp;
- _errorBuilderForAppType = (c, s) => GoRouterErrorScreen(s.error);
+ _errorBuilderForAppType =
+ (BuildContext c, GoRouterState s) => GoRouterErrorScreen(s.error);
}
}
@@ -793,7 +818,7 @@
return _pageBuilderForAppType!(
key: state.pageKey,
name: state.name ?? state.fullpath,
- arguments: {...state.params, ...state.queryParams},
+ arguments: <String, String>{...state.params, ...state.queryParams},
restorationId: state.pageKey.value,
child: builder(context, state),
);
@@ -840,7 +865,7 @@
if (_namedMatches.isNotEmpty) {
log.info('known full paths for route names:');
- for (final e in _namedMatches.entries) {
+ for (final MapEntry<String, GoRouteMatch> e in _namedMatches.entries) {
log.info(' ${e.key} => ${e.value.fullpath}');
}
}
@@ -851,15 +876,15 @@
String parentFullpath,
int depth,
) {
- for (final route in routes) {
- final fullpath = fullLocFor(parentFullpath, route.path);
+ for (final GoRoute route in routes) {
+ final String fullpath = fullLocFor(parentFullpath, route.path);
log.info(' => ${''.padLeft(depth * 2)}$fullpath');
_outputFullPathsFor(route.routes, fullpath, depth + 1);
}
}
static String _canonicalUri(String loc) {
- var canon = Uri.parse(loc).toString();
+ String canon = Uri.parse(loc).toString();
canon = canon.endsWith('?') ? canon.substring(0, canon.length - 1) : canon;
// remove trailing slash except for when you shouldn't, e.g.
@@ -878,7 +903,7 @@
}
static String _addQueryParams(String loc, Map<String, String> queryParams) {
- final uri = Uri.parse(loc);
+ final Uri uri = Uri.parse(loc);
assert(uri.queryParameters.isEmpty);
return _canonicalUri(
Uri(path: uri.path, queryParameters: queryParams).toString());
diff --git a/packages/go_router/lib/src/go_router_error_page.dart b/packages/go_router/lib/src/go_router_error_page.dart
index 2300219..f9c1e68 100644
--- a/packages/go_router/lib/src/go_router_error_page.dart
+++ b/packages/go_router/lib/src/go_router_error_page.dart
@@ -15,14 +15,14 @@
/// The exception to be displayed.
final Exception? error;
- static const _white = Color(0xFFFFFFFF);
+ static const Color _kWhite = Color(0xFFFFFFFF);
@override
Widget build(BuildContext context) => SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
const Text(
'Page Not Found',
style: TextStyle(fontWeight: FontWeight.bold),
@@ -34,7 +34,7 @@
onPressed: () => context.go('/'),
child: const Text(
'Go to home page',
- style: TextStyle(color: _white),
+ style: TextStyle(color: _kWhite),
),
),
],
@@ -51,6 +51,8 @@
}) : super(key: key);
final VoidCallback onPressed;
+
+ /// The child subtree.
final Widget child;
@override
diff --git a/packages/go_router/lib/src/go_router_material.dart b/packages/go_router/lib/src/go_router_material.dart
index 7a949de..1b01920 100644
--- a/packages/go_router/lib/src/go_router_material.dart
+++ b/packages/go_router/lib/src/go_router_material.dart
@@ -41,7 +41,7 @@
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
- children: [
+ children: <Widget>[
SelectableText(error?.toString() ?? 'page not found'),
TextButton(
onPressed: () => context.go('/'),
diff --git a/packages/go_router/lib/src/go_router_refresh_stream.dart b/packages/go_router/lib/src/go_router_refresh_stream.dart
index 5452252..7860640 100644
--- a/packages/go_router/lib/src/go_router_refresh_stream.dart
+++ b/packages/go_router/lib/src/go_router_refresh_stream.dart
@@ -27,14 +27,14 @@
///
/// Every time the [stream] receives an event the [GoRouter] will refresh its
/// current route.
- GoRouterRefreshStream(Stream stream) {
+ GoRouterRefreshStream(Stream<dynamic> stream) {
notifyListeners();
_subscription = stream.asBroadcastStream().listen(
(dynamic _) => notifyListeners(),
);
}
- late final StreamSubscription _subscription;
+ late final StreamSubscription<dynamic> _subscription;
@override
void dispose() {
diff --git a/packages/go_router/lib/src/go_router_state.dart b/packages/go_router/lib/src/go_router_state.dart
index ea3ac98..ffcb072 100644
--- a/packages/go_router/lib/src/go_router_state.dart
+++ b/packages/go_router/lib/src/go_router_state.dart
@@ -16,13 +16,13 @@
required this.name,
this.path,
this.fullpath,
- this.params = const {},
- this.queryParams = const {},
+ this.params = const <String, String>{},
+ this.queryParams = const <String, String>{},
this.extra,
this.error,
ValueKey<String>? pageKey,
}) : pageKey = pageKey ??
- ValueKey(error != null
+ ValueKey<String>(error != null
? 'error'
: fullpath != null && fullpath.isNotEmpty
? fullpath
@@ -65,8 +65,8 @@
/// This is useful for redirecting to a named location.
String namedLocation(
String name, {
- Map<String, String> params = const {},
- Map<String, String> queryParams = const {},
+ Map<String, String> params = const <String, String>{},
+ Map<String, String> queryParams = const <String, String>{},
}) =>
_delegate.namedLocation(name, params: params, queryParams: queryParams);
}
diff --git a/packages/go_router/lib/src/logging.dart b/packages/go_router/lib/src/logging.dart
index 9e8adb5..ccbb09b 100644
--- a/packages/go_router/lib/src/logging.dart
+++ b/packages/go_router/lib/src/logging.dart
@@ -8,21 +8,23 @@
import 'package:logging/logging.dart';
/// The logger for this package.
-final log = Logger('GoRouter');
+final Logger log = Logger('GoRouter');
-StreamSubscription? _subscription;
+StreamSubscription<LogRecord>? _subscription;
/// Forwards diagnostic messages to the dart:developer log() API.
void setLogging({bool enabled = false}) {
_subscription?.cancel();
- if (!enabled) return;
+ if (!enabled) {
+ return;
+ }
- _subscription = log.onRecord.listen((e) {
+ _subscription = log.onRecord.listen((LogRecord e) {
// use `dumpErrorToConsole` for severe messages to ensure that severe
// exceptions are formatted consistently with other Flutter examples and
// avoids printing duplicate exceptions
if (e.level >= Level.SEVERE) {
- final error = e.error;
+ final Object? error = e.error;
FlutterError.dumpErrorToConsole(
FlutterErrorDetails(
exception: error is Exception ? error : Exception(error),
diff --git a/packages/go_router/lib/src/path_parser.dart b/packages/go_router/lib/src/path_parser.dart
index bb4d2e4..bf12633 100644
--- a/packages/go_router/lib/src/path_parser.dart
+++ b/packages/go_router/lib/src/path_parser.dart
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-final _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?');
+final RegExp _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?');
/// Converts a [pattern] such as `/user/:id` into [RegExp].
///
@@ -90,7 +90,7 @@
Map<String, String> extractPathParameters(
List<String> parameters, RegExpMatch match) {
return <String, String>{
- for (var i = 0; i < parameters.length; ++i)
+ for (int i = 0; i < parameters.length; ++i)
parameters[i]: match.namedGroup(parameters[i])!
};
}
diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml
index 1d65523..b340fad 100644
--- a/packages/go_router/pubspec.yaml
+++ b/packages/go_router/pubspec.yaml
@@ -1,7 +1,7 @@
name: go_router
description: A declarative router for Flutter based on Navigation 2 supporting
deep linking, data-driven routes and more
-version: 3.0.3
+version: 3.0.4
repository: https://github.com/flutter/packages/tree/main/packages/go_router
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22
diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart
index abb247f..3e55a1a 100644
--- a/packages/go_router/test/go_router_test.dart
+++ b/packages/go_router/test/go_router_test.dart
@@ -14,34 +14,38 @@
import 'package:go_router/src/go_route_match.dart';
import 'package:logging/logging.dart';
-const enableLogs = true;
-final log = Logger('GoRouter tests');
+const bool enableLogs = true;
+final Logger log = Logger('GoRouter tests');
void main() {
- if (enableLogs) Logger.root.onRecord.listen((e) => debugPrint('$e'));
+ if (enableLogs)
+ Logger.root.onRecord.listen((LogRecord e) => debugPrint('$e'));
group('path routes', () {
test('match home route', () {
- final routes = [
- GoRoute(path: '/', builder: (builder, state) => const HomeScreen()),
+ final List<GoRoute> routes = <GoRoute>[
+ GoRoute(
+ path: '/',
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen()),
];
- final router = _router(routes);
- final matches = router.routerDelegate.matches;
+ final GoRouter router = _router(routes);
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(matches.first.fullpath, '/');
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
});
test('match too many routes', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(path: '/', builder: _dummy),
GoRoute(path: '/', builder: _dummy),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(matches.first.fullpath, '/');
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
@@ -58,7 +62,7 @@
GoRoute(
path: '/',
builder: _dummy,
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: '/foo',
builder: _dummy,
@@ -73,7 +77,7 @@
GoRoute(
path: '/',
builder: _dummy,
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'foo/',
builder: _dummy,
@@ -85,7 +89,7 @@
test('lack of leading / on top-level route', () {
expect(() {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(path: 'foo', builder: _dummy),
];
_router(routes);
@@ -93,97 +97,106 @@
});
test('match no routes', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(path: '/', builder: _dummy),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/foo');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
});
test('match 2nd top level route', () {
- final routes = [
- GoRoute(path: '/', builder: (builder, state) => const HomeScreen()),
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
- path: '/login', builder: (builder, state) => const LoginScreen()),
+ path: '/',
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen()),
+ GoRoute(
+ path: '/login',
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen()),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/login');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(matches.first.subloc, '/login');
expect(router.screenFor(matches.first).runtimeType, LoginScreen);
});
test('match top level route when location has trailing /', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
),
GoRoute(
path: '/login',
- builder: (builder, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/login/');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(matches.first.subloc, '/login');
expect(router.screenFor(matches.first).runtimeType, LoginScreen);
});
test('match top level route when location has trailing / (2)', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(path: '/profile', redirect: (_) => '/profile/foo'),
GoRoute(path: '/profile/:kind', builder: _dummy),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/profile/');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(matches.first.subloc, '/profile/foo');
expect(router.screenFor(matches.first).runtimeType, DummyScreen);
});
test('match top level route when location has trailing / (3)', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(path: '/profile', redirect: (_) => '/profile/foo'),
GoRoute(path: '/profile/:kind', builder: _dummy),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/profile/?bar=baz');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(matches.first.subloc, '/profile/foo');
expect(router.screenFor(matches.first).runtimeType, DummyScreen);
});
test('match sub-route', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
path: 'login',
- builder: (builder, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/login');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches.length, 2);
expect(matches.first.subloc, '/');
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
@@ -192,33 +205,36 @@
});
test('match sub-routes', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
path: 'family/:fid',
- builder: (context, state) => const FamilyScreen('dummy'),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const FamilyScreen('dummy'),
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- builder: (context, state) =>
+ builder: (BuildContext context, GoRouterState state) =>
const PersonScreen('dummy', 'dummy'),
),
],
),
GoRoute(
path: 'login',
- builder: (context, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
{
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(matches.first.fullpath, '/');
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
@@ -226,7 +242,7 @@
router.go('/login');
{
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches.length, 2);
expect(matches.first.subloc, '/');
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
@@ -236,7 +252,7 @@
router.go('/family/f2');
{
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches.length, 2);
expect(matches.first.subloc, '/');
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
@@ -246,7 +262,7 @@
router.go('/family/f2/person/p1');
{
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches.length, 3);
expect(matches.first.subloc, '/');
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
@@ -258,11 +274,11 @@
});
test('match too many sub-routes', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
builder: _dummy,
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'foo/bar',
builder: _dummy,
@@ -270,7 +286,7 @@
GoRoute(
path: 'foo',
builder: _dummy,
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: 'bar',
builder: _dummy,
@@ -281,22 +297,27 @@
),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/foo/bar');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
});
test('router state', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (builder, state) {
+ builder: (BuildContext context, GoRouterState state) {
expect(
state.location,
- anyOf(['/', '/login', '/family/f2', '/family/f2/person/p1']),
+ anyOf(<String>[
+ '/',
+ '/login',
+ '/family/f2',
+ '/family/f2/person/p1'
+ ]),
);
expect(state.subloc, '/');
expect(state.name, 'home');
@@ -307,11 +328,11 @@
expect(state.extra! as int, 1);
return const HomeScreen();
},
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'login',
path: 'login',
- builder: (builder, state) {
+ builder: (BuildContext context, GoRouterState state) {
expect(state.location, '/login');
expect(state.subloc, '/login');
expect(state.name, 'login');
@@ -326,10 +347,10 @@
GoRoute(
name: 'family',
path: 'family/:fid',
- builder: (builder, state) {
+ builder: (BuildContext context, GoRouterState state) {
expect(
state.location,
- anyOf(['/family/f2', '/family/f2/person/p1']),
+ anyOf(<String>['/family/f2', '/family/f2/person/p1']),
);
expect(state.subloc, '/family/f2');
expect(state.name, 'family');
@@ -340,11 +361,11 @@
expect(state.extra! as int, 3);
return FamilyScreen(state.params['fid']!);
},
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'person',
path: 'person/:pid',
- builder: (context, state) {
+ builder: (BuildContext context, GoRouterState state) {
expect(state.location, '/family/f2/person/p1');
expect(state.subloc, '/family/f2/person/p1');
expect(state.name, 'person');
@@ -366,7 +387,7 @@
),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/', extra: 1);
router.go('/login', extra: 2);
router.go('/family/f2', extra: 3);
@@ -374,21 +395,23 @@
});
test('match path case insensitively', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
),
GoRoute(
path: '/family/:fid',
- builder: (builder, state) => FamilyScreen(state.params['fid']!),
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(state.params['fid']!),
),
];
- final router = _router(routes);
- const loc = '/FaMiLy/f2';
+ final GoRouter router = _router(routes);
+ const String loc = '/FaMiLy/f2';
router.go(loc);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
// NOTE: match the lower case, since subloc is canonicalized to match the
// path case whereas the location can be any case; so long as the path
@@ -400,14 +423,14 @@
});
test('match too many routes, ignoring case', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(path: '/page1', builder: _dummy),
GoRoute(path: '/PaGe1', builder: _dummy),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/PAGE1');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
});
@@ -415,19 +438,20 @@
group('named routes', () {
test('match home route', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (builder, state) => const HomeScreen()),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen()),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.goNamed('home');
});
test('match too many routes', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(name: 'home', path: '/', builder: _dummy),
GoRoute(name: 'home', path: '/', builder: _dummy),
];
@@ -445,95 +469,105 @@
test('match no routes', () {
expect(() {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(name: 'home', path: '/', builder: _dummy),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.goNamed('work');
}, throwsException);
});
test('match 2nd top level route', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (builder, state) => const HomeScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
),
GoRoute(
name: 'login',
path: '/login',
- builder: (builder, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.goNamed('login');
});
test('match sub-route', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (builder, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
name: 'login',
path: 'login',
- builder: (builder, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.goNamed('login');
});
test('match sub-route case insensitive', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (builder, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
name: 'page1',
path: 'page1',
- builder: (builder, state) => const Page1Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page1Screen(),
),
GoRoute(
name: 'page2',
path: 'Page2',
- builder: (builder, state) => const Page2Screen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const Page2Screen(),
),
],
),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.goNamed('Page1');
router.goNamed('page2');
});
test('match w/ params', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (context, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
name: 'family',
path: 'family/:fid',
- builder: (context, state) => const FamilyScreen('dummy'),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const FamilyScreen('dummy'),
+ routes: <GoRoute>[
GoRoute(
name: 'person',
path: 'person/:pid',
- builder: (context, state) {
- expect(state.params, {'fid': 'f2', 'pid': 'p1'});
+ builder: (BuildContext context, GoRouterState state) {
+ expect(state.params,
+ <String, String>{'fid': 'f2', 'pid': 'p1'});
return const PersonScreen('dummy', 'dummy');
},
),
@@ -543,26 +577,29 @@
),
];
- final router = _router(routes);
- router.goNamed('person', params: {'fid': 'f2', 'pid': 'p1'});
+ final GoRouter router = _router(routes);
+ router.goNamed('person',
+ params: <String, String>{'fid': 'f2', 'pid': 'p1'});
});
test('too few params', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (context, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
name: 'family',
path: 'family/:fid',
- builder: (context, state) => const FamilyScreen('dummy'),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const FamilyScreen('dummy'),
+ routes: <GoRoute>[
GoRoute(
name: 'person',
path: 'person/:pid',
- builder: (context, state) =>
+ builder: (BuildContext context, GoRouterState state) =>
const PersonScreen('dummy', 'dummy'),
),
],
@@ -571,28 +608,31 @@
),
];
expect(() {
- final router = _router(routes);
- router.goNamed('person', params: {'fid': 'f2'});
+ final GoRouter router = _router(routes);
+ router.goNamed('person', params: <String, String>{'fid': 'f2'});
}, throwsException);
});
test('match case insensitive w/ params', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (context, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
name: 'family',
path: 'family/:fid',
- builder: (context, state) => const FamilyScreen('dummy'),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const FamilyScreen('dummy'),
+ routes: <GoRoute>[
GoRoute(
name: 'PeRsOn',
path: 'person/:pid',
- builder: (context, state) {
- expect(state.params, {'fid': 'f2', 'pid': 'p1'});
+ builder: (BuildContext context, GoRouterState state) {
+ expect(state.params,
+ <String, String>{'fid': 'f2', 'pid': 'p1'});
return const PersonScreen('dummy', 'dummy');
},
),
@@ -602,54 +642,59 @@
),
];
- final router = _router(routes);
- router.goNamed('person', params: {'fid': 'f2', 'pid': 'p1'});
+ final GoRouter router = _router(routes);
+ router.goNamed('person',
+ params: <String, String>{'fid': 'f2', 'pid': 'p1'});
});
test('too few params', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'family',
path: '/family/:fid',
- builder: (context, state) => const FamilyScreen('dummy'),
+ builder: (BuildContext context, GoRouterState state) =>
+ const FamilyScreen('dummy'),
),
];
expect(() {
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.goNamed('family');
}, throwsException);
});
test('too many params', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'family',
path: '/family/:fid',
- builder: (context, state) => const FamilyScreen('dummy'),
+ builder: (BuildContext context, GoRouterState state) =>
+ const FamilyScreen('dummy'),
),
];
expect(() {
- final router = _router(routes);
- router.goNamed('family', params: {'fid': 'f2', 'pid': 'p1'});
+ final GoRouter router = _router(routes);
+ router.goNamed('family',
+ params: <String, String>{'fid': 'f2', 'pid': 'p1'});
}, throwsException);
});
test('sparsely named routes', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
redirect: (_) => '/family/f2',
),
GoRoute(
path: '/family/:fid',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) => FamilyScreen(
state.params['fid']!,
),
- routes: [
+ routes: <GoRoute>[
GoRoute(
name: 'person',
path: 'person:pid',
- builder: (context, state) => PersonScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ PersonScreen(
state.params['fid']!,
state.params['pid']!,
),
@@ -658,57 +703,59 @@
),
];
- final router = _router(routes);
- router.goNamed('person', params: {'fid': 'f2', 'pid': 'p1'});
+ final GoRouter router = _router(routes);
+ router.goNamed('person',
+ params: <String, String>{'fid': 'f2', 'pid': 'p1'});
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(router.screenFor(matches.last).runtimeType, PersonScreen);
});
test('preserve path param spaces and slashes', () {
- const param1 = 'param w/ spaces and slashes';
- final routes = [
+ const String param1 = 'param w/ spaces and slashes';
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'page1',
path: '/page1/:param1',
- builder: (c, s) {
+ builder: (BuildContext c, GoRouterState s) {
expect(s.params['param1'], param1);
return const DummyScreen();
},
),
];
- final router = _router(routes);
- final loc = router.namedLocation('page1', params: {'param1': param1});
+ final GoRouter router = _router(routes);
+ final String loc = router
+ .namedLocation('page1', params: <String, String>{'param1': param1});
log.info('loc= $loc');
router.go(loc);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
log.info('param1= ${matches.first.decodedParams['param1']}');
expect(router.screenFor(matches.first).runtimeType, DummyScreen);
expect(matches.first.decodedParams['param1'], param1);
});
test('preserve query param spaces and slashes', () {
- const param1 = 'param w/ spaces and slashes';
- final routes = [
+ const String param1 = 'param w/ spaces and slashes';
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'page1',
path: '/page1',
- builder: (c, s) {
+ builder: (BuildContext c, GoRouterState s) {
expect(s.queryParams['param1'], param1);
return const DummyScreen();
},
),
];
- final router = _router(routes);
- final loc =
- router.namedLocation('page1', queryParams: {'param1': param1});
+ final GoRouter router = _router(routes);
+ final String loc = router.namedLocation('page1',
+ queryParams: <String, String>{'param1': param1});
log.info('loc= $loc');
router.go(loc);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
log.info('param1= ${matches.first.queryParams['param1']}');
expect(router.screenFor(matches.first).runtimeType, DummyScreen);
expect(matches.first.queryParams['param1'], param1);
@@ -717,80 +764,90 @@
group('redirects', () {
test('top-level redirect', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
path: 'dummy',
- builder: (builder, state) => const DummyScreen()),
+ builder: (BuildContext context, GoRouterState state) =>
+ const DummyScreen()),
GoRoute(
path: 'login',
- builder: (builder, state) => const LoginScreen()),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen()),
],
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
- redirect: (state) => state.subloc == '/login' ? null : '/login',
+ redirect: (GoRouterState state) =>
+ state.subloc == '/login' ? null : '/login',
);
expect(router.location, '/login');
});
test('top-level redirect w/ named routes', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (builder, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
name: 'dummy',
path: 'dummy',
- builder: (builder, state) => const DummyScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const DummyScreen(),
),
GoRoute(
name: 'login',
path: 'login',
- builder: (builder, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
debugLogDiagnostics: true,
routes: routes,
errorBuilder: _dummy,
- redirect: (state) =>
+ redirect: (GoRouterState state) =>
state.subloc == '/login' ? null : state.namedLocation('login'),
);
expect(router.location, '/login');
});
test('route-level redirect', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
path: 'dummy',
- builder: (builder, state) => const DummyScreen(),
- redirect: (state) => '/login',
+ builder: (BuildContext context, GoRouterState state) =>
+ const DummyScreen(),
+ redirect: (GoRouterState state) => '/login',
),
GoRoute(
path: 'login',
- builder: (builder, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
);
@@ -799,28 +856,31 @@
});
test('route-level redirect w/ named routes', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
path: '/',
- builder: (builder, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
name: 'dummy',
path: 'dummy',
- builder: (builder, state) => const DummyScreen(),
- redirect: (state) => state.namedLocation('login'),
+ builder: (BuildContext context, GoRouterState state) =>
+ const DummyScreen(),
+ redirect: (GoRouterState state) => state.namedLocation('login'),
),
GoRoute(
name: 'login',
path: 'login',
- builder: (builder, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
],
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
);
@@ -829,45 +889,50 @@
});
test('multiple mixed redirect', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
path: 'dummy1',
- builder: (builder, state) => const DummyScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const DummyScreen(),
),
GoRoute(
path: 'dummy2',
- builder: (builder, state) => const DummyScreen(),
- redirect: (state) => '/',
+ builder: (BuildContext context, GoRouterState state) =>
+ const DummyScreen(),
+ redirect: (GoRouterState state) => '/',
),
],
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
- redirect: (state) => state.subloc == '/dummy1' ? '/dummy2' : null,
+ redirect: (GoRouterState state) =>
+ state.subloc == '/dummy1' ? '/dummy2' : null,
);
router.go('/dummy1');
expect(router.location, '/');
});
test('top-level redirect loop', () {
- final router = GoRouter(
- routes: [],
- errorBuilder: (context, state) => ErrorScreen(state.error!),
- redirect: (state) => state.subloc == '/'
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[],
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ ErrorScreen(state.error!),
+ redirect: (GoRouterState state) => state.subloc == '/'
? '/login'
: state.subloc == '/login'
? '/'
: null,
);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
expect((router.screenFor(matches.first) as ErrorScreen).ex, isNotNull);
@@ -875,21 +940,22 @@
});
test('route-level redirect loop', () {
- final router = GoRouter(
- routes: [
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- redirect: (state) => '/login',
+ redirect: (GoRouterState state) => '/login',
),
GoRoute(
path: '/login',
- redirect: (state) => '/',
+ redirect: (GoRouterState state) => '/',
),
],
- errorBuilder: (context, state) => ErrorScreen(state.error!),
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ ErrorScreen(state.error!),
);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
expect((router.screenFor(matches.first) as ErrorScreen).ex, isNotNull);
@@ -897,18 +963,20 @@
});
test('mixed redirect loop', () {
- final router = GoRouter(
- routes: [
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/login',
- redirect: (state) => '/',
+ redirect: (GoRouterState state) => '/',
),
],
- errorBuilder: (context, state) => ErrorScreen(state.error!),
- redirect: (state) => state.subloc == '/' ? '/login' : null,
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ ErrorScreen(state.error!),
+ redirect: (GoRouterState state) =>
+ state.subloc == '/' ? '/login' : null,
);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
expect((router.screenFor(matches.first) as ErrorScreen).ex, isNotNull);
@@ -916,17 +984,18 @@
});
test('top-level redirect loop w/ query params', () {
- final router = GoRouter(
- routes: [],
- errorBuilder: (context, state) => ErrorScreen(state.error!),
- redirect: (state) => state.subloc == '/'
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[],
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ ErrorScreen(state.error!),
+ redirect: (GoRouterState state) => state.subloc == '/'
? '/login?from=${state.location}'
: state.subloc == '/login'
? '/'
: null,
);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
expect((router.screenFor(matches.first) as ErrorScreen).ex, isNotNull);
@@ -934,18 +1003,19 @@
});
test('expect null path/fullpath on top-level redirect', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
),
GoRoute(
path: '/dummy',
- redirect: (state) => '/',
+ redirect: (GoRouterState state) => '/',
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
initialLocation: '/dummy',
@@ -954,23 +1024,25 @@
});
test('top-level redirect state', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
),
GoRoute(
path: '/login',
- builder: (builder, state) => const LoginScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const LoginScreen(),
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
initialLocation: '/login?from=/',
debugLogDiagnostics: true,
- redirect: (state) {
+ redirect: (GoRouterState state) {
expect(Uri.parse(state.location).queryParameters, isNotEmpty);
expect(Uri.parse(state.subloc).queryParameters, isEmpty);
expect(state.path, isNull);
@@ -982,59 +1054,60 @@
},
);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, LoginScreen);
});
test('route-level redirect state', () {
- const loc = '/book/0';
- final routes = [
+ const String loc = '/book/0';
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/book/:bookId',
- redirect: (state) {
+ redirect: (GoRouterState state) {
expect(state.location, loc);
expect(state.subloc, loc);
expect(state.path, '/book/:bookId');
expect(state.fullpath, '/book/:bookId');
- expect(state.params, {'bookId': '0'});
+ expect(state.params, <String, String>{'bookId': '0'});
expect(state.queryParams.length, 0);
return null;
},
- builder: (c, s) => const HomeScreen(),
+ builder: (BuildContext c, GoRouterState s) => const HomeScreen(),
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
initialLocation: loc,
debugLogDiagnostics: true,
);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
});
test('sub-sub-route-level redirect params', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (c, s) => const HomeScreen(),
- routes: [
+ builder: (BuildContext c, GoRouterState s) => const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
path: 'family/:fid',
- builder: (c, s) => FamilyScreen(s.params['fid']!),
- routes: [
+ builder: (BuildContext c, GoRouterState s) =>
+ FamilyScreen(s.params['fid']!),
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- redirect: (s) {
+ redirect: (GoRouterState s) {
expect(s.params['fid'], 'f2');
expect(s.params['pid'], 'p1');
return null;
},
- builder: (c, s) => PersonScreen(
+ builder: (BuildContext c, GoRouterState s) => PersonScreen(
s.params['fid']!,
s.params['pid']!,
),
@@ -1045,32 +1118,33 @@
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
initialLocation: '/family/f2/person/p1',
debugLogDiagnostics: true,
);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches.length, 3);
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
expect(router.screenFor(matches[1]).runtimeType, FamilyScreen);
- final page = router.screenFor(matches[2]) as PersonScreen;
+ final PersonScreen page = router.screenFor(matches[2]) as PersonScreen;
expect(page.fid, 'f2');
expect(page.pid, 'p1');
});
test('redirect limit', () {
- final router = GoRouter(
- routes: [],
- errorBuilder: (context, state) => ErrorScreen(state.error!),
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[],
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ ErrorScreen(state.error!),
debugLogDiagnostics: true,
- redirect: (state) => '${state.location}+',
+ redirect: (GoRouterState state) => '${state.location}+',
redirectLimit: 10,
);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
expect((router.screenFor(matches.first) as ErrorScreen).ex, isNotNull);
@@ -1080,20 +1154,22 @@
group('initial location', () {
test('initial location', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
+ routes: <GoRoute>[
GoRoute(
path: 'dummy',
- builder: (builder, state) => const DummyScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const DummyScreen(),
),
],
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
initialLocation: '/dummy',
@@ -1102,18 +1178,19 @@
});
test('initial location w/ redirection', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
),
GoRoute(
path: '/dummy',
- redirect: (state) => '/',
+ redirect: (GoRouterState state) => '/',
),
];
- final router = GoRouter(
+ final GoRouter router = GoRouter(
routes: routes,
errorBuilder: _dummy,
initialLocation: '/dummy',
@@ -1124,22 +1201,24 @@
group('params', () {
test('preserve path param case', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
),
GoRoute(
path: '/family/:fid',
- builder: (builder, state) => FamilyScreen(state.params['fid']!),
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(state.params['fid']!),
),
];
- final router = _router(routes);
- for (final fid in ['f2', 'F2']) {
- final loc = '/family/$fid';
+ final GoRouter router = _router(routes);
+ for (final String fid in <String>['f2', 'F2']) {
+ final String loc = '/family/$fid';
router.go(loc);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(router.location, loc);
expect(matches, hasLength(1));
@@ -1149,24 +1228,25 @@
});
test('preserve query param case', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
),
GoRoute(
path: '/family',
- builder: (builder, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) => FamilyScreen(
state.queryParams['fid']!,
),
),
];
- final router = _router(routes);
- for (final fid in ['f2', 'F2']) {
- final loc = '/family?fid=$fid';
+ final GoRouter router = _router(routes);
+ for (final String fid in <String>['f2', 'F2']) {
+ final String loc = '/family?fid=$fid';
router.go(loc);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(router.location, loc);
expect(matches, hasLength(1));
@@ -1176,50 +1256,50 @@
});
test('preserve path param spaces and slashes', () {
- const param1 = 'param w/ spaces and slashes';
- final routes = [
+ const String param1 = 'param w/ spaces and slashes';
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/page1/:param1',
- builder: (c, s) {
+ builder: (BuildContext c, GoRouterState s) {
expect(s.params['param1'], param1);
return const DummyScreen();
},
),
];
- final router = _router(routes);
- final loc = '/page1/${Uri.encodeComponent(param1)}';
+ final GoRouter router = _router(routes);
+ final String loc = '/page1/${Uri.encodeComponent(param1)}';
router.go(loc);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
log.info('param1= ${matches.first.decodedParams['param1']}');
expect(router.screenFor(matches.first).runtimeType, DummyScreen);
expect(matches.first.decodedParams['param1'], param1);
});
test('preserve query param spaces and slashes', () {
- const param1 = 'param w/ spaces and slashes';
- final routes = [
+ const String param1 = 'param w/ spaces and slashes';
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/page1',
- builder: (c, s) {
+ builder: (BuildContext c, GoRouterState s) {
expect(s.queryParams['param1'], param1);
return const DummyScreen();
},
),
];
- final router = _router(routes);
+ final GoRouter router = _router(routes);
router.go('/page1?param1=$param1');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(router.screenFor(matches.first).runtimeType, DummyScreen);
expect(matches.first.queryParams['param1'], param1);
- final loc = '/page1?param1=${Uri.encodeQueryComponent(param1)}';
+ final String loc = '/page1?param1=${Uri.encodeQueryComponent(param1)}';
router.go(loc);
- final matches2 = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches2 = router.routerDelegate.matches;
expect(router.screenFor(matches2[0]).runtimeType, DummyScreen);
expect(matches2[0].queryParams['param1'], param1);
});
@@ -1227,13 +1307,14 @@
test('error: duplicate path param', () {
try {
GoRouter(
- routes: [
+ routes: <GoRoute>[
GoRoute(
path: '/:id/:blah/:bam/:id/:blah',
builder: _dummy,
),
],
- errorBuilder: (context, state) => ErrorScreen(state.error!),
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ ErrorScreen(state.error!),
initialLocation: '/0/1/2/0/1',
);
expect(false, true);
@@ -1243,11 +1324,11 @@
});
test('duplicate query param', () {
- final router = GoRouter(
- routes: [
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/',
- builder: (context, state) {
+ builder: (BuildContext context, GoRouterState state) {
log.info('id= ${state.params['id']}');
expect(state.params.length, 0);
expect(state.queryParams.length, 1);
@@ -1256,24 +1337,25 @@
},
),
],
- errorBuilder: (context, state) => ErrorScreen(state.error!),
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ ErrorScreen(state.error!),
);
router.go('/?id=0&id=1');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(matches.first.fullpath, '/');
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
});
test('duplicate path + query param', () {
- final router = GoRouter(
- routes: [
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[
GoRoute(
path: '/:id',
- builder: (context, state) {
- expect(state.params, {'id': '0'});
- expect(state.queryParams, {'id': '1'});
+ builder: (BuildContext context, GoRouterState state) {
+ expect(state.params, <String, String>{'id': '0'});
+ expect(state.queryParams, <String, String>{'id': '1'});
return const HomeScreen();
},
),
@@ -1282,25 +1364,27 @@
);
router.go('/0?id=1');
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(matches, hasLength(1));
expect(matches.first.fullpath, '/:id');
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
});
test('push + query param', () {
- final router = GoRouter(
- routes: [
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[
GoRoute(path: '/', builder: _dummy),
GoRoute(
path: '/family',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(
state.queryParams['fid']!,
),
),
GoRoute(
path: '/person',
- builder: (context, state) => PersonScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ PersonScreen(
state.queryParams['fid']!,
state.queryParams['pid']!,
),
@@ -1311,29 +1395,31 @@
router.go('/family?fid=f2');
router.push('/person?fid=f2&pid=p1');
- final page1 =
+ final FamilyScreen page1 =
router.screenFor(router.routerDelegate.matches.first) as FamilyScreen;
expect(page1.fid, 'f2');
- final page2 =
+ final PersonScreen page2 =
router.screenFor(router.routerDelegate.matches[1]) as PersonScreen;
expect(page2.fid, 'f2');
expect(page2.pid, 'p1');
});
test('push + extra param', () {
- final router = GoRouter(
- routes: [
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[
GoRoute(path: '/', builder: _dummy),
GoRoute(
path: '/family',
- builder: (context, state) => FamilyScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(
(state.extra! as Map<String, String>)['fid']!,
),
),
GoRoute(
path: '/person',
- builder: (context, state) => PersonScreen(
+ builder: (BuildContext context, GoRouterState state) =>
+ PersonScreen(
(state.extra! as Map<String, String>)['fid']!,
(state.extra! as Map<String, String>)['pid']!,
),
@@ -1342,33 +1428,35 @@
errorBuilder: _dummy,
);
- router.go('/family', extra: {'fid': 'f2'});
- router.push('/person', extra: {'fid': 'f2', 'pid': 'p1'});
- final page1 =
+ router.go('/family', extra: <String, String>{'fid': 'f2'});
+ router.push('/person', extra: <String, String>{'fid': 'f2', 'pid': 'p1'});
+ final FamilyScreen page1 =
router.screenFor(router.routerDelegate.matches.first) as FamilyScreen;
expect(page1.fid, 'f2');
- final page2 =
+ final PersonScreen page2 =
router.screenFor(router.routerDelegate.matches[1]) as PersonScreen;
expect(page2.fid, 'f2');
expect(page2.pid, 'p1');
});
test('keep param in nested route', () {
- final routes = [
+ final List<GoRoute> routes = <GoRoute>[
GoRoute(
path: '/',
- builder: (builder, state) => const HomeScreen(),
+ builder: (BuildContext context, GoRouterState state) =>
+ const HomeScreen(),
),
GoRoute(
path: '/family/:fid',
- builder: (builder, state) => FamilyScreen(state.params['fid']!),
- routes: [
+ builder: (BuildContext context, GoRouterState state) =>
+ FamilyScreen(state.params['fid']!),
+ routes: <GoRoute>[
GoRoute(
path: 'person/:pid',
- builder: (context, state) {
- final fid = state.params['fid']!;
- final pid = state.params['pid']!;
+ builder: (BuildContext context, GoRouterState state) {
+ final String fid = state.params['fid']!;
+ final String pid = state.params['pid']!;
return PersonScreen(fid, pid);
},
@@ -1377,13 +1465,13 @@
),
];
- final router = _router(routes);
- const fid = "f1";
- const pid = "p2";
- final loc = "/family/$fid/person/$pid";
+ final GoRouter router = _router(routes);
+ const String fid = 'f1';
+ const String pid = 'p2';
+ const String loc = '/family/$fid/person/$pid';
router.push(loc);
- final matches = router.routerDelegate.matches;
+ final List<GoRouteMatch> matches = router.routerDelegate.matches;
expect(router.location, loc);
expect(matches, hasLength(2));
@@ -1398,7 +1486,7 @@
setUpAll(() async {
streamController = StreamController<int>.broadcast();
- await streamController.addStream(Stream.value(0));
+ await streamController.addStream(Stream<int>.value(0));
});
tearDownAll(() {
@@ -1408,7 +1496,8 @@
group('stream', () {
test('no stream emits', () async {
// Act
- final notifyListener = MockGoRouterRefreshStream(
+ final MockGoRouterRefreshStream notifyListener =
+ MockGoRouterRefreshStream(
streamController.stream,
);
@@ -1421,14 +1510,15 @@
test('three stream emits', () async {
// Arrange
- final toEmit = [1, 2, 3];
+ final List<int> toEmit = <int>[1, 2, 3];
// Act
- final notifyListener = MockGoRouterRefreshStream(
+ final MockGoRouterRefreshStream notifyListener =
+ MockGoRouterRefreshStream(
streamController.stream,
);
- await streamController.addStream(Stream.fromIterable(toEmit));
+ await streamController.addStream(Stream<int>.fromIterable(toEmit));
// Assert
expect(notifyListener.notifyCount, equals(toEmit.length + 1));
@@ -1442,7 +1532,7 @@
class MockGoRouterRefreshStream extends GoRouterRefreshStream {
MockGoRouterRefreshStream(
- Stream stream,
+ Stream<dynamic> stream,
) : notifyCount = 0,
super(stream);
@@ -1457,7 +1547,8 @@
GoRouter _router(List<GoRoute> routes) => GoRouter(
routes: routes,
- errorBuilder: (context, state) => ErrorScreen(state.error!),
+ errorBuilder: (BuildContext context, GoRouterState state) =>
+ ErrorScreen(state.error!),
debugLogDiagnostics: true,
);
@@ -1509,9 +1600,9 @@
extension on GoRouter {
Page<dynamic> _pageFor(GoRouteMatch match) {
- final matches = routerDelegate.matches;
- final i = matches.indexOf(match);
- final pages =
+ final List<GoRouteMatch> matches = routerDelegate.matches;
+ final int i = matches.indexOf(match);
+ final List<Page<dynamic>> pages =
routerDelegate.getPages(DummyBuildContext(), matches).toList();
return pages[i];
}
diff --git a/packages/go_router/test/path_parser_test.dart b/packages/go_router/test/path_parser_test.dart
index 85876ec..5d5ff89 100644
--- a/packages/go_router/test/path_parser_test.dart
+++ b/packages/go_router/test/path_parser_test.dart
@@ -7,7 +7,7 @@
void main() {
test('patternToRegExp without path parameter', () async {
- final String pattern = '/settings/detail';
+ const String pattern = '/settings/detail';
final List<String> pathParameter = <String>[];
final RegExp regex = patternToRegExp(pattern, pathParameter);
expect(pathParameter.isEmpty, isTrue);
@@ -20,7 +20,7 @@
});
test('patternToRegExp with path parameter', () async {
- final String pattern = '/user/:id/book/:bookId';
+ const String pattern = '/user/:id/book/:bookId';
final List<String> pathParameter = <String>[];
final RegExp regex = patternToRegExp(pattern, pathParameter);
expect(pathParameter.length, 2);
@@ -42,11 +42,11 @@
});
test('patternToPath without path parameter', () async {
- final String pattern = '/settings/detail';
+ const String pattern = '/settings/detail';
final List<String> pathParameter = <String>[];
final RegExp regex = patternToRegExp(pattern, pathParameter);
- final String url = '/settings/detail';
+ const String url = '/settings/detail';
final RegExpMatch? match = regex.firstMatch(url);
expect(match, isNotNull);
@@ -58,11 +58,11 @@
});
test('patternToPath with path parameter', () async {
- final String pattern = '/user/:id/book/:bookId';
+ const String pattern = '/user/:id/book/:bookId';
final List<String> pathParameter = <String>[];
final RegExp regex = patternToRegExp(pattern, pathParameter);
- final String url = '/user/123/book/456';
+ const String url = '/user/123/book/456';
final RegExpMatch? match = regex.firstMatch(url);
expect(match, isNotNull);