From 838d6aa668f2eba3942e942a661af5ebc3c263af Mon Sep 17 00:00:00 2001 From: toly <1981462002@qq.com> Date: Mon, 11 Dec 2023 08:48:30 +0800 Subject: [PATCH] 24 --- lib/go_router/examples/async_redirection.dart | 244 ----- lib/go_router/examples/books/main.dart | 171 ---- lib/go_router/examples/books/src/auth.dart | 46 - lib/go_router/examples/books/src/data.dart | 7 - .../examples/books/src/data/author.dart | 23 - .../examples/books/src/data/book.dart | 32 - .../examples/books/src/data/library.dart | 76 -- .../books/src/screens/author_details.dart | 49 - .../examples/books/src/screens/authors.dart | 31 - .../books/src/screens/book_details.dart | 77 -- .../examples/books/src/screens/books.dart | 118 --- .../examples/books/src/screens/scaffold.dart | 71 -- .../examples/books/src/screens/settings.dart | 101 -- .../examples/books/src/screens/sign_in.dart | 77 -- .../books/src/widgets/author_list.dart | 37 - .../examples/books/src/widgets/book_list.dart | 37 - .../examples/exception_handling.dart | 87 -- .../input/decoration/code_decoration.dart | 175 ---- lib/go_router/examples/input/input.dart | 215 ----- .../examples/input/input_painter.dart | 141 --- lib/go_router/examples/input/main.dart | 25 - lib/go_router/examples/main.dart | 87 -- lib/go_router/examples/named_routes.dart | 181 ---- lib/go_router/examples/on_exit.dart | 139 --- .../others/custom_stateful_shell_route.dart | 460 ---------- .../examples/others/error_screen.dart | 110 --- .../examples/others/extra_param.dart | 133 --- lib/go_router/examples/others/init_loc.dart | 110 --- .../examples/others/nav_observer.dart | 167 ---- lib/go_router/examples/others/push.dart | 101 -- .../examples/others/router_neglect.dart | 95 -- .../examples/others/state_restoration.dart | 102 --- .../stateful_shell_state_restoration.dart | 236 ----- .../examples/others/transitions.dart | 163 ---- .../examples/path_and_query_parameters.dart | 169 ---- lib/go_router/examples/redirection.dart | 146 --- lib/go_router/examples/routing_config.dart | 108 --- lib/go_router/examples/shell_route.dart | 289 ------ .../examples/stateful_shell_route.dart | 324 ------- .../examples/transition_animations.dart | 175 ---- .../context_menu_controller.0.dart | 183 ---- .../editable_text_toolbar_builder.0.dart | 89 -- .../editable_text_toolbar_builder.1.dart | 109 --- .../editable_text_toolbar_builder.2.dart | 132 --- .../selectable_region_toolbar_builder.0.dart | 93 -- lib/go_router/v0/app.dart | 1 - .../router/app_router_delegate.dart | 221 ----- .../v0/app/navigation/router/iroute.dart | 105 --- .../transition/fade_transition_page.dart | 53 -- .../transition/no_transition_page.dart | 47 - .../app/navigation/views/app_navigation.dart | 34 - .../navigation/views/app_navigation_rail.dart | 65 -- .../navigation/views/app_router_editor.dart | 64 -- .../v0/app/navigation/views/app_top_bar.dart | 104 --- lib/go_router/v0/app/unit_app.dart | 29 - .../v0/pages/color/color_add_page.dart | 102 --- .../v0/pages/color/color_detail_page.dart | 34 - lib/go_router/v0/pages/color/color_page.dart | 54 -- .../v0/pages/counter/counter_page.dart | 43 - lib/go_router/v0/pages/empty/empty_page.dart | 30 - .../v0/pages/settings/settings_page.dart | 11 - lib/go_router/v0/pages/sort/sort_page.dart | 859 ------------------ lib/go_router/v0/pages/user/user_page.dart | 11 - 63 files changed, 7608 deletions(-) delete mode 100644 lib/go_router/examples/async_redirection.dart delete mode 100644 lib/go_router/examples/books/main.dart delete mode 100644 lib/go_router/examples/books/src/auth.dart delete mode 100644 lib/go_router/examples/books/src/data.dart delete mode 100644 lib/go_router/examples/books/src/data/author.dart delete mode 100644 lib/go_router/examples/books/src/data/book.dart delete mode 100644 lib/go_router/examples/books/src/data/library.dart delete mode 100644 lib/go_router/examples/books/src/screens/author_details.dart delete mode 100644 lib/go_router/examples/books/src/screens/authors.dart delete mode 100644 lib/go_router/examples/books/src/screens/book_details.dart delete mode 100644 lib/go_router/examples/books/src/screens/books.dart delete mode 100644 lib/go_router/examples/books/src/screens/scaffold.dart delete mode 100644 lib/go_router/examples/books/src/screens/settings.dart delete mode 100644 lib/go_router/examples/books/src/screens/sign_in.dart delete mode 100644 lib/go_router/examples/books/src/widgets/author_list.dart delete mode 100644 lib/go_router/examples/books/src/widgets/book_list.dart delete mode 100644 lib/go_router/examples/exception_handling.dart delete mode 100644 lib/go_router/examples/input/decoration/code_decoration.dart delete mode 100644 lib/go_router/examples/input/input.dart delete mode 100644 lib/go_router/examples/input/input_painter.dart delete mode 100644 lib/go_router/examples/input/main.dart delete mode 100644 lib/go_router/examples/main.dart delete mode 100644 lib/go_router/examples/named_routes.dart delete mode 100644 lib/go_router/examples/on_exit.dart delete mode 100644 lib/go_router/examples/others/custom_stateful_shell_route.dart delete mode 100644 lib/go_router/examples/others/error_screen.dart delete mode 100644 lib/go_router/examples/others/extra_param.dart delete mode 100644 lib/go_router/examples/others/init_loc.dart delete mode 100644 lib/go_router/examples/others/nav_observer.dart delete mode 100644 lib/go_router/examples/others/push.dart delete mode 100644 lib/go_router/examples/others/router_neglect.dart delete mode 100644 lib/go_router/examples/others/state_restoration.dart delete mode 100644 lib/go_router/examples/others/stateful_shell_state_restoration.dart delete mode 100644 lib/go_router/examples/others/transitions.dart delete mode 100644 lib/go_router/examples/path_and_query_parameters.dart delete mode 100644 lib/go_router/examples/redirection.dart delete mode 100644 lib/go_router/examples/routing_config.dart delete mode 100644 lib/go_router/examples/shell_route.dart delete mode 100644 lib/go_router/examples/stateful_shell_route.dart delete mode 100644 lib/go_router/examples/transition_animations.dart delete mode 100644 lib/go_router/overlay/context_menu/context_menu_controller.0.dart delete mode 100644 lib/go_router/overlay/context_menu/editable_text_toolbar_builder.0.dart delete mode 100644 lib/go_router/overlay/context_menu/editable_text_toolbar_builder.1.dart delete mode 100644 lib/go_router/overlay/context_menu/editable_text_toolbar_builder.2.dart delete mode 100644 lib/go_router/overlay/context_menu/selectable_region_toolbar_builder.0.dart delete mode 100644 lib/go_router/v0/app.dart delete mode 100644 lib/go_router/v0/app/navigation/router/app_router_delegate.dart delete mode 100644 lib/go_router/v0/app/navigation/router/iroute.dart delete mode 100644 lib/go_router/v0/app/navigation/transition/fade_transition_page.dart delete mode 100644 lib/go_router/v0/app/navigation/transition/no_transition_page.dart delete mode 100644 lib/go_router/v0/app/navigation/views/app_navigation.dart delete mode 100644 lib/go_router/v0/app/navigation/views/app_navigation_rail.dart delete mode 100644 lib/go_router/v0/app/navigation/views/app_router_editor.dart delete mode 100644 lib/go_router/v0/app/navigation/views/app_top_bar.dart delete mode 100644 lib/go_router/v0/app/unit_app.dart delete mode 100644 lib/go_router/v0/pages/color/color_add_page.dart delete mode 100644 lib/go_router/v0/pages/color/color_detail_page.dart delete mode 100644 lib/go_router/v0/pages/color/color_page.dart delete mode 100644 lib/go_router/v0/pages/counter/counter_page.dart delete mode 100644 lib/go_router/v0/pages/empty/empty_page.dart delete mode 100644 lib/go_router/v0/pages/settings/settings_page.dart delete mode 100644 lib/go_router/v0/pages/sort/sort_page.dart delete mode 100644 lib/go_router/v0/pages/user/user_page.dart diff --git a/lib/go_router/examples/async_redirection.dart b/lib/go_router/examples/async_redirection.dart deleted file mode 100644 index f84c14e..0000000 --- a/lib/go_router/examples/async_redirection.dart +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -// This scenario demonstrates how to use redirect to handle a asynchronous -// sign-in flow. -// -// The `StreamAuth` is a mock of google_sign_in. This example wraps it with an -// InheritedNotifier, StreamAuthScope, and relies on -// `dependOnInheritedWidgetOfExactType` to create a dependency between the -// notifier and go_router's parsing pipeline. When StreamAuth broadcasts new -// event, the dependency will cause the go_router to reparse the current url -// which will also trigger the redirect. - -void main() => runApp(StreamAuthScope(child: App())); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// 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) => MaterialApp.router( - routerConfig: _router, - title: title, - debugShowCheckedModeBanner: false, - ); - - late final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) => - const HomeScreen(), - ), - GoRoute( - path: '/login', - builder: (BuildContext context, GoRouterState state) => - const LoginScreen(), - ), - ], - - // redirect to the login page if the user is not logged in - redirect: (BuildContext context, GoRouterState state) async { - // Using `of` method creates a dependency of StreamAuthScope. It will - // cause go_router to reparse current route if StreamAuth has new sign-in - // information. - final bool loggedIn = await StreamAuthScope.of(context).isSignedIn(); - final bool loggingIn = state.matchedLocation == '/login'; - if (!loggedIn) { - return '/login'; - } - - // if the user is logged in but still on the login page, send them to - // the home page - if (loggingIn) { - return '/'; - } - - // no need to redirect at all - return null; - }, - ); -} - -/// The login screen. -class LoginScreen extends StatefulWidget { - /// Creates a [LoginScreen]. - const LoginScreen({super.key}); - - @override - State createState() => _LoginScreenState(); -} - -class _LoginScreenState extends State - with TickerProviderStateMixin { - bool loggingIn = false; - late final AnimationController controller; - - @override - void initState() { - super.initState(); - controller = AnimationController( - vsync: this, - duration: const Duration(seconds: 1), - )..addListener(() { - setState(() {}); - }); - controller.repeat(); - } - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (loggingIn) CircularProgressIndicator(value: controller.value), - if (!loggingIn) - ElevatedButton( - onPressed: () { - StreamAuthScope.of(context).signIn('test-user'); - setState(() { - loggingIn = true; - }); - }, - child: const Text('Login'), - ), - ], - ), - ), - ); -} - -/// The home screen. -class HomeScreen extends StatelessWidget { - /// Creates a [HomeScreen]. - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - final StreamAuth info = StreamAuthScope.of(context); - - return Scaffold( - appBar: AppBar( - title: const Text(App.title), - actions: [ - IconButton( - onPressed: () => info.signOut(), - tooltip: 'Logout: ${info.currentUser}', - icon: const Icon(Icons.logout), - ) - ], - ), - body: const Center( - child: Text('HomeScreen'), - ), - ); - } -} - -/// A scope that provides [StreamAuth] for the subtree. -class StreamAuthScope extends InheritedNotifier { - /// Creates a [StreamAuthScope] sign in scope. - StreamAuthScope({ - super.key, - required super.child, - }) : super( - notifier: StreamAuthNotifier(), - ); - - /// Gets the [StreamAuth]. - static StreamAuth of(BuildContext context) { - return context - .dependOnInheritedWidgetOfExactType()! - .notifier! - .streamAuth; - } -} - -/// A class that converts [StreamAuth] into a [ChangeNotifier]. -class StreamAuthNotifier extends ChangeNotifier { - /// Creates a [StreamAuthNotifier]. - StreamAuthNotifier() : streamAuth = StreamAuth() { - streamAuth.onCurrentUserChanged.listen((String? string) { - notifyListeners(); - }); - } - - /// The stream auth client. - final StreamAuth streamAuth; -} - -/// An asynchronous log in services mock with stream similar to google_sign_in. -/// -/// This class adds an artificial delay of 3 second when logging in an user, and -/// will automatically clear the login session after [refreshInterval]. -class StreamAuth { - /// Creates an [StreamAuth] that clear the current user session in - /// [refeshInterval] second. - StreamAuth({this.refreshInterval = 20}) - : _userStreamController = StreamController.broadcast() { - _userStreamController.stream.listen((String? currentUser) { - _currentUser = currentUser; - }); - } - - /// The current user. - String? get currentUser => _currentUser; - String? _currentUser; - - /// Checks whether current user is signed in with an artificial delay to mimic - /// async operation. - Future isSignedIn() async { - await Future.delayed(const Duration(seconds: 1)); - return _currentUser != null; - } - - /// A stream that notifies when current user has changed. - Stream get onCurrentUserChanged => _userStreamController.stream; - final StreamController _userStreamController; - - /// The interval that automatically signs out the user. - final int refreshInterval; - - Timer? _timer; - Timer _createRefreshTimer() { - return Timer(Duration(seconds: refreshInterval), () { - _userStreamController.add(null); - _timer = null; - }); - } - - /// Signs in a user with an artificial delay to mimic async operation. - Future signIn(String newUserName) async { - await Future.delayed(const Duration(seconds: 3)); - _userStreamController.add(newUserName); - _timer?.cancel(); - _timer = _createRefreshTimer(); - } - - /// Signs out the current user. - Future signOut() async { - _timer?.cancel(); - _timer = null; - _userStreamController.add(null); - } -} diff --git a/lib/go_router/examples/books/main.dart b/lib/go_router/examples/books/main.dart deleted file mode 100644 index eb757e3..0000000 --- a/lib/go_router/examples/books/main.dart +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -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'; -import 'src/screens/book_details.dart'; -import 'src/screens/books.dart'; -import 'src/screens/scaffold.dart'; -import 'src/screens/settings.dart'; -import 'src/screens/sign_in.dart'; - -void main() => runApp(Bookstore()); - -/// The book store view. -class Bookstore extends StatelessWidget { - /// Creates a [Bookstore]. - Bookstore({super.key}); - - final ValueKey _scaffoldKey = const ValueKey('App scaffold'); - - @override - Widget build(BuildContext context) => BookstoreAuthScope( - notifier: _auth, - child: MaterialApp.router( - routerConfig: _router, - ), - ); - - final BookstoreAuth _auth = BookstoreAuth(); - - late final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - redirect: (_, __) => '/books', - ), - GoRoute( - path: '/signin', - pageBuilder: (BuildContext context, GoRouterState state) => - FadeTransitionPage( - key: state.pageKey, - child: SignInScreen( - onSignIn: (Credentials credentials) { - BookstoreAuthScope.of(context) - .signIn(credentials.username, credentials.password); - }, - ), - ), - ), - GoRoute( - path: '/books', - redirect: (_, __) => '/books/popular', - ), - GoRoute( - path: '/book/:bookId', - redirect: (BuildContext context, GoRouterState state) => - '/books/all/${state.pathParameters['bookId']}', - ), - GoRoute( - path: '/books/:kind(new|all|popular)', - pageBuilder: (BuildContext context, GoRouterState state) => - FadeTransitionPage( - key: _scaffoldKey, - child: BookstoreScaffold( - selectedTab: ScaffoldTab.books, - child: BooksScreen(state.pathParameters['kind']!), - ), - ), - routes: [ - GoRoute( - path: ':bookId', - builder: (BuildContext context, GoRouterState state) { - final String bookId = state.pathParameters['bookId']!; - final Book? selectedBook = libraryInstance.allBooks - .firstWhereOrNull((Book b) => b.id.toString() == bookId); - - return BookDetailsScreen(book: selectedBook); - }, - ), - ], - ), - GoRoute( - path: '/author/:authorId', - redirect: (BuildContext context, GoRouterState state) => - '/authors/${state.pathParameters['authorId']}', - ), - GoRoute( - path: '/authors', - pageBuilder: (BuildContext context, GoRouterState state) => - FadeTransitionPage( - key: _scaffoldKey, - child: const BookstoreScaffold( - selectedTab: ScaffoldTab.authors, - child: AuthorsScreen(), - ), - ), - routes: [ - GoRoute( - path: ':authorId', - builder: (BuildContext context, GoRouterState state) { - final int authorId = int.parse(state.pathParameters['authorId']!); - final Author? selectedAuthor = libraryInstance.allAuthors - .firstWhereOrNull((Author a) => a.id == authorId); - - return AuthorDetailsScreen(author: selectedAuthor); - }, - ), - ], - ), - GoRoute( - path: '/settings', - pageBuilder: (BuildContext context, GoRouterState state) => - FadeTransitionPage( - key: _scaffoldKey, - child: const BookstoreScaffold( - selectedTab: ScaffoldTab.settings, - child: SettingsScreen(), - ), - ), - ), - ], - redirect: _guard, - refreshListenable: _auth, - debugLogDiagnostics: true, - ); - - String? _guard(BuildContext context, GoRouterState state) { - final bool signedIn = _auth.signedIn; - final bool signingIn = state.matchedLocation == '/signin'; - - // Go to /signin if the user is not signed in - if (!signedIn && !signingIn) { - return '/signin'; - } - // Go to /books if the user is signed in and tries to go to /signin. - else if (signedIn && signingIn) { - return '/books'; - } - - // no redirect - return null; - } -} - -/// A page that fades in an out. -class FadeTransitionPage extends CustomTransitionPage { - /// Creates a [FadeTransitionPage]. - FadeTransitionPage({ - required LocalKey super.key, - required super.child, - }) : super( - transitionsBuilder: (BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child) => - FadeTransition( - opacity: animation.drive(_curveTween), - child: child, - )); - - static final CurveTween _curveTween = CurveTween(curve: Curves.easeIn); -} diff --git a/lib/go_router/examples/books/src/auth.dart b/lib/go_router/examples/books/src/auth.dart deleted file mode 100644 index b9c353c..0000000 --- a/lib/go_router/examples/books/src/auth.dart +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/widgets.dart'; - -/// A mock authentication service. -class BookstoreAuth extends ChangeNotifier { - bool _signedIn = false; - - /// Whether user has signed in. - bool get signedIn => _signedIn; - - /// Signs out the current user. - Future signOut() async { - await Future.delayed(const Duration(milliseconds: 200)); - // Sign out. - _signedIn = false; - notifyListeners(); - } - - /// Signs in a user. - Future signIn(String username, String password) async { - await Future.delayed(const Duration(milliseconds: 200)); - - // Sign in. Allow any password. - _signedIn = true; - notifyListeners(); - return _signedIn; - } -} - -/// An inherited notifier to host [BookstoreAuth] for the subtree. -class BookstoreAuthScope extends InheritedNotifier { - /// Creates a [BookstoreAuthScope]. - const BookstoreAuthScope({ - required BookstoreAuth super.notifier, - required super.child, - super.key, - }); - - /// Gets the [BookstoreAuth] above the context. - static BookstoreAuth of(BuildContext context) => context - .dependOnInheritedWidgetOfExactType()! - .notifier!; -} diff --git a/lib/go_router/examples/books/src/data.dart b/lib/go_router/examples/books/src/data.dart deleted file mode 100644 index 109082e..0000000 --- a/lib/go_router/examples/books/src/data.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -export 'data/author.dart'; -export 'data/book.dart'; -export 'data/library.dart'; diff --git a/lib/go_router/examples/books/src/data/author.dart b/lib/go_router/examples/books/src/data/author.dart deleted file mode 100644 index b58db11..0000000 --- a/lib/go_router/examples/books/src/data/author.dart +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import '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 books = []; -} diff --git a/lib/go_router/examples/books/src/data/book.dart b/lib/go_router/examples/books/src/data/book.dart deleted file mode 100644 index cd2c94f..0000000 --- a/lib/go_router/examples/books/src/data/book.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'author.dart'; - -/// Book data class. -class Book { - /// Creates a book data object. - Book({ - required this.id, - required this.title, - required this.isPopular, - required this.isNew, - 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/lib/go_router/examples/books/src/data/library.dart b/lib/go_router/examples/books/src/data/library.dart deleted file mode 100644 index 075b825..0000000 --- a/lib/go_router/examples/books/src/data/library.dart +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'author.dart'; -import 'book.dart'; - -/// Library data mock. -final Library libraryInstance = Library() - ..addBook( - title: 'Left Hand of Darkness', - authorName: 'Ursula K. Le Guin', - isPopular: true, - isNew: true) - ..addBook( - title: 'Too Like the Lightning', - authorName: 'Ada Palmer', - isPopular: false, - isNew: true) - ..addBook( - title: 'Kindred', - authorName: 'Octavia E. Butler', - isPopular: true, - isNew: false) - ..addBook( - title: 'The Lathe of Heaven', - authorName: 'Ursula K. Le Guin', - isPopular: false, - isNew: false); - -/// A library that contains books and authors. -class Library { - /// The books in the library. - final List allBooks = []; - - /// The authors in the library. - final List allAuthors = []; - - /// Adds a book into the library. - void addBook({ - required String title, - required String authorName, - required bool isPopular, - required bool isNew, - }) { - final Author author = allAuthors.firstWhere( - (Author author) => author.name == authorName, - orElse: () { - final Author value = Author(id: allAuthors.length, name: authorName); - allAuthors.add(value); - return value; - }, - ); - - final Book book = Book( - id: allBooks.length, - title: title, - isPopular: isPopular, - isNew: isNew, - author: author, - ); - - author.books.add(book); - allBooks.add(book); - } - - /// The list of popular books in the library. - List get popularBooks => [ - ...allBooks.where((Book book) => book.isPopular), - ]; - - /// The list of new books in the library. - List get newBooks => [ - ...allBooks.where((Book book) => book.isNew), - ]; -} diff --git a/lib/go_router/examples/books/src/screens/author_details.dart b/lib/go_router/examples/books/src/screens/author_details.dart deleted file mode 100644 index 3aff898..0000000 --- a/lib/go_router/examples/books/src/screens/author_details.dart +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -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, - super.key, - }); - - /// The author to be displayed. - final Author? author; - - @override - Widget build(BuildContext context) { - if (author == null) { - return const Scaffold( - body: Center( - child: Text('No author found.'), - ), - ); - } - return Scaffold( - appBar: AppBar( - title: Text(author!.name), - ), - body: Center( - child: Column( - children: [ - Expanded( - child: BookList( - books: author!.books, - onTap: (Book book) => context.go('/book/${book.id}'), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/go_router/examples/books/src/screens/authors.dart b/lib/go_router/examples/books/src/screens/authors.dart deleted file mode 100644 index 0eeb1c3..0000000 --- a/lib/go_router/examples/books/src/screens/authors.dart +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.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({super.key}); - - /// The title of the screen. - static const String title = 'Authors'; - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - title: const Text(title), - ), - body: AuthorList( - authors: libraryInstance.allAuthors, - onTap: (Author author) { - context.go('/author/${author.id}'); - }, - ), - ); -} diff --git a/lib/go_router/examples/books/src/screens/book_details.dart b/lib/go_router/examples/books/src/screens/book_details.dart deleted file mode 100644 index 9a51a83..0000000 --- a/lib/go_router/examples/books/src/screens/book_details.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:url_launcher/link.dart'; - -import '../data.dart'; -import 'author_details.dart'; - -/// A screen to display book details. -class BookDetailsScreen extends StatelessWidget { - /// Creates a [BookDetailsScreen]. - const BookDetailsScreen({ - super.key, - this.book, - }); - - /// The book to be displayed. - final Book? book; - - @override - Widget build(BuildContext context) { - if (book == null) { - return const Scaffold( - body: Center( - child: Text('No book found.'), - ), - ); - } - return Scaffold( - appBar: AppBar( - title: Text(book!.title), - ), - body: Center( - child: Column( - children: [ - Text( - book!.title, - style: Theme.of(context).textTheme.headlineMedium, - ), - Text( - book!.author.name, - style: Theme.of(context).textTheme.titleMedium, - ), - TextButton( - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) => - AuthorDetailsScreen(author: book!.author), - ), - ); - }, - child: const Text('View author (navigator.push)'), - ), - Link( - uri: Uri.parse('/author/${book!.author.id}'), - builder: (BuildContext context, FollowLink? followLink) => - TextButton( - onPressed: followLink, - child: const Text('View author (Link)'), - ), - ), - TextButton( - onPressed: () { - context.push('/author/${book!.author.id}'); - }, - child: const Text('View author (GoRouter.push)'), - ), - ], - ), - ), - ); - } -} diff --git a/lib/go_router/examples/books/src/screens/books.dart b/lib/go_router/examples/books/src/screens/books.dart deleted file mode 100644 index b0e7ca2..0000000 --- a/lib/go_router/examples/books/src/screens/books.dart +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -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, {super.key}); - - /// Which tab to display. - final String kind; - - @override - State createState() => _BooksScreenState(); -} - -class _BooksScreenState extends State - with SingleTickerProviderStateMixin { - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(length: 3, vsync: this); - } - - @override - void didUpdateWidget(BooksScreen oldWidget) { - super.didUpdateWidget(oldWidget); - - switch (widget.kind) { - case 'popular': - _tabController.index = 0; - break; - - case 'new': - _tabController.index = 1; - break; - - case 'all': - _tabController.index = 2; - break; - } - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - title: const Text('Books'), - bottom: TabBar( - controller: _tabController, - onTap: _handleTabTapped, - tabs: const [ - Tab( - text: 'Popular', - icon: Icon(Icons.people), - ), - Tab( - text: 'New', - icon: Icon(Icons.new_releases), - ), - Tab( - text: 'All', - icon: Icon(Icons.list), - ), - ], - ), - ), - body: TabBarView( - controller: _tabController, - children: [ - BookList( - books: libraryInstance.popularBooks, - onTap: _handleBookTapped, - ), - BookList( - books: libraryInstance.newBooks, - onTap: _handleBookTapped, - ), - BookList( - books: libraryInstance.allBooks, - onTap: _handleBookTapped, - ), - ], - ), - ); - - void _handleBookTapped(Book book) { - context.go('/book/${book.id}'); - } - - void _handleTabTapped(int index) { - switch (index) { - case 1: - context.go('/books/new'); - break; - case 2: - context.go('/books/all'); - break; - case 0: - default: - context.go('/books/popular'); - break; - } - } -} diff --git a/lib/go_router/examples/books/src/screens/scaffold.dart b/lib/go_router/examples/books/src/screens/scaffold.dart deleted file mode 100644 index 2ffaf9a..0000000 --- a/lib/go_router/examples/books/src/screens/scaffold.dart +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:adaptive_navigation/adaptive_navigation.dart'; -import 'package:flutter/material.dart'; - -import 'package:go_router/go_router.dart'; - -/// 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, - super.key, - }); - - /// Which tab of the scaffold to display. - final ScaffoldTab selectedTab; - - /// The scaffold body. - final Widget child; - - @override - Widget build(BuildContext context) => Scaffold( - body: AdaptiveNavigationScaffold( - selectedIndex: selectedTab.index, - body: child, - onDestinationSelected: (int idx) { - switch (ScaffoldTab.values[idx]) { - case ScaffoldTab.books: - context.go('/books'); - break; - case ScaffoldTab.authors: - context.go('/authors'); - break; - case ScaffoldTab.settings: - context.go('/settings'); - break; - } - }, - destinations: const [ - AdaptiveScaffoldDestination( - title: 'Books', - icon: Icons.book, - ), - AdaptiveScaffoldDestination( - title: 'Authors', - icon: Icons.person, - ), - AdaptiveScaffoldDestination( - title: 'Settings', - icon: Icons.settings, - ), - ], - ), - ); -} diff --git a/lib/go_router/examples/books/src/screens/settings.dart b/lib/go_router/examples/books/src/screens/settings.dart deleted file mode 100644 index a098e30..0000000 --- a/lib/go_router/examples/books/src/screens/settings.dart +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:url_launcher/link.dart'; - -import '../auth.dart'; - -/// The settings screen. -class SettingsScreen extends StatefulWidget { - /// Creates a [SettingsScreen]. - const SettingsScreen({super.key}); - - @override - State createState() => _SettingsScreenState(); -} - -class _SettingsScreenState extends State { - @override - Widget build(BuildContext context) => Scaffold( - body: SafeArea( - child: SingleChildScrollView( - child: Align( - alignment: Alignment.topCenter, - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 400), - child: const Card( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 18, horizontal: 12), - child: SettingsContent(), - ), - ), - ), - ), - ), - ), - ); -} - -/// The content of a [SettingsScreen]. -class SettingsContent extends StatelessWidget { - /// Creates a [SettingsContent]. - const SettingsContent({ - super.key, - }); - - @override - Widget build(BuildContext context) => Column( - children: [ - ...[ - Text( - 'Settings', - style: Theme.of(context).textTheme.headlineMedium, - ), - ElevatedButton( - onPressed: () { - BookstoreAuthScope.of(context).signOut(); - }, - child: const Text('Sign out'), - ), - Link( - uri: Uri.parse('/book/0'), - builder: (BuildContext context, FollowLink? followLink) => - TextButton( - onPressed: followLink, - child: const Text('Go directly to /book/0 (Link)'), - ), - ), - TextButton( - onPressed: () { - context.go('/book/0'); - }, - child: const Text('Go directly to /book/0 (GoRouter)'), - ), - ].map((Widget w) => - Padding(padding: const EdgeInsets.all(8), child: w)), - TextButton( - onPressed: () => showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: const Text('Alert!'), - content: const Text('The alert description goes here.'), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context, 'Cancel'), - child: const Text('Cancel'), - ), - TextButton( - onPressed: () => Navigator.pop(context, 'OK'), - child: const Text('OK'), - ), - ], - ), - ), - child: const Text('Show Dialog'), - ) - ], - ); -} diff --git a/lib/go_router/examples/books/src/screens/sign_in.dart b/lib/go_router/examples/books/src/screens/sign_in.dart deleted file mode 100644 index e02c870..0000000 --- a/lib/go_router/examples/books/src/screens/sign_in.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/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, - super.key, - }); - - /// Called when users sign in with [Credentials]. - final ValueChanged onSignIn; - - @override - State createState() => _SignInScreenState(); -} - -class _SignInScreenState extends State { - final TextEditingController _usernameController = TextEditingController(); - final TextEditingController _passwordController = TextEditingController(); - - @override - Widget build(BuildContext context) => Scaffold( - body: Center( - child: Card( - child: Container( - constraints: BoxConstraints.loose(const Size(600, 600)), - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text('Sign in', - style: Theme.of(context).textTheme.headlineMedium), - TextField( - decoration: const InputDecoration(labelText: 'Username'), - controller: _usernameController, - ), - TextField( - decoration: const InputDecoration(labelText: 'Password'), - obscureText: true, - controller: _passwordController, - ), - Padding( - padding: const EdgeInsets.all(16), - child: TextButton( - onPressed: () async { - widget.onSignIn(Credentials( - _usernameController.value.text, - _passwordController.value.text)); - }, - child: const Text('Sign in'), - ), - ), - ], - ), - ), - ), - ), - ); -} diff --git a/lib/go_router/examples/books/src/widgets/author_list.dart b/lib/go_router/examples/books/src/widgets/author_list.dart deleted file mode 100644 index 371e30a..0000000 --- a/lib/go_router/examples/books/src/widgets/author_list.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; - -import '../data.dart'; - -/// The author list view. -class AuthorList extends StatelessWidget { - /// Creates an [AuthorList]. - const AuthorList({ - required this.authors, - this.onTap, - super.key, - }); - - /// The list of authors to be shown. - final List authors; - - /// Called when the user taps an author. - final ValueChanged? onTap; - - @override - Widget build(BuildContext context) => ListView.builder( - itemCount: authors.length, - itemBuilder: (BuildContext context, int index) => ListTile( - title: Text( - authors[index].name, - ), - subtitle: Text( - '${authors[index].books.length} books', - ), - onTap: onTap != null ? () => onTap!(authors[index]) : null, - ), - ); -} diff --git a/lib/go_router/examples/books/src/widgets/book_list.dart b/lib/go_router/examples/books/src/widgets/book_list.dart deleted file mode 100644 index 3e2761f..0000000 --- a/lib/go_router/examples/books/src/widgets/book_list.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; - -import '../data.dart'; - -/// The book list view. -class BookList extends StatelessWidget { - /// Creates an [BookList]. - const BookList({ - required this.books, - this.onTap, - super.key, - }); - - /// The list of books to be displayed. - final List books; - - /// Called when the user taps a book. - final ValueChanged? onTap; - - @override - Widget build(BuildContext context) => ListView.builder( - itemCount: books.length, - itemBuilder: (BuildContext context, int index) => ListTile( - title: Text( - books[index].title, - ), - subtitle: Text( - books[index].author.name, - ), - onTap: onTap != null ? () => onTap!(books[index]) : null, - ), - ); -} diff --git a/lib/go_router/examples/exception_handling.dart b/lib/go_router/examples/exception_handling.dart deleted file mode 100644 index 82c10e3..0000000 --- a/lib/go_router/examples/exception_handling.dart +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -/// This sample app shows how to use `GoRouter.onException` to redirect on -/// exception. -/// -/// The first route '/' is mapped to [HomeScreen], and the second route -/// '/404' is mapped to [NotFoundScreen]. -/// -/// Any other unknown route or exception is redirected to `/404`. -void main() => runApp(const MyApp()); - -/// The route configuration. -final GoRouter _router = GoRouter( - onException: (_, GoRouterState state, GoRouter router) { - router.go('/404', extra: state.uri.toString()); - }, - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) { - return const HomeScreen(); - }, - ), - GoRoute( - path: '/404', - builder: (BuildContext context, GoRouterState state) { - return NotFoundScreen(uri: state.extra as String? ?? ''); - }, - ), - ], -); - -/// The main app. -class MyApp extends StatelessWidget { - /// Constructs a [MyApp] - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - routerConfig: _router, - ); - } -} - -/// The home screen -class HomeScreen extends StatelessWidget { - /// Constructs a [HomeScreen] - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Home Screen')), - body: Center( - child: ElevatedButton( - onPressed: () => context.go('/some-unknown-route'), - child: const Text('Simulates user entering unknown url'), - ), - ), - ); - } -} - -/// The not found screen -class NotFoundScreen extends StatelessWidget { - /// Constructs a [HomeScreen] - const NotFoundScreen({super.key, required this.uri}); - - /// The uri that can not be found. - final String uri; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Page Not Found')), - body: Center( - child: Text("Can't find a page for: $uri"), - ), - ); - } -} diff --git a/lib/go_router/examples/input/decoration/code_decoration.dart b/lib/go_router/examples/input/decoration/code_decoration.dart deleted file mode 100644 index e1f35c4..0000000 --- a/lib/go_router/examples/input/decoration/code_decoration.dart +++ /dev/null @@ -1,175 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; - -abstract class CodeDecoration { - final Color activeColor; - final Color inactiveColor; - final Size cursorSize; - final Color cursorColor; - - final TextStyle textStyle; - final String? obscureText; - - CodeDecoration({ - required this.activeColor, - required this.inactiveColor, - required this.textStyle, - required this.cursorSize, - required this.cursorColor, - this.obscureText, - }); - - void paint( - Canvas canvas, Size size, int alpha, String text, int count, double gap) { - double boxWidth = (size.width - (count - 1) * gap) / count; - - /// 绘制装饰 - paintDecoration(canvas, size, text, count, gap); - - /// 绘制游标 - paintCursor(canvas, alpha,size, text.length, boxWidth, gap); - - /// 绘制文字 - paintText(canvas,size, text, boxWidth, gap); - } - - void paintCursor( - Canvas canvas, int alpha,Size size, int count, double boxWidth, double gap) { - Paint cursorPaint = Paint() - ..color = cursorColor.withAlpha(alpha) - ..strokeWidth = cursorSize.width - ..style = PaintingStyle.fill - ..isAntiAlias = true; - - double startX = count * (boxWidth + gap) + boxWidth / 2; - double startY = size.height/2-cursorSize.height/2; - var endX = startX + cursorSize.width; - var endY = size.height/2 + cursorSize.height/2; -// var endY = size.height - 28.0 - 12; -// canvas.drawLine(Offset(startX, startY), Offset(startX, endY), cursorPaint); - //绘制圆角光标 - Rect rect = Rect.fromLTRB(startX, startY, endX, endY); - RRect rrect = - RRect.fromRectAndRadius(rect, Radius.circular(cursorSize.width)); - canvas.drawRRect(rrect, cursorPaint); - } - - void paintText(Canvas canvas, Size size,String text, double boxWidth, double gap) { - /// 画文本 - double startX = 0.0; - /// Determine whether display obscureText. - bool obscure = obscureText != null; - - for (int i = 0; i < text.runes.length; i++) { - int rune = text.runes.elementAt(i); - String code = obscure ? obscureText! : String.fromCharCode(rune); - TextPainter textPainter = TextPainter( - text: TextSpan( - style: textStyle, - text: code, - ), - textAlign: TextAlign.center, - textDirection: TextDirection.ltr, - ); - - /// Layout the text. - textPainter.layout(); - var startY = size.height/2-textPainter.height/2; - startX = boxWidth * i + boxWidth / 2 - textPainter.width / 2 + gap * i; - textPainter.paint(canvas, Offset(startX, startY)); - } - } - - void paintDecoration( - Canvas canvas, Size size, String text, int count, double gap); -} - -class UnderlineCodeDecoration extends CodeDecoration { - final double strokeWidth; - - UnderlineCodeDecoration( - {required this.strokeWidth, - required super.activeColor, - required super.inactiveColor, - required super.cursorColor, - required super.textStyle, - required super.cursorSize, - super.obscureText}); - - @override - void paintDecoration( - Canvas canvas, Size size, String text, int count, double gap) { - Paint underlinePaint = Paint() - ..color = activeColor - ..strokeWidth = strokeWidth - ..style = PaintingStyle.stroke - ..isAntiAlias = true; - - /// 画下划线 - double singleWidth = (size.width - (count - 1) * gap) / count; - var startX = 0.0; - var startY = size.height; - - for (int i = 0; i < count; i++) { - if (i == text.length) { - underlinePaint.color = activeColor; - // underlinePaint.strokeWidth = strokeWidth; - } else { - underlinePaint.color = inactiveColor; - // underlinePaint.strokeWidth = strokeWidth; - } - canvas.drawLine(Offset(startX, startY), - Offset(startX + singleWidth, startY), underlinePaint); - startX += singleWidth + gap; - } - } -} - -class RRectCodeDecoration extends CodeDecoration { - final double strokeWidth; - final double height; - - RRectCodeDecoration( - {required this.height, - required this.strokeWidth, - required super.activeColor, - required super.inactiveColor, - required super.cursorColor, - required super.textStyle, - required super.cursorSize, - super.obscureText}); - - @override - void paintDecoration( - Canvas canvas, Size size, String text, int count, double gap) { - Paint paint = Paint() - ..color = activeColor - ..strokeWidth = strokeWidth - ..style = PaintingStyle.stroke - ..isAntiAlias = true; - - /// 画下划线 - double singleWidth = (size.width - (count - 1) * gap) / count; - var startX = 0.0; - var startY = size.height; - - for (int i = 0; i < count; i++) { - if (i == text.length) { - paint.color = activeColor; - } else { - paint.color = inactiveColor; - } - RRect rRect = RRect.fromRectAndRadius( - Rect.fromPoints( - Offset(startX, 0), - Offset(startX + singleWidth, startY), - ), - Radius.circular(6)); - canvas.drawRRect(rRect, paint); - // canvas.drawLine(Offset(startX, startY), - // Offset(startX + singleWidth, startY), underlinePaint); - startX += singleWidth + gap; - } - } -} diff --git a/lib/go_router/examples/input/input.dart b/lib/go_router/examples/input/input.dart deleted file mode 100644 index d0cadf6..0000000 --- a/lib/go_router/examples/input/input.dart +++ /dev/null @@ -1,215 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import 'decoration/code_decoration.dart'; -import 'input_painter.dart'; - -typedef AsyncSubmit = Future Function(String value); - -/// @desc 短信验证码输入框 -/// @time 2019-05-14 16:16 -/// @author Cheney -class TolyCodeInput extends StatefulWidget { - final int count; - - final AsyncSubmit onSubmit; - - final CodeDecoration decoration; - - final TextInputType keyboardType; - - final bool autoFocus; - - final FocusNode? focusNode; - - final TextInputAction textInputAction; - - const TolyCodeInput( - {this.count = 6, - required this.onSubmit, - this.decoration = const UnderlineDecoration(), - this.keyboardType = TextInputType.number, - this.focusNode, - this.autoFocus = false, - this.textInputAction = TextInputAction.done, - super.key}); - - @override - State createState() { - return TolyCodeInputState(); - } -} - -class TolyCodeInputState extends State - with SingleTickerProviderStateMixin { - ///输入监听器 - final TextEditingController _controller = TextEditingController(); - late AnimationController _animaCtrl; - late Animation _animation; - - late FocusNode _focusNode; - - @override - void initState() { - _focusNode = widget.focusNode ?? FocusNode(); - _controller.addListener(_listenValueChange); - initAnimation(); - super.initState(); - } - - void initAnimation() { - _animaCtrl = AnimationController( - duration: const Duration(milliseconds: 500), - vsync: this, - ); - _animation = IntTween(begin: 0, end: 255).animate(_animaCtrl) - ..addStatusListener(_stateChange); - _animaCtrl.forward(); - } - - void _listenValueChange() { - submit(_controller.text); - } - - void submit(String text) async{ - if (text.length >= widget.count) { - bool success = await widget.onSubmit.call(text.substring(0, widget.count)); - if(success){ - _focusNode.unfocus(); - }else{ - _controller.text = ""; - } - } - } - - @override - void dispose() { - /// Only execute when the controller is autoDispose. - _controller.dispose(); - _animaCtrl.dispose(); - _focusNode.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return CustomPaint( - foregroundPainter: InputPainter( - RRectCodeDecoration( - height: 56, - strokeWidth: 1, - activeColor: const Color(0xff010101), - inactiveColor: const Color(0xffB6B6B6), - cursorColor: Color(0xff3776E9), - textStyle: TextStyle(color: Colors.black), - cursorSize: Size(1, 24), - ), - controller: _controller, - count: widget.count, - decoration: widget.decoration, - alpha: _animation, - ), - child: RepaintBoundary( - child: TextField( - controller: _controller, - /// Fake the text style. - style: const TextStyle( - color: Colors.transparent, - ), - cursorColor: Colors.transparent, - cursorWidth: 0.0, - autocorrect: false, - textAlign: TextAlign.center, - enableInteractiveSelection: false, - maxLength: widget.count, - onSubmitted: submit, - keyboardType: widget.keyboardType, - focusNode: _focusNode, - autofocus: widget.autoFocus, - textInputAction: widget.textInputAction, - obscureText: true, - decoration: const InputDecoration( - counterText: '', - contentPadding: EdgeInsets.symmetric(vertical: 24), - border: OutlineInputBorder( - borderSide: BorderSide.none, - ), - ), - ), - ), - ); - } - - void _stateChange(AnimationStatus status) { - if (status == AnimationStatus.completed) { - _animaCtrl.reverse(); - } else if (status == AnimationStatus.dismissed) { - _animaCtrl.forward(); - } - } - - -} - -/// 默认的样式 -const TextStyle defaultStyle = TextStyle( - /// Default text color. - color: Color(0x80000000), - - /// Default text size. - fontSize: 24.0, -); - -abstract class CodeDecoration { - /// The style of painting text. - final TextStyle? textStyle; - - final ObscureStyle? obscureStyle; - - const CodeDecoration({ - this.textStyle, - this.obscureStyle, - }); -} - -/// The object determine the obscure display -class ObscureStyle { - /// Determine whether replace [obscureText] with number. - final bool isTextObscure; - - /// The display text when [isTextObscure] is true - final String obscureText; - - const ObscureStyle({ - this.isTextObscure = false, - this.obscureText = '*', - }) : assert(obscureText.length == 1); -} - -/// The object determine the underline color etc. -class UnderlineDecoration extends CodeDecoration { - /// The space between text and underline. - final double gapSpace; - - /// The color of the underline. - final Color color; - - /// The height of the underline. - final double lineHeight; - - /// The underline changed color when user enter pin. - final Color? enteredColor; - - const UnderlineDecoration({ - TextStyle? textStyle, - ObscureStyle? obscureStyle, - this.enteredColor = const Color(0xff3776E9), - this.gapSpace = 15.0, - this.color = const Color(0x24000000), - this.lineHeight = 0.5, - }) : super( - textStyle: textStyle, - obscureStyle: obscureStyle, - ); -} diff --git a/lib/go_router/examples/input/input_painter.dart b/lib/go_router/examples/input/input_painter.dart deleted file mode 100644 index 03233c1..0000000 --- a/lib/go_router/examples/input/input_painter.dart +++ /dev/null @@ -1,141 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -import 'decoration/code_decoration.dart' as c; -import 'input.dart'; - -class InputPainter extends CustomPainter { - final TextEditingController controller; - final int count; - final double space; - final CodeDecoration decoration; - final c.CodeDecoration underlineCodeDecoration; - final Animation alpha; - - InputPainter( - this.underlineCodeDecoration, { - required this.controller, - required this.count, - required this.decoration, - this.space = 4.0, - required this.alpha, - }) : super(repaint: Listenable.merge([controller, alpha])); - - void _drawUnderLine(Canvas canvas, Size size) { - /// Force convert to [UnderlineDecoration]. - var dr = decoration as UnderlineDecoration; - Paint underlinePaint = Paint() - ..color = dr.color - ..strokeWidth = dr.lineHeight - ..style = PaintingStyle.stroke - ..isAntiAlias = true; - - var startX = 0.0; - var startY = size.height; - - /// 画下划线 - double singleWidth = (size.width - (count - 1) * dr.gapSpace) / count; - - for (int i = 0; i < count; i++) { - if (i == controller.text.length && dr.enteredColor != null) { - underlinePaint.color = dr.enteredColor!; - underlinePaint.strokeWidth = 1; - } else { - underlinePaint.color = dr.color; - underlinePaint.strokeWidth = 0.5; - } - canvas.drawLine(Offset(startX, startY), - Offset(startX + singleWidth, startY), underlinePaint); - startX += singleWidth + dr.gapSpace; - } - - /// 画文本 - var index = 0; - startX = 0.0; - startY = 28; - - /// Determine whether display obscureText. - bool obscureOn; - obscureOn = decoration.obscureStyle != null && - decoration.obscureStyle!.isTextObscure; - - /// The text style of pin. - TextStyle textStyle; - if (decoration.textStyle == null) { - textStyle = defaultStyle; - } else { - textStyle = decoration.textStyle!; - } - - controller.text.runes.forEach((rune) { - String code; - if (obscureOn) { - code = decoration.obscureStyle!.obscureText; - } else { - code = String.fromCharCode(rune); - } - TextPainter textPainter = TextPainter( - text: TextSpan( - style: textStyle, - text: code, - ), - textAlign: TextAlign.center, - textDirection: TextDirection.ltr, - ); - - /// Layout the text. - textPainter.layout(); - - startX = singleWidth * index + - singleWidth / 2 - - textPainter.width / 2 + - dr.gapSpace * index; - textPainter.paint(canvas, Offset(startX, startY)); - index++; - }); - - ///画光标 如果外部有传,则直接使用外部 - Color cursorColor = dr.enteredColor ?? const Color(0xff3776E9); - cursorColor = cursorColor.withAlpha(alpha.value); - - double cursorWidth = 1; - double cursorHeight = 24; - - //LogUtil.v("animation.value=$alpha"); - - Paint cursorPaint = Paint() - ..color = cursorColor - ..strokeWidth = cursorWidth - ..style = PaintingStyle.stroke - ..isAntiAlias = true; - - startX = - controller.text.length * (singleWidth + dr.gapSpace) + singleWidth / 2; - - var endX = startX + cursorWidth; - var endY = startY + cursorHeight; -// var endY = size.height - 28.0 - 12; -// canvas.drawLine(Offset(startX, startY), Offset(startX, endY), cursorPaint); - //绘制圆角光标 - Rect rect = Rect.fromLTRB(startX, startY, endX, endY); - RRect rrect = RRect.fromRectAndRadius(rect, Radius.circular(cursorWidth)); - canvas.drawRRect(rrect, cursorPaint); - } - - @override - void paint(Canvas canvas, Size size) { - // _drawUnderLine(canvas, size); - underlineCodeDecoration.paint( - canvas, - size, - alpha.value, - controller.text, - count, - space, - ); - } - - @override - bool shouldRepaint(covariant InputPainter oldDelegate) { - return oldDelegate.controller.text != controller.text; - } -} diff --git a/lib/go_router/examples/input/main.dart b/lib/go_router/examples/input/main.dart deleted file mode 100644 index ba1a330..0000000 --- a/lib/go_router/examples/input/main.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'input.dart'; - -void main() => runApp(const MyApp()); - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - body: Center( - child: TolyCodeInput( - autoFocus: true, - onSubmit: (value) async { - print(value); - return true; - }, - )), - ), - ); - } -} diff --git a/lib/go_router/examples/main.dart b/lib/go_router/examples/main.dart deleted file mode 100644 index d159648..0000000 --- a/lib/go_router/examples/main.dart +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -/// This sample app shows an app with two screens. -/// -/// The first route '/' is mapped to [HomeScreen], and the second route -/// '/details' is mapped to [DetailsScreen]. -/// -/// The buttons use context.go() to navigate to each destination. On mobile -/// devices, each destination is deep-linkable and on the web, can be navigated -/// to using the address bar. -void main() => runApp(const MyApp()); - -/// The route configuration. -final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) { - return const HomeScreen(); - }, - routes: [ - GoRoute( - path: 'details', - builder: (BuildContext context, GoRouterState state) { - return const DetailsScreen(); - }, - ), - ], - ), - ], -); - -/// The main app. -class MyApp extends StatelessWidget { - /// Constructs a [MyApp] - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - routerConfig: _router, - ); - } -} - -/// The home screen -class HomeScreen extends StatelessWidget { - /// Constructs a [HomeScreen] - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Home Screen')), - body: Center( - child: ElevatedButton( - onPressed: () => context.go('/details'), - child: const Text('Go to the Details screen'), - ), - ), - ); - } -} - -/// The details screen -class DetailsScreen extends StatelessWidget { - /// Constructs a [DetailsScreen] - const DetailsScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Details Screen')), - body: Center( - child: ElevatedButton( - onPressed: () => context.go('/'), - child: const Text('Go back to the Home screen'), - ), - ), - ); - } -} diff --git a/lib/go_router/examples/named_routes.dart b/lib/go_router/examples/named_routes.dart deleted file mode 100644 index 9685fc4..0000000 --- a/lib/go_router/examples/named_routes.dart +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -// This scenario demonstrates how to navigate using named locations instead of -// URLs. -// -// Instead of hardcoding the URI locations , you can also use the named -// locations. To use this API, give a unique name to each GoRoute. The name can -// then be used in context.namedLocation to be translate back to the actual URL -// location. - -/// Family data class. -class Family { - /// Create a family. - const Family({required this.name, required this.people}); - - /// The last name of the family. - final String name; - - /// The people in the family. - final Map people; -} - -/// Person data class. -class Person { - /// Creates a person. - const Person({required this.name, required this.age}); - - /// The first name of the person. - final String name; - - /// The age of the person. - final int age; -} - -const Map _families = { - 'f1': Family( - name: 'Doe', - people: { - 'p1': Person(name: 'Jane', age: 23), - 'p2': Person(name: 'John', age: 6), - }, - ), - 'f2': Family( - name: 'Wong', - people: { - 'p1': Person(name: 'June', age: 51), - 'p2': Person(name: 'Xin', age: 44), - }, - ), -}; - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// The title of the app. - static const String title = 'GoRouter Example: Named Routes'; - - @override - Widget build(BuildContext context) => MaterialApp.router( - routerConfig: _router, - title: title, - debugShowCheckedModeBanner: false, - ); - - late final GoRouter _router = GoRouter( - debugLogDiagnostics: true, - routes: [ - GoRoute( - name: 'home', - path: '/', - builder: (BuildContext context, GoRouterState state) => - const HomeScreen(), - routes: [ - GoRoute( - name: 'family', - path: 'family/:fid', - builder: (BuildContext context, GoRouterState state) => - FamilyScreen(fid: state.pathParameters['fid']!), - routes: [ - GoRoute( - name: 'person', - path: 'person/:pid', - builder: (BuildContext context, GoRouterState state) { - return PersonScreen( - fid: state.pathParameters['fid']!, - pid: state.pathParameters['pid']!); - }, - ), - ], - ), - ], - ), - ], - ); -} - -/// The home screen that shows a list of families. -class HomeScreen extends StatelessWidget { - /// Creates a [HomeScreen]. - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text(App.title), - ), - body: ListView( - children: [ - for (final MapEntry entry in _families.entries) - ListTile( - title: Text(entry.value.name), - onTap: () => context.go(context.namedLocation('family', - pathParameters: {'fid': entry.key})), - ) - ], - ), - ); - } -} - -/// The screen that shows a list of persons in a family. -class FamilyScreen extends StatelessWidget { - /// Creates a [FamilyScreen]. - const FamilyScreen({required this.fid, super.key}); - - /// The id family to display. - final String fid; - - @override - Widget build(BuildContext context) { - final Map people = _families[fid]!.people; - return Scaffold( - appBar: AppBar(title: Text(_families[fid]!.name)), - body: ListView( - children: [ - for (final MapEntry entry in people.entries) - ListTile( - title: Text(entry.value.name), - onTap: () => context.go(context.namedLocation( - 'person', - pathParameters: {'fid': fid, 'pid': entry.key}, - queryParameters: {'qid': 'quid'}, - )), - ), - ], - ), - ); - } -} - -/// The person screen. -class PersonScreen extends StatelessWidget { - /// Creates a [PersonScreen]. - const PersonScreen({required this.fid, required this.pid, super.key}); - - /// The id of family this person belong to. - final String fid; - - /// The id of the person to be displayed. - final String pid; - - @override - Widget build(BuildContext context) { - final Family family = _families[fid]!; - final Person person = family.people[pid]!; - return Scaffold( - appBar: AppBar(title: Text(person.name)), - body: Text('${person.name} ${family.name} is ${person.age} years old'), - ); - } -} diff --git a/lib/go_router/examples/on_exit.dart b/lib/go_router/examples/on_exit.dart deleted file mode 100644 index fba83a7..0000000 --- a/lib/go_router/examples/on_exit.dart +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -/// This sample app demonstrates how to use GoRoute.onExit. -void main() => runApp(const MyApp()); - -/// The route configuration. -final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) { - return const HomeScreen(); - }, - routes: [ - GoRoute( - path: 'details', - builder: (BuildContext context, GoRouterState state) { - return const DetailsScreen(); - }, - onExit: (BuildContext context) async { - final bool? confirmed = await showDialog( - context: context, - builder: (_) { - return AlertDialog( - content: const Text('Are you sure to leave this page?'), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(false), - child: const Text('Cancel'), - ), - TextButton( - onPressed: () => Navigator.of(context).pop(true), - child: const Text('Confirm'), - ), - ], - ); - }, - ); - return confirmed ?? false; - }, - ), - GoRoute( - path: 'settings', - builder: (BuildContext context, GoRouterState state) { - return const SettingsScreen(); - }, - ), - ], - ), - ], -); - -/// The main app. -class MyApp extends StatelessWidget { - /// Constructs a [MyApp] - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - routerConfig: _router, - ); - } -} - -/// The home screen -class HomeScreen extends StatelessWidget { - /// Constructs a [HomeScreen] - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Home Screen')), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/details'), - child: const Text('Go to the Details screen'), - ), - ], - ), - ), - ); - } -} - -/// The details screen -class DetailsScreen extends StatelessWidget { - /// Constructs a [DetailsScreen] - const DetailsScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Details Screen')), - body: Center( - child: Column( - children: [ - TextButton( - onPressed: () { - context.pop(); - }, - child: const Text('go back'), - ), - TextButton( - onPressed: () { - context.go('/settings'); - }, - child: const Text('go to settings'), - ), - ], - )), - ); - } -} - -/// The settings screen -class SettingsScreen extends StatelessWidget { - /// Constructs a [SettingsScreen] - const SettingsScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Settings Screen')), - body: const Center( - child: Text('Settings'), - ), - ); - } -} diff --git a/lib/go_router/examples/others/custom_stateful_shell_route.dart b/lib/go_router/examples/others/custom_stateful_shell_route.dart deleted file mode 100644 index 5fbe2b6..0000000 --- a/lib/go_router/examples/others/custom_stateful_shell_route.dart +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -final GlobalKey _rootNavigatorKey = - GlobalKey(debugLabel: 'root'); -final GlobalKey _tabANavigatorKey = - GlobalKey(debugLabel: 'tabANav'); - -// This example demonstrates how to setup nested navigation using a -// BottomNavigationBar, where each bar item uses its own persistent navigator, -// i.e. navigation state is maintained separately for each item. This setup also -// enables deep linking into nested pages. -// -// This example also demonstrates how build a nested shell with a custom -// container for the branch Navigators (in this case a TabBarView). - -void main() { - runApp(NestedTabNavigationExampleApp()); -} - -/// An example demonstrating how to use nested navigators -class NestedTabNavigationExampleApp extends StatelessWidget { - /// Creates a NestedTabNavigationExampleApp - NestedTabNavigationExampleApp({super.key}); - - final GoRouter _router = GoRouter( - navigatorKey: _rootNavigatorKey, - initialLocation: '/a', - routes: [ - StatefulShellRoute( - builder: (BuildContext context, GoRouterState state, - StatefulNavigationShell navigationShell) { - // This nested StatefulShellRoute demonstrates the use of a - // custom container for the branch Navigators. In this implementation, - // no customization is done in the builder function (navigationShell - // itself is simply used as the Widget for the route). Instead, the - // navigatorContainerBuilder function below is provided to - // customize the container for the branch Navigators. - return navigationShell; - }, - navigatorContainerBuilder: (BuildContext context, - StatefulNavigationShell navigationShell, List children) { - // Returning a customized container for the branch - // Navigators (i.e. the `List children` argument). - // - // See ScaffoldWithNavBar for more details on how the children - // are managed (using AnimatedBranchContainer). - return ScaffoldWithNavBar( - navigationShell: navigationShell, children: children); - }, - branches: [ - // The route branch for the first tab of the bottom navigation bar. - StatefulShellBranch( - navigatorKey: _tabANavigatorKey, - routes: [ - GoRoute( - // The screen to display as the root in the first tab of the - // bottom navigation bar. - path: '/a', - builder: (BuildContext context, GoRouterState state) => - const RootScreenA(), - routes: [ - // The details screen to display stacked on navigator of the - // first tab. This will cover screen A but not the application - // shell (bottom navigation bar). - GoRoute( - path: 'details', - builder: (BuildContext context, GoRouterState state) => - const DetailsScreen(label: 'A'), - ), - ], - ), - ], - ), - - // The route branch for the third tab of the bottom navigation bar. - StatefulShellBranch( - // StatefulShellBranch will automatically use the first descendant - // GoRoute as the initial location of the branch. If another route - // is desired, specify the location of it using the defaultLocation - // parameter. - // defaultLocation: '/c2', - routes: [ - StatefulShellRoute( - builder: (BuildContext context, GoRouterState state, - StatefulNavigationShell navigationShell) { - // Just like with the top level StatefulShellRoute, no - // customization is done in the builder function. - return navigationShell; - }, - navigatorContainerBuilder: (BuildContext context, - StatefulNavigationShell navigationShell, - List children) { - // Returning a customized container for the branch - // Navigators (i.e. the `List children` argument). - // - // See TabbedRootScreen for more details on how the children - // are managed (in a TabBarView). - return TabbedRootScreen( - navigationShell: navigationShell, children: children); - }, - // This bottom tab uses a nested shell, wrapping sub routes in a - // top TabBar. - branches: [ - StatefulShellBranch(routes: [ - GoRoute( - path: '/b1', - builder: (BuildContext context, GoRouterState state) => - const TabScreen( - label: 'B1', detailsPath: '/b1/details'), - routes: [ - GoRoute( - path: 'details', - builder: - (BuildContext context, GoRouterState state) => - const DetailsScreen( - label: 'B1', - withScaffold: false, - ), - ), - ], - ), - ]), - StatefulShellBranch(routes: [ - GoRoute( - path: '/b2', - builder: (BuildContext context, GoRouterState state) => - const TabScreen( - label: 'B2', detailsPath: '/b2/details'), - routes: [ - GoRoute( - path: 'details', - builder: - (BuildContext context, GoRouterState state) => - const DetailsScreen( - label: 'B2', - withScaffold: false, - ), - ), - ], - ), - ]), - ], - ), - ], - ), - ], - ), - ], - ); - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - routerConfig: _router, - ); - } -} - -/// Builds the "shell" for the app by building a Scaffold with a -/// BottomNavigationBar, where [child] is placed in the body of the Scaffold. -class ScaffoldWithNavBar extends StatelessWidget { - /// Constructs an [ScaffoldWithNavBar]. - const ScaffoldWithNavBar({ - required this.navigationShell, - required this.children, - Key? key, - }) : super(key: key ?? const ValueKey('ScaffoldWithNavBar')); - - /// The navigation shell and container for the branch Navigators. - final StatefulNavigationShell navigationShell; - - /// The children (branch Navigators) to display in a custom container - /// ([AnimatedBranchContainer]). - final List children; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: AnimatedBranchContainer( - currentIndex: navigationShell.currentIndex, - children: children, - ), - bottomNavigationBar: BottomNavigationBar( - // Here, the items of BottomNavigationBar are hard coded. In a real - // world scenario, the items would most likely be generated from the - // branches of the shell route, which can be fetched using - // `navigationShell.route.branches`. - items: const [ - BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section A'), - BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section B'), - ], - currentIndex: navigationShell.currentIndex, - onTap: (int index) => _onTap(context, index), - ), - ); - } - - /// Navigate to the current location of the branch at the provided index when - /// tapping an item in the BottomNavigationBar. - void _onTap(BuildContext context, int index) { - // When navigating to a new branch, it's recommended to use the goBranch - // method, as doing so makes sure the last navigation state of the - // Navigator for the branch is restored. - navigationShell.goBranch( - index, - // A common pattern when using bottom navigation bars is to support - // navigating to the initial location when tapping the item that is - // already active. This example demonstrates how to support this behavior, - // using the initialLocation parameter of goBranch. - initialLocation: index == navigationShell.currentIndex, - ); - } -} - -/// Custom branch Navigator container that provides animated transitions -/// when switching branches. -class AnimatedBranchContainer extends StatelessWidget { - /// Creates a AnimatedBranchContainer - const AnimatedBranchContainer( - {super.key, required this.currentIndex, required this.children}); - - /// The index (in [children]) of the branch Navigator to display. - final int currentIndex; - - /// The children (branch Navigators) to display in this container. - final List children; - - @override - Widget build(BuildContext context) { - return Stack( - children: children.mapIndexed( - (int index, Widget navigator) { - return AnimatedScale( - scale: index == currentIndex ? 1 : 1.5, - duration: const Duration(milliseconds: 400), - child: AnimatedOpacity( - opacity: index == currentIndex ? 1 : 0, - duration: const Duration(milliseconds: 400), - child: _branchNavigatorWrapper(index, navigator), - ), - ); - }, - ).toList()); - } - - Widget _branchNavigatorWrapper(int index, Widget navigator) => IgnorePointer( - ignoring: index != currentIndex, - child: TickerMode( - enabled: index == currentIndex, - child: navigator, - ), - ); -} - -/// Widget for the root page for the first section of the bottom navigation bar. -class RootScreenA extends StatelessWidget { - /// Creates a RootScreenA - const RootScreenA({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Root of section A'), - ), - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Screen A', style: Theme.of(context).textTheme.titleLarge), - const Padding(padding: EdgeInsets.all(4)), - TextButton( - onPressed: () { - GoRouter.of(context).go('/a/details'); - }, - child: const Text('View details'), - ), - ], - ), - ), - ); - } -} - -/// The details screen for either the A or B screen. -class DetailsScreen extends StatefulWidget { - /// Constructs a [DetailsScreen]. - const DetailsScreen({ - required this.label, - this.param, - this.withScaffold = true, - super.key, - }); - - /// The label to display in the center of the screen. - final String label; - - /// Optional param - final String? param; - - /// Wrap in scaffold - final bool withScaffold; - - @override - State createState() => DetailsScreenState(); -} - -/// The state for DetailsScreen -class DetailsScreenState extends State { - int _counter = 0; - - @override - Widget build(BuildContext context) { - if (widget.withScaffold) { - return Scaffold( - appBar: AppBar( - title: Text('Details Screen - ${widget.label}'), - ), - body: _build(context), - ); - } else { - return Container( - color: Theme.of(context).scaffoldBackgroundColor, - child: _build(context), - ); - } - } - - Widget _build(BuildContext context) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Details for ${widget.label} - Counter: $_counter', - style: Theme.of(context).textTheme.titleLarge), - const Padding(padding: EdgeInsets.all(4)), - TextButton( - onPressed: () { - setState(() { - _counter++; - }); - }, - child: const Text('Increment counter'), - ), - const Padding(padding: EdgeInsets.all(8)), - if (widget.param != null) - Text('Parameter: ${widget.param!}', - style: Theme.of(context).textTheme.titleMedium), - const Padding(padding: EdgeInsets.all(8)), - if (!widget.withScaffold) ...[ - const Padding(padding: EdgeInsets.all(16)), - TextButton( - onPressed: () { - GoRouter.of(context).pop(); - }, - child: const Text('< Back', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), - ), - ] - ], - ), - ); - } -} - -/// Builds a nested shell using a [TabBar] and [TabBarView]. -class TabbedRootScreen extends StatefulWidget { - /// Constructs a TabbedRootScreen - const TabbedRootScreen( - {required this.navigationShell, required this.children, super.key}); - - /// The current state of the parent StatefulShellRoute. - final StatefulNavigationShell navigationShell; - - /// The children (branch Navigators) to display in the [TabBarView]. - final List children; - - @override - State createState() => _TabbedRootScreenState(); -} - -class _TabbedRootScreenState extends State - with SingleTickerProviderStateMixin { - late final TabController _tabController = TabController( - length: widget.children.length, - vsync: this, - initialIndex: widget.navigationShell.currentIndex); - - @override - void didUpdateWidget(covariant TabbedRootScreen oldWidget) { - super.didUpdateWidget(oldWidget); - _tabController.index = widget.navigationShell.currentIndex; - } - - @override - Widget build(BuildContext context) { - final List tabs = widget.children - .mapIndexed((int i, _) => Tab(text: 'Tab ${i + 1}')) - .toList(); - - return Scaffold( - appBar: AppBar( - title: const Text('Root of Section B (nested TabBar shell)'), - bottom: TabBar( - controller: _tabController, - tabs: tabs, - onTap: (int tappedIndex) => _onTabTap(context, tappedIndex), - )), - body: TabBarView( - controller: _tabController, - children: widget.children, - ), - ); - } - - void _onTabTap(BuildContext context, int index) { - widget.navigationShell.goBranch(index); - } -} - -/// Widget for the pages in the top tab bar. -class TabScreen extends StatelessWidget { - /// Creates a RootScreen - const TabScreen({required this.label, required this.detailsPath, super.key}); - - /// The label - final String label; - - /// The path to the detail page - final String detailsPath; - - @override - Widget build(BuildContext context) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Screen $label', style: Theme.of(context).textTheme.titleLarge), - const Padding(padding: EdgeInsets.all(4)), - TextButton( - onPressed: () { - GoRouter.of(context).go(detailsPath); - }, - child: const Text('View details'), - ), - ], - ), - ); - } -} diff --git a/lib/go_router/examples/others/error_screen.dart b/lib/go_router/examples/others/error_screen.dart deleted file mode 100644 index 5c071fc..0000000 --- a/lib/go_router/examples/others/error_screen.dart +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// The title of the app. - static const String title = 'GoRouter Example: Custom Error Screen'; - - @override - Widget build(BuildContext context) => MaterialApp.router( - routerConfig: _router, - title: title, - ); - - final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) => - const Page1Screen(), - ), - GoRoute( - path: '/page2', - builder: (BuildContext context, GoRouterState state) => - const Page2Screen(), - ), - ], - errorBuilder: (BuildContext context, GoRouterState state) => - ErrorScreen(state.error!), - ); -} - -/// The screen of the first page. -class Page1Screen extends StatelessWidget { - /// Creates a [Page1Screen]. - const Page1Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/page2'), - child: const Text('Go to page 2'), - ), - ], - ), - ), - ); -} - -/// The screen of the second page. -class Page2Screen extends StatelessWidget { - /// Creates a [Page2Screen]. - const Page2Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/'), - child: const Text('Go to home page'), - ), - ], - ), - ), - ); -} - -/// The screen of the error page. -class ErrorScreen extends StatelessWidget { - /// Creates an [ErrorScreen]. - const ErrorScreen(this.error, {super.key}); - - /// The error to display. - final Exception error; - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text('My "Page Not Found" Screen')), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SelectableText(error.toString()), - TextButton( - onPressed: () => context.go('/'), - child: const Text('Home'), - ), - ], - ), - ), - ); -} diff --git a/lib/go_router/examples/others/extra_param.dart b/lib/go_router/examples/others/extra_param.dart deleted file mode 100644 index eaef755..0000000 --- a/lib/go_router/examples/others/extra_param.dart +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -/// Family data class. -class Family { - /// Create a family. - const Family({required this.name, required this.people}); - - /// The last name of the family. - final String name; - - /// The people in the family. - final Map people; -} - -/// Person data class. -class Person { - /// Creates a person. - const Person({required this.name, required this.age}); - - /// The first name of the person. - final String name; - - /// The age of the person. - final int age; -} - -const Map _families = { - 'f1': Family( - name: 'Doe', - people: { - 'p1': Person(name: 'Jane', age: 23), - 'p2': Person(name: 'John', age: 6), - }, - ), - 'f2': Family( - name: 'Wong', - people: { - 'p1': Person(name: 'June', age: 51), - 'p2': Person(name: 'Xin', age: 44), - }, - ), -}; - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// The title of the app. - static const String title = 'GoRouter Example: Extra Parameter'; - - @override - Widget build(BuildContext context) => MaterialApp.router( - routerConfig: _router, - title: title, - ); - - late final GoRouter _router = GoRouter( - routes: [ - GoRoute( - name: 'home', - path: '/', - builder: (BuildContext context, GoRouterState state) => - const HomeScreen(), - routes: [ - GoRoute( - name: 'family', - path: 'family', - builder: (BuildContext context, GoRouterState state) { - final Map params = - state.extra! as Map; - final String fid = params['fid']! as String; - return FamilyScreen(fid: fid); - }, - ), - ], - ), - ], - ); -} - -/// The home screen that shows a list of families. -class HomeScreen extends StatelessWidget { - /// Creates a [HomeScreen]. - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: ListView( - children: [ - for (final MapEntry entry in _families.entries) - ListTile( - title: Text(entry.value.name), - onTap: () => context.goNamed('family', - extra: {'fid': entry.key}), - ) - ], - ), - ); -} - -/// The screen that shows a list of persons in a family. -class FamilyScreen extends StatelessWidget { - /// Creates a [FamilyScreen]. - const FamilyScreen({required this.fid, super.key}); - - /// The family to display. - final String fid; - - @override - Widget build(BuildContext context) { - final Map people = _families[fid]!.people; - return Scaffold( - appBar: AppBar(title: Text(_families[fid]!.name)), - body: ListView( - children: [ - for (final Person p in people.values) - ListTile( - title: Text(p.name), - ), - ], - ), - ); - } -} diff --git a/lib/go_router/examples/others/init_loc.dart b/lib/go_router/examples/others/init_loc.dart deleted file mode 100644 index 4f61b00..0000000 --- a/lib/go_router/examples/others/init_loc.dart +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// The title of the app. - static const String title = 'GoRouter Example: Initial Location'; - - @override - Widget build(BuildContext context) => MaterialApp.router( - routerConfig: _router, - title: title, - ); - - final GoRouter _router = GoRouter( - initialLocation: '/page3', - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) => - const Page1Screen(), - ), - GoRoute( - path: '/page2', - builder: (BuildContext context, GoRouterState state) => - const Page2Screen(), - ), - GoRoute( - path: '/page3', - builder: (BuildContext context, GoRouterState state) => - const Page3Screen(), - ), - ], - ); -} - -/// The screen of the first page. -class Page1Screen extends StatelessWidget { - /// Creates a [Page1Screen]. - const Page1Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/page2'), - child: const Text('Go to page 2'), - ), - ], - ), - ), - ); -} - -/// The screen of the second page. -class Page2Screen extends StatelessWidget { - /// Creates a [Page2Screen]. - const Page2Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/'), - child: const Text('Go to home page'), - ), - ], - ), - ), - ); -} - -/// The screen of the third page. -class Page3Screen extends StatelessWidget { - /// Creates a [Page3Screen]. - const Page3Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/page2'), - child: const Text('Go to page 2'), - ), - ], - ), - ), - ); -} diff --git a/lib/go_router/examples/others/nav_observer.dart b/lib/go_router/examples/others/nav_observer.dart deleted file mode 100644 index 038d337..0000000 --- a/lib/go_router/examples/others/nav_observer.dart +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:logging/logging.dart'; - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// The title of the app. - static const String title = 'GoRouter Example: Navigator Observer'; - - @override - Widget build(BuildContext context) => MaterialApp.router( - routerConfig: _router, - title: title, - ); - - final GoRouter _router = GoRouter( - observers: [MyNavObserver()], - routes: [ - GoRoute( - // if there's no name, path will be used as name for observers - path: '/', - builder: (BuildContext context, GoRouterState state) => - const Page1Screen(), - routes: [ - GoRoute( - name: 'page2', - path: 'page2/:p1', - builder: (BuildContext context, GoRouterState state) => - const Page2Screen(), - routes: [ - GoRoute( - name: 'page3', - path: 'page3', - builder: (BuildContext context, GoRouterState state) => - const Page3Screen(), - ), - ], - ), - ], - ), - ], - ); -} - -/// The Navigator observer. -class MyNavObserver extends NavigatorObserver { - /// Creates a [MyNavObserver]. - MyNavObserver() { - log.onRecord.listen((LogRecord e) => debugPrint('$e')); - } - - /// The logged message. - final Logger log = Logger('MyNavObserver'); - - @override - void didPush(Route route, Route? previousRoute) => - log.info('didPush: ${route.str}, previousRoute= ${previousRoute?.str}'); - - @override - void didPop(Route route, Route? previousRoute) => - log.info('didPop: ${route.str}, previousRoute= ${previousRoute?.str}'); - - @override - void didRemove(Route route, Route? previousRoute) => - log.info('didRemove: ${route.str}, previousRoute= ${previousRoute?.str}'); - - @override - void didReplace({Route? newRoute, Route? oldRoute}) => - log.info('didReplace: new= ${newRoute?.str}, old= ${oldRoute?.str}'); - - @override - void didStartUserGesture( - Route route, - Route? previousRoute, - ) => - log.info('didStartUserGesture: ${route.str}, ' - 'previousRoute= ${previousRoute?.str}'); - - @override - void didStopUserGesture() => log.info('didStopUserGesture'); -} - -extension on Route { - String get str => 'route(${settings.name}: ${settings.arguments})'; -} - -/// The screen of the first page. -class Page1Screen extends StatelessWidget { - /// Creates a [Page1Screen]. - const Page1Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.goNamed( - 'page2', - pathParameters: {'p1': 'pv1'}, - queryParameters: {'q1': 'qv1'}, - ), - child: const Text('Go to page 2'), - ), - ], - ), - ), - ); -} - -/// The screen of the second page. -class Page2Screen extends StatelessWidget { - /// Creates a [Page2Screen]. - const Page2Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.goNamed( - 'page3', - pathParameters: {'p1': 'pv2'}, - ), - child: const Text('Go to page 3'), - ), - ], - ), - ), - ); -} - -/// The screen of the third page. -class Page3Screen extends StatelessWidget { - /// Creates a [Page3Screen]. - const Page3Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/'), - child: const Text('Go to home page'), - ), - ], - ), - ), - ); -} diff --git a/lib/go_router/examples/others/push.dart b/lib/go_router/examples/others/push.dart deleted file mode 100644 index 0cea894..0000000 --- a/lib/go_router/examples/others/push.dart +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// The title of the app. - static const String title = 'GoRouter Example: Push'; - - @override - Widget build(BuildContext context) => MaterialApp.router( - routerConfig: _router, - title: title, - ); - - late final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) => - const Page1ScreenWithPush(), - ), - GoRoute( - path: '/page2', - builder: (BuildContext context, GoRouterState state) => - Page2ScreenWithPush( - int.parse(state.uri.queryParameters['push-count']!), - ), - ), - ], - ); -} - -/// The screen of the first page. -class Page1ScreenWithPush extends StatelessWidget { - /// Creates a [Page1ScreenWithPush]. - const Page1ScreenWithPush({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text('${App.title}: page 1')), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.push('/page2?push-count=1'), - child: const Text('Push page 2'), - ), - ], - ), - ), - ); -} - -/// The screen of the second page. -class Page2ScreenWithPush extends StatelessWidget { - /// Creates a [Page2ScreenWithPush]. - const Page2ScreenWithPush(this.pushCount, {super.key}); - - /// The push count. - final int pushCount; - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - title: Text('${App.title}: page 2 w/ push count $pushCount'), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: ElevatedButton( - onPressed: () => context.go('/'), - child: const Text('Go to home page'), - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: ElevatedButton( - onPressed: () => context.push( - '/page2?push-count=${pushCount + 1}', - ), - child: const Text('Push page 2 (again)'), - ), - ), - ], - ), - ), - ); -} diff --git a/lib/go_router/examples/others/router_neglect.dart b/lib/go_router/examples/others/router_neglect.dart deleted file mode 100644 index 75686a1..0000000 --- a/lib/go_router/examples/others/router_neglect.dart +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// The title of the app. - static const String title = 'GoRouter Example: Router neglect'; - - @override - Widget build(BuildContext context) => MaterialApp.router( - routerConfig: _router, - title: title, - ); - - final GoRouter _router = GoRouter( - // turn off history tracking in the browser for this navigation - routerNeglect: true, - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) => - const Page1Screen(), - ), - GoRoute( - path: '/page2', - builder: (BuildContext context, GoRouterState state) => - const Page2Screen(), - ), - ], - ); -} - -/// The screen of the first page. -class Page1Screen extends StatelessWidget { - /// Creates a [Page1Screen]. - const Page1Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/page2'), - child: const Text('Go to page 2'), - ), - const SizedBox(height: 8), - ElevatedButton( - // turn off history tracking in the browser for this navigation; - // note that this isn't necessary when you've set routerNeglect - // but it does illustrate the technique - onPressed: () => Router.neglect( - context, - () => context.push('/page2'), - ), - child: const Text('Push page 2'), - ), - ], - ), - ), - ); -} - -/// The screen of the second page. -class Page2Screen extends StatelessWidget { - /// Creates a [Page2Screen]. - const Page2Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/'), - child: const Text('Go to home page'), - ), - ], - ), - ), - ); -} diff --git a/lib/go_router/examples/others/state_restoration.dart b/lib/go_router/examples/others/state_restoration.dart deleted file mode 100644 index 93e8aca..0000000 --- a/lib/go_router/examples/others/state_restoration.dart +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -void main() => runApp( - const RootRestorationScope(restorationId: 'root', child: App()), - ); - -/// The main app. -class App extends StatefulWidget { - /// Creates an [App]. - const App({super.key}); - - /// The title of the app. - static const String title = 'GoRouter Example: State Restoration'; - - @override - State createState() => _AppState(); -} - -class _AppState extends State with RestorationMixin { - @override - String get restorationId => 'wrapper'; - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - // Implement restoreState for your app - } - - @override - Widget build(BuildContext context) => MaterialApp.router( - routerConfig: _router, - title: App.title, - restorationScopeId: 'app', - ); - - final GoRouter _router = GoRouter( - routes: [ - // restorationId set for the route automatically - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) => - const Page1Screen(), - ), - - // restorationId set for the route automatically - GoRoute( - path: '/page2', - builder: (BuildContext context, GoRouterState state) => - const Page2Screen(), - ), - ], - restorationScopeId: 'router', - ); -} - -/// The screen of the first page. -class Page1Screen extends StatelessWidget { - /// Creates a [Page1Screen]. - const Page1Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/page2'), - child: const Text('Go to page 2'), - ), - ], - ), - ), - ); -} - -/// The screen of the second page. -class Page2Screen extends StatelessWidget { - /// Creates a [Page2Screen]. - const Page2Screen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/'), - child: const Text('Go to home page'), - ), - ], - ), - ), - ); -} diff --git a/lib/go_router/examples/others/stateful_shell_state_restoration.dart b/lib/go_router/examples/others/stateful_shell_state_restoration.dart deleted file mode 100644 index aeecd11..0000000 --- a/lib/go_router/examples/others/stateful_shell_state_restoration.dart +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -void main() => runApp(RestorableStatefulShellRouteExampleApp()); - -/// An example demonstrating how to use StatefulShellRoute with state -/// restoration. -class RestorableStatefulShellRouteExampleApp extends StatelessWidget { - /// Creates a NestedTabNavigationExampleApp - RestorableStatefulShellRouteExampleApp({super.key}); - - final GoRouter _router = GoRouter( - initialLocation: '/a', - restorationScopeId: 'router', - routes: [ - StatefulShellRoute.indexedStack( - restorationScopeId: 'shell1', - pageBuilder: (BuildContext context, GoRouterState state, - StatefulNavigationShell navigationShell) { - return MaterialPage( - restorationId: 'shellWidget1', - child: ScaffoldWithNavBar(navigationShell: navigationShell)); - }, - branches: [ - // The route branch for the first tab of the bottom navigation bar. - StatefulShellBranch( - restorationScopeId: 'branchA', - routes: [ - GoRoute( - // The screen to display as the root in the first tab of the - // bottom navigation bar. - path: '/a', - pageBuilder: (BuildContext context, GoRouterState state) => - const MaterialPage( - restorationId: 'screenA', - child: - RootScreen(label: 'A', detailsPath: '/a/details')), - routes: [ - // The details screen to display stacked on navigator of the - // first tab. This will cover screen A but not the application - // shell (bottom navigation bar). - GoRoute( - path: 'details', - pageBuilder: (BuildContext context, GoRouterState state) => - const MaterialPage( - restorationId: 'screenADetail', - child: DetailsScreen(label: 'A')), - ), - ], - ), - ], - ), - // The route branch for the second tab of the bottom navigation bar. - StatefulShellBranch( - restorationScopeId: 'branchB', - routes: [ - GoRoute( - // The screen to display as the root in the second tab of the - // bottom navigation bar. - path: '/b', - pageBuilder: (BuildContext context, GoRouterState state) => - const MaterialPage( - restorationId: 'screenB', - child: - RootScreen(label: 'B', detailsPath: '/b/details')), - routes: [ - // The details screen to display stacked on navigator of the - // first tab. This will cover screen A but not the application - // shell (bottom navigation bar). - GoRoute( - path: 'details', - pageBuilder: (BuildContext context, GoRouterState state) => - const MaterialPage( - restorationId: 'screenBDetail', - child: DetailsScreen(label: 'B')), - ), - ], - ), - ], - ), - ], - ), - ], - ); - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - restorationScopeId: 'app', - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - routerConfig: _router, - ); - } -} - -/// Builds the "shell" for the app by building a Scaffold with a -/// BottomNavigationBar, where [child] is placed in the body of the Scaffold. -class ScaffoldWithNavBar extends StatelessWidget { - /// Constructs an [ScaffoldWithNavBar]. - const ScaffoldWithNavBar({ - required this.navigationShell, - Key? key, - }) : super(key: key ?? const ValueKey('ScaffoldWithNavBar')); - - /// The navigation shell and container for the branch Navigators. - final StatefulNavigationShell navigationShell; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: navigationShell, - bottomNavigationBar: BottomNavigationBar( - items: const [ - BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section A'), - BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section B'), - ], - currentIndex: navigationShell.currentIndex, - onTap: (int tappedIndex) => navigationShell.goBranch(tappedIndex), - ), - ); - } -} - -/// Widget for the root/initial pages in the bottom navigation bar. -class RootScreen extends StatelessWidget { - /// Creates a RootScreen - const RootScreen({ - required this.label, - required this.detailsPath, - super.key, - }); - - /// The label - final String label; - - /// The path to the detail page - final String detailsPath; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Root of section $label'), - ), - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Screen $label', - style: Theme.of(context).textTheme.titleLarge), - const Padding(padding: EdgeInsets.all(4)), - TextButton( - onPressed: () { - GoRouter.of(context).go(detailsPath); - }, - child: const Text('View details'), - ), - ], - ), - ), - ); - } -} - -/// The details screen for either the A or B screen. -class DetailsScreen extends StatefulWidget { - /// Constructs a [DetailsScreen]. - const DetailsScreen({ - required this.label, - super.key, - }); - - /// The label to display in the center of the screen. - final String label; - - @override - State createState() => DetailsScreenState(); -} - -/// The state for DetailsScreen -class DetailsScreenState extends State with RestorationMixin { - final RestorableInt _counter = RestorableInt(0); - - @override - String? get restorationId => 'DetailsScreen-${widget.label}'; - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - registerForRestoration(_counter, 'counter'); - } - - @override - void dispose() { - super.dispose(); - _counter.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Details Screen - ${widget.label}'), - ), - body: _build(context), - ); - } - - Widget _build(BuildContext context) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Details for ${widget.label} - Counter: ${_counter.value}', - style: Theme.of(context).textTheme.titleLarge), - const Padding(padding: EdgeInsets.all(4)), - TextButton( - onPressed: () { - setState(() { - _counter.value++; - }); - }, - child: const Text('Increment counter'), - ), - const Padding(padding: EdgeInsets.all(8)), - ], - ), - ); - } -} diff --git a/lib/go_router/examples/others/transitions.dart b/lib/go_router/examples/others/transitions.dart deleted file mode 100644 index e4b9a3e..0000000 --- a/lib/go_router/examples/others/transitions.dart +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// The title of the app. - static const String title = 'GoRouter Example: Custom Transitions'; - - @override - Widget build(BuildContext context) => MaterialApp.router( - routerConfig: _router, - title: title, - ); - - final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - redirect: (_, __) => '/none', - ), - GoRoute( - path: '/fade', - pageBuilder: (BuildContext context, GoRouterState state) => - CustomTransitionPage( - key: state.pageKey, - child: const ExampleTransitionsScreen( - kind: 'fade', - color: Colors.red, - ), - transitionsBuilder: (BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child) => - FadeTransition(opacity: animation, child: child), - ), - ), - GoRoute( - path: '/scale', - pageBuilder: (BuildContext context, GoRouterState state) => - CustomTransitionPage( - key: state.pageKey, - child: const ExampleTransitionsScreen( - kind: 'scale', - color: Colors.green, - ), - transitionsBuilder: (BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child) => - ScaleTransition(scale: animation, child: child), - ), - ), - GoRoute( - path: '/slide', - pageBuilder: (BuildContext context, GoRouterState state) => - CustomTransitionPage( - key: state.pageKey, - child: const ExampleTransitionsScreen( - kind: 'slide', - color: Colors.yellow, - ), - transitionsBuilder: (BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child) => - SlideTransition( - position: animation.drive( - Tween( - begin: const Offset(0.25, 0.25), - end: Offset.zero, - ).chain(CurveTween(curve: Curves.easeIn)), - ), - child: child, - ), - ), - ), - GoRoute( - path: '/rotation', - pageBuilder: (BuildContext context, GoRouterState state) => - CustomTransitionPage( - key: state.pageKey, - child: const ExampleTransitionsScreen( - kind: 'rotation', - color: Colors.purple, - ), - transitionsBuilder: (BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child) => - RotationTransition(turns: animation, child: child), - ), - ), - GoRoute( - path: '/none', - pageBuilder: (BuildContext context, GoRouterState state) => - NoTransitionPage( - key: state.pageKey, - child: const ExampleTransitionsScreen( - kind: 'none', - color: Colors.white, - ), - ), - ), - ], - ); -} - -/// An Example transitions screen. -class ExampleTransitionsScreen extends StatelessWidget { - /// Creates an [ExampleTransitionsScreen]. - const ExampleTransitionsScreen({ - required this.color, - required this.kind, - super.key, - }); - - /// The available transition kinds. - static final List kinds = [ - 'fade', - 'scale', - 'slide', - 'rotation', - 'none' - ]; - - /// The color of the container. - final Color color; - - /// The transition kind. - final String kind; - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: Text('${App.title}: $kind')), - body: Container( - color: color, - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - for (final String kind in kinds) - Padding( - padding: const EdgeInsets.all(8), - child: ElevatedButton( - onPressed: () => context.go('/$kind'), - child: Text('$kind transition'), - ), - ) - ], - ), - ), - ), - ); -} diff --git a/lib/go_router/examples/path_and_query_parameters.dart b/lib/go_router/examples/path_and_query_parameters.dart deleted file mode 100644 index 3a8f289..0000000 --- a/lib/go_router/examples/path_and_query_parameters.dart +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -// This scenario demonstrates how to use path parameters and query parameters. -// -// The route segments that start with ':' are treated as path parameters when -// defining GoRoute[s]. The parameter values can be accessed through -// GoRouterState.pathParameters. -// -// The query parameters are automatically stored in GoRouterState.queryParameters. - -/// Family data class. -class Family { - /// Create a family. - const Family({required this.name, required this.people}); - - /// The last name of the family. - final String name; - - /// The people in the family. - final Map people; -} - -/// Person data class. -class Person { - /// Creates a person. - const Person({required this.name, required this.age}); - - /// The first name of the person. - final String name; - - /// The age of the person. - final int age; -} - -const Map _families = { - 'f1': Family( - name: 'Doe', - people: { - 'p1': Person(name: 'Jane', age: 23), - 'p2': Person(name: 'John', age: 6), - }, - ), - 'f2': Family( - name: 'Wong', - people: { - 'p1': Person(name: 'June', age: 51), - 'p2': Person(name: 'Xin', age: 44), - }, - ), -}; - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - /// 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) => MaterialApp.router( - routerConfig: _router, - title: title, - debugShowCheckedModeBanner: false, - ); - - late final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) => - const HomeScreen(), - routes: [ - GoRoute( - name: 'family', - path: 'family/:fid', - builder: (BuildContext context, GoRouterState state) { - return FamilyScreen( - fid: state.pathParameters['fid']!, - asc: state.uri.queryParameters['sort'] == 'asc', - ); - }), - ], - ), - ], - ); -} - -/// The home screen that shows a list of families. -class HomeScreen extends StatelessWidget { - /// Creates a [HomeScreen]. - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text(App.title), - ), - body: ListView( - children: [ - for (final MapEntry entry in _families.entries) - ListTile( - title: Text(entry.value.name), - onTap: () => context.go('/family/${entry.key}'), - ) - ], - ), - ); - } -} - -/// The screen that shows a list of persons in a family. -class FamilyScreen extends StatelessWidget { - /// Creates a [FamilyScreen]. - const FamilyScreen({required this.fid, required this.asc, super.key}); - - /// The family to display. - final String fid; - - /// Whether to sort the name in ascending order. - final bool asc; - - @override - Widget build(BuildContext context) { - final Map newQueries; - final List names = _families[fid]! - .people - .values - .map((Person p) => p.name) - .toList(); - names.sort(); - if (asc) { - newQueries = const {'sort': 'desc'}; - } else { - newQueries = const {'sort': 'asc'}; - } - return Scaffold( - appBar: AppBar( - title: Text(_families[fid]!.name), - actions: [ - IconButton( - onPressed: () => context.goNamed('family', - pathParameters: {'fid': fid}, - queryParameters: newQueries), - tooltip: 'sort ascending or descending', - icon: const Icon(Icons.sort), - ) - ], - ), - body: ListView( - children: [ - for (final String name in asc ? names : names.reversed) - ListTile( - title: Text(name), - ), - ], - ), - ); - } -} diff --git a/lib/go_router/examples/redirection.dart b/lib/go_router/examples/redirection.dart deleted file mode 100644 index aded4b7..0000000 --- a/lib/go_router/examples/redirection.dart +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:provider/provider.dart'; - -// This scenario demonstrates how to use redirect to handle a sign-in flow. -// -// The GoRouter.redirect method is called before the app is navigate to a -// new page. You can choose to redirect to a different page by returning a -// non-null URL string. - -/// The login information. -class LoginInfo extends ChangeNotifier { - /// 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(); - } -} - -void main() => runApp(App()); - -/// The main app. -class App extends StatelessWidget { - /// Creates an [App]. - App({super.key}); - - 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.value( - value: _loginInfo, - child: MaterialApp.router( - routerConfig: _router, - title: title, - debugShowCheckedModeBanner: false, - ), - ); - - late final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) => - const HomeScreen(), - ), - GoRoute( - path: '/login', - builder: (BuildContext context, GoRouterState state) => - const LoginScreen(), - ), - ], - - // redirect to the login page if the user is not logged in - redirect: (BuildContext context, GoRouterState state) { - // if the user is not logged in, they need to login - final bool loggedIn = _loginInfo.loggedIn; - final bool loggingIn = state.matchedLocation == '/login'; - if (!loggedIn) { - return '/login'; - } - - // if the user is logged in but still on the login page, send them to - // the home page - 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, - ); -} - -/// The login screen. -class LoginScreen extends StatelessWidget { - /// Creates a [LoginScreen]. - const LoginScreen({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text(App.title)), - body: Center( - child: ElevatedButton( - onPressed: () { - // log a user in, letting all the listeners know - context.read().login('test-user'); - - // router will automatically redirect from /login to / using - // refreshListenable - }, - child: const Text('Login'), - ), - ), - ); -} - -/// The home screen. -class HomeScreen extends StatelessWidget { - /// Creates a [HomeScreen]. - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - final LoginInfo info = context.read(); - - return Scaffold( - appBar: AppBar( - title: const Text(App.title), - actions: [ - IconButton( - onPressed: info.logout, - tooltip: 'Logout: ${info.userName}', - icon: const Icon(Icons.logout), - ) - ], - ), - body: const Center( - child: Text('HomeScreen'), - ), - ); - } -} diff --git a/lib/go_router/examples/routing_config.dart b/lib/go_router/examples/routing_config.dart deleted file mode 100644 index 3425520..0000000 --- a/lib/go_router/examples/routing_config.dart +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -/// This app shows how to dynamically add more route into routing config -void main() => runApp(const MyApp()); - -/// The main app. -class MyApp extends StatefulWidget { - /// Constructs a [MyApp] - const MyApp({super.key}); - - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - bool isNewRouteAdded = false; - - late final ValueNotifier myConfig = - ValueNotifier(_generateRoutingConfig()); - - late final GoRouter router = GoRouter.routingConfig( - routingConfig: myConfig, - errorBuilder: (_, GoRouterState state) => Scaffold( - appBar: AppBar(title: const Text('Page not found')), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('${state.uri} does not exist'), - ElevatedButton( - onPressed: () => router.go('/'), - child: const Text('Go to home')), - ], - )), - )); - - RoutingConfig _generateRoutingConfig() { - return RoutingConfig( - routes: [ - GoRoute( - path: '/', - builder: (_, __) { - return Scaffold( - appBar: AppBar(title: const Text('Home')), - body: Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: isNewRouteAdded - ? null - : () { - setState(() { - isNewRouteAdded = true; - // Modify the routing config. - myConfig.value = _generateRoutingConfig(); - }); - }, - child: isNewRouteAdded - ? const Text('A route has been added') - : const Text('Add a new route'), - ), - ElevatedButton( - onPressed: () { - router.go('/new-route'); - }, - child: const Text('Try going to /new-route'), - ) - ], - ), - ), - ); - }, - ), - if (isNewRouteAdded) - GoRoute( - path: '/new-route', - builder: (_, __) { - return Scaffold( - appBar: AppBar(title: const Text('A new Route')), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => router.go('/'), - child: const Text('Go to home')), - ], - )), - ); - }, - ), - ], - ); - } - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - routerConfig: router, - ); - } -} diff --git a/lib/go_router/examples/shell_route.dart b/lib/go_router/examples/shell_route.dart deleted file mode 100644 index 70878b8..0000000 --- a/lib/go_router/examples/shell_route.dart +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -final GlobalKey _rootNavigatorKey = - GlobalKey(debugLabel: 'root'); -final GlobalKey _shellNavigatorKey = - GlobalKey(debugLabel: 'shell'); - -// This scenario demonstrates how to set up nested navigation using ShellRoute, -// which is a pattern where an additional Navigator is placed in the widget tree -// to be used instead of the root navigator. This allows deep-links to display -// pages along with other UI components such as a BottomNavigationBar. -// -// This example demonstrates how to display a route within a ShellRoute and also -// push a screen using a different navigator (such as the root Navigator) by -// providing a `parentNavigatorKey`. - -void main() { - runApp(ShellRouteExampleApp()); -} - -/// An example demonstrating how to use [ShellRoute] -class ShellRouteExampleApp extends StatelessWidget { - /// Creates a [ShellRouteExampleApp] - ShellRouteExampleApp({super.key}); - - final GoRouter _router = GoRouter( - navigatorKey: _rootNavigatorKey, - initialLocation: '/a', - debugLogDiagnostics: true, - routes: [ - /// Application shell - ShellRoute( - navigatorKey: _shellNavigatorKey, - builder: (BuildContext context, GoRouterState state, Widget child) { - return ScaffoldWithNavBar(child: child); - }, - routes: [ - /// The first screen to display in the bottom navigation bar. - GoRoute( - path: '/a', - builder: (BuildContext context, GoRouterState state) { - return const ScreenA(); - }, - routes: [ - // The details screen to display stacked on the inner Navigator. - // This will cover screen A but not the application shell. - GoRoute( - path: 'details', - builder: (BuildContext context, GoRouterState state) { - return const DetailsScreen(label: 'A'); - }, - ), - ], - ), - - /// Displayed when the second item in the the bottom navigation bar is - /// selected. - GoRoute( - path: '/b', - builder: (BuildContext context, GoRouterState state) { - return const ScreenB(); - }, - routes: [ - /// Same as "/a/details", but displayed on the root Navigator by - /// specifying [parentNavigatorKey]. This will cover both screen B - /// and the application shell. - GoRoute( - path: 'details', - parentNavigatorKey: _rootNavigatorKey, - builder: (BuildContext context, GoRouterState state) { - return const DetailsScreen(label: 'B'); - }, - ), - ], - ), - - /// The third screen to display in the bottom navigation bar. - GoRoute( - path: '/c', - builder: (BuildContext context, GoRouterState state) { - return const ScreenC(); - }, - routes: [ - // The details screen to display stacked on the inner Navigator. - // This will cover screen A but not the application shell. - GoRoute( - path: 'details', - builder: (BuildContext context, GoRouterState state) { - return const DetailsScreen(label: 'C'); - }, - ), - ], - ), - ], - ), - ], - ); - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - routerConfig: _router, - ); - } -} - -/// Builds the "shell" for the app by building a Scaffold with a -/// BottomNavigationBar, where [child] is placed in the body of the Scaffold. -class ScaffoldWithNavBar extends StatelessWidget { - /// Constructs an [ScaffoldWithNavBar]. - const ScaffoldWithNavBar({ - required this.child, - super.key, - }); - - /// The widget to display in the body of the Scaffold. - /// In this sample, it is a Navigator. - final Widget child; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: child, - bottomNavigationBar: BottomNavigationBar( - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.home), - label: 'A Screen', - ), - BottomNavigationBarItem( - icon: Icon(Icons.business), - label: 'B Screen', - ), - BottomNavigationBarItem( - icon: Icon(Icons.notification_important_rounded), - label: 'C Screen', - ), - ], - currentIndex: _calculateSelectedIndex(context), - onTap: (int idx) => _onItemTapped(idx, context), - ), - ); - } - - static int _calculateSelectedIndex(BuildContext context) { - final String location = GoRouterState.of(context).uri.toString(); - if (location.startsWith('/a')) { - return 0; - } - if (location.startsWith('/b')) { - return 1; - } - if (location.startsWith('/c')) { - return 2; - } - return 0; - } - - void _onItemTapped(int index, BuildContext context) { - switch (index) { - case 0: - GoRouter.of(context).go('/a'); - break; - case 1: - GoRouter.of(context).go('/b'); - break; - case 2: - GoRouter.of(context).go('/c'); - break; - } - } -} - -/// The first screen in the bottom navigation bar. -class ScreenA extends StatelessWidget { - /// Constructs a [ScreenA] widget. - const ScreenA({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(), - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text('Screen A'), - TextButton( - onPressed: () { - GoRouter.of(context).go('/a/details'); - }, - child: const Text('View A details'), - ), - ], - ), - ), - ); - } -} - -/// The second screen in the bottom navigation bar. -class ScreenB extends StatelessWidget { - /// Constructs a [ScreenB] widget. - const ScreenB({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(), - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text('Screen B'), - TextButton( - onPressed: () { - GoRouter.of(context).go('/b/details'); - }, - child: const Text('View B details'), - ), - ], - ), - ), - ); - } -} - -/// The third screen in the bottom navigation bar. -class ScreenC extends StatelessWidget { - /// Constructs a [ScreenC] widget. - const ScreenC({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(), - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text('Screen C'), - TextButton( - onPressed: () { - GoRouter.of(context).go('/c/details'); - }, - child: const Text('View C details'), - ), - ], - ), - ), - ); - } -} - -/// The details screen for either the A, B or C screen. -class DetailsScreen extends StatelessWidget { - /// Constructs a [DetailsScreen]. - const DetailsScreen({ - required this.label, - super.key, - }); - - /// The label to display in the center of the screen. - final String label; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Details Screen'), - ), - body: Center( - child: Text( - 'Details for $label', - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - ); - } -} diff --git a/lib/go_router/examples/stateful_shell_route.dart b/lib/go_router/examples/stateful_shell_route.dart deleted file mode 100644 index eb0a67b..0000000 --- a/lib/go_router/examples/stateful_shell_route.dart +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -final GlobalKey _rootNavigatorKey = - GlobalKey(debugLabel: 'root'); -final GlobalKey _sectionANavigatorKey = - GlobalKey(debugLabel: 'sectionANav'); - -// This example demonstrates how to setup nested navigation using a -// BottomNavigationBar, where each bar item uses its own persistent navigator, -// i.e. navigation state is maintained separately for each item. This setup also -// enables deep linking into nested pages. - -void main() { - runApp(NestedTabNavigationExampleApp()); -} - -/// An example demonstrating how to use nested navigators -class NestedTabNavigationExampleApp extends StatelessWidget { - /// Creates a NestedTabNavigationExampleApp - NestedTabNavigationExampleApp({super.key}); - - final GoRouter _router = GoRouter( - navigatorKey: _rootNavigatorKey, - initialLocation: '/a', - routes: [ - StatefulShellRoute.indexedStack( - builder: (BuildContext context, GoRouterState state, - StatefulNavigationShell navigationShell) { - // Return the widget that implements the custom shell (in this case - // using a BottomNavigationBar). The StatefulNavigationShell is passed - // to be able access the state of the shell and to navigate to other - // branches in a stateful way. - return ScaffoldWithNavBar(navigationShell: navigationShell); - }, - branches: [ - // The route branch for the first tab of the bottom navigation bar. - StatefulShellBranch( - navigatorKey: _sectionANavigatorKey, - routes: [ - GoRoute( - // The screen to display as the root in the first tab of the - // bottom navigation bar. - path: '/a', - builder: (BuildContext context, GoRouterState state) => - const RootScreen(label: 'A', detailsPath: '/a/details'), - routes: [ - // The details screen to display stacked on navigator of the - // first tab. This will cover screen A but not the application - // shell (bottom navigation bar). - GoRoute( - path: 'details', - builder: (BuildContext context, GoRouterState state) => - const DetailsScreen(label: 'A'), - ), - ], - ), - ], - ), - - // The route branch for the second tab of the bottom navigation bar. - StatefulShellBranch( - // It's not necessary to provide a navigatorKey if it isn't also - // needed elsewhere. If not provided, a default key will be used. - routes: [ - GoRoute( - // The screen to display as the root in the second tab of the - // bottom navigation bar. - path: '/b', - builder: (BuildContext context, GoRouterState state) => - const RootScreen( - label: 'B', - detailsPath: '/b/details/1', - secondDetailsPath: '/b/details/2', - ), - routes: [ - GoRoute( - path: 'details/:param', - builder: (BuildContext context, GoRouterState state) => - DetailsScreen( - label: 'B', - param: state.pathParameters['param'], - ), - ), - ], - ), - ], - ), - - // The route branch for the third tab of the bottom navigation bar. - StatefulShellBranch( - routes: [ - GoRoute( - // The screen to display as the root in the third tab of the - // bottom navigation bar. - path: '/c', - builder: (BuildContext context, GoRouterState state) => - const RootScreen( - label: 'C', - detailsPath: '/c/details', - ), - routes: [ - GoRoute( - path: 'details', - builder: (BuildContext context, GoRouterState state) => - DetailsScreen( - label: 'C', - extra: state.extra, - ), - ), - ], - ), - ], - ), - ], - ), - ], - ); - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - routerConfig: _router, - ); - } -} - -/// Builds the "shell" for the app by building a Scaffold with a -/// BottomNavigationBar, where [child] is placed in the body of the Scaffold. -class ScaffoldWithNavBar extends StatelessWidget { - /// Constructs an [ScaffoldWithNavBar]. - const ScaffoldWithNavBar({ - required this.navigationShell, - Key? key, - }) : super(key: key ?? const ValueKey('ScaffoldWithNavBar')); - - /// The navigation shell and container for the branch Navigators. - final StatefulNavigationShell navigationShell; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: navigationShell, - bottomNavigationBar: BottomNavigationBar( - // Here, the items of BottomNavigationBar are hard coded. In a real - // world scenario, the items would most likely be generated from the - // branches of the shell route, which can be fetched using - // `navigationShell.route.branches`. - items: const [ - BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section A'), - BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section B'), - BottomNavigationBarItem(icon: Icon(Icons.tab), label: 'Section C'), - ], - currentIndex: navigationShell.currentIndex, - onTap: (int index) => _onTap(context, index), - ), - ); - } - - /// Navigate to the current location of the branch at the provided index when - /// tapping an item in the BottomNavigationBar. - void _onTap(BuildContext context, int index) { - // When navigating to a new branch, it's recommended to use the goBranch - // method, as doing so makes sure the last navigation state of the - // Navigator for the branch is restored. - navigationShell.goBranch( - index, - // A common pattern when using bottom navigation bars is to support - // navigating to the initial location when tapping the item that is - // already active. This example demonstrates how to support this behavior, - // using the initialLocation parameter of goBranch. - initialLocation: index == navigationShell.currentIndex, - ); - } -} - -/// Widget for the root/initial pages in the bottom navigation bar. -class RootScreen extends StatelessWidget { - /// Creates a RootScreen - const RootScreen({ - required this.label, - required this.detailsPath, - this.secondDetailsPath, - super.key, - }); - - /// The label - final String label; - - /// The path to the detail page - final String detailsPath; - - /// The path to another detail page - final String? secondDetailsPath; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Root of section $label'), - ), - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Screen $label', - style: Theme.of(context).textTheme.titleLarge), - const Padding(padding: EdgeInsets.all(4)), - TextButton( - onPressed: () { - GoRouter.of(context).go(detailsPath, extra: '$label-XYZ'); - }, - child: const Text('View details'), - ), - const Padding(padding: EdgeInsets.all(4)), - if (secondDetailsPath != null) - TextButton( - onPressed: () { - GoRouter.of(context).go(secondDetailsPath!); - }, - child: const Text('View more details'), - ), - ], - ), - ), - ); - } -} - -/// The details screen for either the A or B screen. -class DetailsScreen extends StatefulWidget { - /// Constructs a [DetailsScreen]. - const DetailsScreen({ - required this.label, - this.param, - this.extra, - this.withScaffold = true, - super.key, - }); - - /// The label to display in the center of the screen. - final String label; - - /// Optional param - final String? param; - - /// Optional extra object - final Object? extra; - - /// Wrap in scaffold - final bool withScaffold; - - @override - State createState() => DetailsScreenState(); -} - -/// The state for DetailsScreen -class DetailsScreenState extends State { - int _counter = 0; - - @override - Widget build(BuildContext context) { - if (widget.withScaffold) { - return Scaffold( - appBar: AppBar( - title: Text('Details Screen - ${widget.label}'), - ), - body: _build(context), - ); - } else { - return Container( - color: Theme.of(context).scaffoldBackgroundColor, - child: _build(context), - ); - } - } - - Widget _build(BuildContext context) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Details for ${widget.label} - Counter: $_counter', - style: Theme.of(context).textTheme.titleLarge), - const Padding(padding: EdgeInsets.all(4)), - TextButton( - onPressed: () { - setState(() { - _counter++; - }); - }, - child: const Text('Increment counter'), - ), - const Padding(padding: EdgeInsets.all(8)), - if (widget.param != null) - Text('Parameter: ${widget.param!}', - style: Theme.of(context).textTheme.titleMedium), - const Padding(padding: EdgeInsets.all(8)), - if (widget.extra != null) - Text('Extra: ${widget.extra!}', - style: Theme.of(context).textTheme.titleMedium), - if (!widget.withScaffold) ...[ - const Padding(padding: EdgeInsets.all(16)), - TextButton( - onPressed: () { - GoRouter.of(context).pop(); - }, - child: const Text('< Back', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), - ), - ] - ], - ), - ); - } -} diff --git a/lib/go_router/examples/transition_animations.dart b/lib/go_router/examples/transition_animations.dart deleted file mode 100644 index f245719..0000000 --- a/lib/go_router/examples/transition_animations.dart +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -/// To use a custom transition animation, provide a `pageBuilder` with a -/// CustomTransitionPage. -/// -/// To learn more about animation in Flutter, check out the [Introduction to -/// animations](https://docs.flutter.dev/development/ui/animations) page on -/// flutter.dev. -void main() => runApp(const MyApp()); - -/// The route configuration. -final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) { - return const HomeScreen(); - }, - routes: [ - GoRoute( - path: 'details', - pageBuilder: (BuildContext context, GoRouterState state) { - return CustomTransitionPage( - key: state.pageKey, - child: const DetailsScreen(), - transitionDuration: const Duration(milliseconds: 150), - transitionsBuilder: (BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child) { - // Change the opacity of the screen using a Curve based on the the animation's - // value - return FadeTransition( - opacity: - CurveTween(curve: Curves.easeInOut).animate(animation), - child: child, - ); - }, - ); - }, - ), - GoRoute( - path: 'dismissible-details', - pageBuilder: (BuildContext context, GoRouterState state) { - return CustomTransitionPage( - key: state.pageKey, - child: const DismissibleDetails(), - barrierDismissible: true, - barrierColor: Colors.black38, - opaque: false, - transitionDuration: Duration.zero, - transitionsBuilder: (_, __, ___, Widget child) => child, - ); - }, - ), - GoRoute( - path: 'custom-reverse-transition-duration', - pageBuilder: (BuildContext context, GoRouterState state) { - return CustomTransitionPage( - key: state.pageKey, - child: const DetailsScreen(), - barrierDismissible: true, - barrierColor: Colors.black38, - opaque: false, - transitionDuration: const Duration(milliseconds: 500), - reverseTransitionDuration: const Duration(milliseconds: 200), - transitionsBuilder: (BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child) { - return FadeTransition( - opacity: animation, - child: child, - ); - }, - ); - }, - ), - ], - ), - ], -); - -/// The main app. -class MyApp extends StatelessWidget { - /// Constructs a [MyApp] - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - routerConfig: _router, - ); - } -} - -/// The home screen -class HomeScreen extends StatelessWidget { - /// Constructs a [HomeScreen] - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Home Screen')), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/details'), - child: const Text('Go to the Details screen'), - ), - const SizedBox(height: 48), - ElevatedButton( - onPressed: () => context.go('/dismissible-details'), - child: const Text('Go to the Dismissible Details screen'), - ), - const SizedBox(height: 48), - ElevatedButton( - onPressed: () => - context.go('/custom-reverse-transition-duration'), - child: const Text( - 'Go to the Custom Reverse Transition Duration Screen', - ), - ), - ], - ), - ), - ); - } -} - -/// The details screen -class DetailsScreen extends StatelessWidget { - /// Constructs a [DetailsScreen] - const DetailsScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Details Screen')), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => context.go('/'), - child: const Text('Go back to the Home screen'), - ), - ], - ), - ), - ); - } -} - -/// The dismissible details screen -class DismissibleDetails extends StatelessWidget { - /// Constructs a [DismissibleDetails] - const DismissibleDetails({super.key}); - - @override - Widget build(BuildContext context) { - return const Padding( - padding: EdgeInsets.all(48), - child: ColoredBox(color: Colors.red), - ); - } -} diff --git a/lib/go_router/overlay/context_menu/context_menu_controller.0.dart b/lib/go_router/overlay/context_menu/context_menu_controller.0.dart deleted file mode 100644 index 99e77ec..0000000 --- a/lib/go_router/overlay/context_menu/context_menu_controller.0.dart +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This sample demonstrates allowing a context menu to be shown in a widget -// subtree in response to user gestures. - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -void main() => runApp(const ContextMenuControllerExampleApp()); - -/// A builder that includes an Offset to draw the context menu at. -typedef ContextMenuBuilder = Widget Function(BuildContext context, Offset offset); - -class ContextMenuControllerExampleApp extends StatefulWidget { - const ContextMenuControllerExampleApp({super.key}); - - @override - State createState() => _ContextMenuControllerExampleAppState(); -} - -class _ContextMenuControllerExampleAppState extends State { - void _showDialog(BuildContext context) { - Navigator.of(context).push( - DialogRoute( - context: context, - builder: (BuildContext context) => const AlertDialog(title: Text('You clicked print!')), - ), - ); - } - - @override - void initState() { - super.initState(); - // On web, disable the browser's context menu since this example uses a custom - // Flutter-rendered context menu. - if (kIsWeb) { - BrowserContextMenu.disableContextMenu(); - } - } - - @override - void dispose() { - if (kIsWeb) { - BrowserContextMenu.enableContextMenu(); - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Context menu outside of text'), - ), - body: _ContextMenuRegion( - contextMenuBuilder: (BuildContext context, Offset offset) { - // The custom context menu will look like the default context menu - // on the current platform with a single 'Print' button. - return AdaptiveTextSelectionToolbar.buttonItems( - anchors: TextSelectionToolbarAnchors( - primaryAnchor: offset, - ), - buttonItems: [ - ContextMenuButtonItem( - onPressed: () { - ContextMenuController.removeAny(); - _showDialog(context); - }, - label: 'Print', - ), - ], - ); - }, - // In this case this wraps a big open space in a GestureDetector in - // order to show the context menu, but it could also wrap a single - // widget like an Image to give it a context menu. - child: ListView( - children: [ - Container(height: 20.0), - const Text( - 'Right click (desktop) or long press (mobile) anywhere, not just on this text, to show the custom menu.'), - ], - ), - ), - ), - ); - } -} - -/// Shows and hides the context menu based on user gestures. -/// -/// By default, shows the menu on right clicks and long presses. -class _ContextMenuRegion extends StatefulWidget { - /// Creates an instance of [_ContextMenuRegion]. - const _ContextMenuRegion({ - required this.child, - required this.contextMenuBuilder, - }); - - /// Builds the context menu. - final ContextMenuBuilder contextMenuBuilder; - - /// The child widget that will be listened to for gestures. - final Widget child; - - @override - State<_ContextMenuRegion> createState() => _ContextMenuRegionState(); -} - -class _ContextMenuRegionState extends State<_ContextMenuRegion> { - Offset? _longPressOffset; - - final ContextMenuController _contextMenuController = ContextMenuController(); - - static bool get _longPressEnabled { - switch (defaultTargetPlatform) { - case TargetPlatform.android: - case TargetPlatform.iOS: - return true; - case TargetPlatform.macOS: - case TargetPlatform.fuchsia: - case TargetPlatform.linux: - case TargetPlatform.windows: - return false; - } - } - - void _onSecondaryTapUp(TapUpDetails details) { - _show(details.globalPosition); - } - - void _onTap() { - if (!_contextMenuController.isShown) { - return; - } - _hide(); - } - - void _onLongPressStart(LongPressStartDetails details) { - _longPressOffset = details.globalPosition; - } - - void _onLongPress() { - assert(_longPressOffset != null); - _show(_longPressOffset!); - _longPressOffset = null; - } - - void _show(Offset position) { - _contextMenuController.show( - context: context, - contextMenuBuilder: (BuildContext context) { - return widget.contextMenuBuilder(context, position); - }, - ); - } - - void _hide() { - _contextMenuController.remove(); - } - - @override - void dispose() { - _hide(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - onSecondaryTapUp: _onSecondaryTapUp, - onTap: _onTap, - onLongPress: _longPressEnabled ? _onLongPress : null, - onLongPressStart: _longPressEnabled ? _onLongPressStart : null, - child: widget.child, - ); - } -} diff --git a/lib/go_router/overlay/context_menu/editable_text_toolbar_builder.0.dart b/lib/go_router/overlay/context_menu/editable_text_toolbar_builder.0.dart deleted file mode 100644 index 138c14d..0000000 --- a/lib/go_router/overlay/context_menu/editable_text_toolbar_builder.0.dart +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This example demonstrates showing the default buttons, but customizing their -// appearance. - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -void main() => runApp(const EditableTextToolbarBuilderExampleApp()); - -class EditableTextToolbarBuilderExampleApp extends StatefulWidget { - const EditableTextToolbarBuilderExampleApp({super.key}); - - @override - State createState() => _EditableTextToolbarBuilderExampleAppState(); -} - -class _EditableTextToolbarBuilderExampleAppState extends State { - final TextEditingController _controller = TextEditingController( - text: 'Right click (desktop) or long press (mobile) to see the menu with custom buttons.', - ); - - @override - void initState() { - super.initState(); - // On web, disable the browser's context menu since this example uses a custom - // Flutter-rendered context menu. - if (kIsWeb) { - BrowserContextMenu.disableContextMenu(); - } - } - - @override - void dispose() { - if (kIsWeb) { - BrowserContextMenu.enableContextMenu(); - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Custom button appearance'), - ), - body: Center( - child: Column( - children: [ - const SizedBox(height: 20.0), - TextField( - controller: _controller, - contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) { - return AdaptiveTextSelectionToolbar( - anchors: editableTextState.contextMenuAnchors, - // Build the default buttons, but make them look custom. - // In a real project you may want to build different - // buttons depending on the platform. - children: editableTextState.contextMenuButtonItems.map((ContextMenuButtonItem buttonItem) { - return CupertinoButton( - borderRadius: null, - color: const Color(0xffaaaa00), - disabledColor: const Color(0xffaaaaff), - onPressed: buttonItem.onPressed, - padding: const EdgeInsets.all(10.0), - pressedOpacity: 0.7, - child: SizedBox( - width: 200.0, - child: Text( - CupertinoTextSelectionToolbarButton.getButtonLabel(context, buttonItem), - ), - ), - ); - }).toList(), - ); - }, - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/go_router/overlay/context_menu/editable_text_toolbar_builder.1.dart b/lib/go_router/overlay/context_menu/editable_text_toolbar_builder.1.dart deleted file mode 100644 index c7c16f0..0000000 --- a/lib/go_router/overlay/context_menu/editable_text_toolbar_builder.1.dart +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This example demonstrates showing a custom context menu only when some -// narrowly defined text is selected. - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -void main() => runApp(const EditableTextToolbarBuilderExampleApp()); - -const String emailAddress = 'me@example.com'; -const String text = 'Select the email address and open the menu: $emailAddress'; - -class EditableTextToolbarBuilderExampleApp extends StatefulWidget { - const EditableTextToolbarBuilderExampleApp({super.key}); - - @override - State createState() => _EditableTextToolbarBuilderExampleAppState(); -} - -class _EditableTextToolbarBuilderExampleAppState extends State { - final TextEditingController _controller = TextEditingController( - text: text, - ); - - void _showDialog(BuildContext context) { - Navigator.of(context).push( - DialogRoute( - context: context, - builder: (BuildContext context) => const AlertDialog(title: Text('You clicked send email!')), - ), - ); - } - - @override - void initState() { - super.initState(); - // On web, disable the browser's context menu since this example uses a custom - // Flutter-rendered context menu. - if (kIsWeb) { - BrowserContextMenu.disableContextMenu(); - } - } - - @override - void dispose() { - if (kIsWeb) { - BrowserContextMenu.enableContextMenu(); - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Custom button for emails'), - ), - body: Center( - child: Column( - children: [ - Container(height: 20.0), - TextField( - controller: _controller, - contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) { - final List buttonItems = editableTextState.contextMenuButtonItems; - // Here we add an "Email" button to the default TextField - // context menu for the current platform, but only if an email - // address is currently selected. - final TextEditingValue value = _controller.value; - if (_isValidEmail(value.selection.textInside(value.text))) { - buttonItems.insert( - 0, - ContextMenuButtonItem( - label: 'Send email', - onPressed: () { - ContextMenuController.removeAny(); - _showDialog(context); - }, - ), - ); - } - return AdaptiveTextSelectionToolbar.buttonItems( - anchors: editableTextState.contextMenuAnchors, - buttonItems: buttonItems, - ); - }, - ), - ], - ), - ), - ), - ); - } -} - -bool _isValidEmail(String text) { - return RegExp( - r'(?[a-zA-Z0-9]+)' - r'@' - r'(?[a-zA-Z0-9]+)' - r'\.' - r'(?[a-zA-Z0-9]+)', - ).hasMatch(text); -} diff --git a/lib/go_router/overlay/context_menu/editable_text_toolbar_builder.2.dart b/lib/go_router/overlay/context_menu/editable_text_toolbar_builder.2.dart deleted file mode 100644 index 25fa236..0000000 --- a/lib/go_router/overlay/context_menu/editable_text_toolbar_builder.2.dart +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This example demonstrates how to create a custom toolbar that retains the -// look of the default buttons for the current platform. - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -void main() => runApp(const EditableTextToolbarBuilderExampleApp()); - -class EditableTextToolbarBuilderExampleApp extends StatefulWidget { - const EditableTextToolbarBuilderExampleApp({super.key}); - - @override - State createState() => _EditableTextToolbarBuilderExampleAppState(); -} - -class _EditableTextToolbarBuilderExampleAppState extends State { - final TextEditingController _controller = TextEditingController( - text: 'Right click (desktop) or long press (mobile) to see the menu with a custom toolbar.', - ); - - @override - void initState() { - super.initState(); - // On web, disable the browser's context menu since this example uses a custom - // Flutter-rendered context menu. - if (kIsWeb) { - BrowserContextMenu.disableContextMenu(); - } - } - - @override - void dispose() { - if (kIsWeb) { - BrowserContextMenu.enableContextMenu(); - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Custom toolbar, default-looking buttons'), - ), - body: Center( - child: Column( - children: [ - const SizedBox(height: 20.0), - TextField( - controller: _controller, - contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) { - return _MyTextSelectionToolbar( - anchor: editableTextState.contextMenuAnchors.primaryAnchor, - // getAdaptiveButtons creates the default button widgets for - // the current platform. - children: AdaptiveTextSelectionToolbar.getAdaptiveButtons( - context, - // These buttons just close the menu when clicked. - [ - ContextMenuButtonItem( - label: 'One', - onPressed: () => ContextMenuController.removeAny(), - ), - ContextMenuButtonItem( - label: 'Two', - onPressed: () => ContextMenuController.removeAny(), - ), - ContextMenuButtonItem( - label: 'Three', - onPressed: () => ContextMenuController.removeAny(), - ), - ContextMenuButtonItem( - label: 'Four', - onPressed: () => ContextMenuController.removeAny(), - ), - ContextMenuButtonItem( - label: 'Five', - onPressed: () => ContextMenuController.removeAny(), - ), - ], - ).toList(), - ); - }, - ), - ], - ), - ), - ), - ); - } -} - -/// A simple, yet totally custom, text selection toolbar. -/// -/// Displays its children in a scrollable grid. -class _MyTextSelectionToolbar extends StatelessWidget { - const _MyTextSelectionToolbar({ - required this.anchor, - required this.children, - }); - - final Offset anchor; - final List children; - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - Positioned( - top: anchor.dy, - left: anchor.dx, - child: Container( - width: 200.0, - height: 200.0, - color: Colors.cyanAccent.withOpacity(0.5), - child: GridView.count( - padding: const EdgeInsets.all(12.0), - crossAxisCount: 2, - children: children, - ), - ), - ), - ], - ); - } -} diff --git a/lib/go_router/overlay/context_menu/selectable_region_toolbar_builder.0.dart b/lib/go_router/overlay/context_menu/selectable_region_toolbar_builder.0.dart deleted file mode 100644 index a63b625..0000000 --- a/lib/go_router/overlay/context_menu/selectable_region_toolbar_builder.0.dart +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This example demonstrates a custom context menu in non-editable text using -// SelectionArea. - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -void main() => runApp(const SelectableRegionToolbarBuilderExampleApp()); - -const String text = - 'I am some text inside of SelectionArea. Right click (desktop) or long press (mobile) me to show the customized context menu.'; - -class SelectableRegionToolbarBuilderExampleApp extends StatefulWidget { - const SelectableRegionToolbarBuilderExampleApp({super.key}); - - @override - State createState() => _SelectableRegionToolbarBuilderExampleAppState(); -} - -class _SelectableRegionToolbarBuilderExampleAppState extends State { - void _showDialog(BuildContext context) { - Navigator.of(context).push( - DialogRoute( - context: context, - builder: (BuildContext context) => const AlertDialog(title: Text('You clicked print!')), - ), - ); - } - - @override - void initState() { - super.initState(); - // On web, disable the browser's context menu since this example uses a custom - // Flutter-rendered context menu. - if (kIsWeb) { - BrowserContextMenu.disableContextMenu(); - } - } - - @override - void dispose() { - if (kIsWeb) { - BrowserContextMenu.enableContextMenu(); - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Context menu anywhere'), - ), - body: Center( - child: SizedBox( - width: 200.0, - child: SelectionArea( - contextMenuBuilder: ( - BuildContext context, - SelectableRegionState selectableRegionState, - ) { - return AdaptiveTextSelectionToolbar.buttonItems( - anchors: selectableRegionState.contextMenuAnchors, - buttonItems: [ - ...selectableRegionState.contextMenuButtonItems, - ContextMenuButtonItem( - onPressed: () { - ContextMenuController.removeAny(); - _showDialog(context); - }, - label: 'Print', - ), - ], - ); - }, - child: ListView( - children: const [ - SizedBox(height: 20.0), - Text(text), - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/go_router/v0/app.dart b/lib/go_router/v0/app.dart deleted file mode 100644 index c9460c2..0000000 --- a/lib/go_router/v0/app.dart +++ /dev/null @@ -1 +0,0 @@ -export 'app/unit_app.dart'; \ No newline at end of file diff --git a/lib/go_router/v0/app/navigation/router/app_router_delegate.dart b/lib/go_router/v0/app/navigation/router/app_router_delegate.dart deleted file mode 100644 index 3e51873..0000000 --- a/lib/go_router/v0/app/navigation/router/app_router_delegate.dart +++ /dev/null @@ -1,221 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/material.dart'; - -import '../../../pages/color/color_detail_page.dart'; -import '../../../pages/color/color_page.dart'; -import '../../../pages/empty/empty_page.dart'; -import '../../../pages/settings/settings_page.dart'; -import '../../../pages/counter/counter_page.dart'; -import '../../../pages/sort/sort_page.dart'; -import '../transition/fade_transition_page.dart'; -import '../../../pages/color/color_add_page.dart'; - -const List kDestinationsPaths = [ - '/color', - '/counter', - '/user', - '/settings', -]; - -AppRouterDelegate router = AppRouterDelegate(); - -class AppRouterDelegate extends RouterDelegate with ChangeNotifier { - String _path = '/color'; - - String get path => _path; - - int? get activeIndex { - if(path.startsWith('/color')) return 0; - if(path.startsWith('/counter')) return 1; - if(path.startsWith('/user')) return 2; - if(path.startsWith('/settings')) return 3; - return null; - } - - final Map> _completerMap = {}; - - Completer? completer; - - final Map> _alivePageMap = {}; - - void setPathKeepLive(String value){ - _alivePageMap[value] = _buildPageByPath(value); - path = value; - } - - final Map _pathExtraMap = {}; - - FutureOr changePath(String value,{bool forResult=false,Object? extra}){ - if(forResult){ - _completerMap[value] = Completer(); - } - if(extra!=null){ - _pathExtraMap[value] = extra; - } - - if(forResult){ - return _completerMap[value]!.future; - } - } - - - set path(String value) { - if (_path == value) return; - _path = value; - notifyListeners(); - } - - @override - Widget build(BuildContext context) { - List pages = []; - if(_alivePageMap.containsKey(path)){ - pages = _alivePageMap[path]!; - }else{ - for (var element in _alivePageMap.values) { - pages.addAll(element); - } - pages.addAll(_buildPageByPath(path)); - } - - return Navigator( - onPopPage: _onPopPage, - pages: pages.toSet().toList(), - ); - } - - List _buildPageByPath(String path) { - Widget? child; - if(path.startsWith('/color')){ - return buildColorPages(path); - } - - if (path == kDestinationsPaths[1]) { - child = const CounterPage(); - } - if (path == kDestinationsPaths[2]) { - child = const SortPage(); - } - if (path == kDestinationsPaths[3]) { - child = const SettingPage(); - } - return [ - FadeTransitionPage( - key: ValueKey(path), - child: child ?? const EmptyPage(), - ) - ]; - } - - List buildColorPages(String path){ - List result = []; - Uri uri = Uri.parse(path); - for (String segment in uri.pathSegments) { - if(segment == 'color'){ - result.add( const FadeTransitionPage( - key: ValueKey('/color'), - child:ColorPage(), - )); - } - if(segment =='detail'){ - final Map queryParams = uri.queryParameters; - String? selectedColor = queryParams['color']; - - if (selectedColor != null) { - Color color = Color(int.parse(selectedColor, radix: 16)); - result.add( FadeTransitionPage( - key: const ValueKey('/color/detail'), - child:ColorDetailPage(color: color), - )); - }else{ - Color? selectedColor = _pathExtraMap[path]; - if (selectedColor != null) { - result.add( FadeTransitionPage( - key: const ValueKey('/color/detail'), - child:ColorDetailPage(color: selectedColor), - )); - _pathExtraMap.remove(path); - } - } - } - if(segment == 'add'){ - result.add( const FadeTransitionPage( - key: ValueKey('/color/add'), - child:ColorAddPage(), - )); - } - - } - return result; - } - - @override - Future popRoute() async { - print('=======popRoute========='); - return true; - } - - bool _onPopPage(Route route, result) { - if(_completerMap.containsKey(path)){ - _completerMap[path]?.complete(result); - _completerMap.remove(path); - } - - path = backPath(path); - return route.didPop(result); - } - - String backPath(String path){ - Uri uri = Uri.parse(path); - if(uri.pathSegments.length==1) return path; - List parts = List.of(uri.pathSegments)..removeLast(); - return '/${parts.join('/')}'; - } - - @override - Future setNewRoutePath(configuration) async {} -} - - - -// class AppRouterDelegate extends RouterDelegate with ChangeNotifier, PopNavigatorRouterDelegateMixin { -// -// List _value = ['/']; -// -// -// List get value => _value; -// -// set value(List value){ -// _value = value; -// notifyListeners(); -// } -// -// @override -// Widget build(BuildContext context) { -// return Navigator( -// onPopPage: _onPopPage, -// pages: _value.map((e) => _pageMap[e]!).toList(), -// ); -// } -// -// final Map _pageMap = const { -// '/': MaterialPage(child: HomePage()), -// 'a': MaterialPage(child: PageA()), -// 'b': MaterialPage(child: PageB()), -// 'c': MaterialPage(child: PageC()), -// }; -// -// bool _onPopPage(Route route, result) { -// _value = List.of(_value)..removeLast(); -// notifyListeners(); -// return route.didPop(result); -// } -// -// @override -// GlobalKey? navigatorKey = GlobalKey(); -// -// @override -// Future setNewRoutePath(String configuration) async{ -// } -// } diff --git a/lib/go_router/v0/app/navigation/router/iroute.dart b/lib/go_router/v0/app/navigation/router/iroute.dart deleted file mode 100644 index ef2160d..0000000 --- a/lib/go_router/v0/app/navigation/router/iroute.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -import '../../../pages/color/color_add_page.dart'; -import '../../../pages/color/color_detail_page.dart'; -import '../../../pages/color/color_page.dart'; -import '../transition/fade_transition_page.dart'; - -class IRoute { - final String path; - final IRoutePageBuilder builder; - final List children; - - const IRoute({ - required this.path, - this.children = const [], - required this.builder, - }); - - @override - String toString() { - return 'IRoute{path: $path, children: $children}'; - } - - List list() { - return []; - } -} - -typedef IRoutePageBuilder = Page? Function( - BuildContext context, IRouteData data); - -class IRouteData { - final Object? extra; - final bool forResult; - final Uri uri; - final bool keepAlive; - - IRouteData({ - required this.extra, - required this.uri, - required this.forResult, - required this.keepAlive, - }); -} - -List kDestinationsIRoutes = [ - IRoute( - path: '/color', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color'), - child: ColorPage(), - ); - }, - children: [ - IRoute( - path: '/color/detail', - builder: (ctx, data) { - final Map queryParams = data.uri.queryParameters; - String? selectedColor = queryParams['color']; - if (selectedColor != null) { - Color color = Color(int.parse(selectedColor, radix: 16)); - return FadeTransitionPage( - key: const ValueKey('/color/detail'), - child: ColorDetailPage(color: color), - ); - } - return null; - }, - ), - IRoute( - path: '/color/add', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color/add'), - child: ColorAddPage(), - ); - }), - ], - ), - IRoute( - path: '/counter', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/counter'), - child: ColorAddPage(), - ); - }), - IRoute( - path: '/user', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/user'), - child: ColorAddPage(), - ); - }), - IRoute( - path: '/settings', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/settings'), - child: ColorAddPage(), - ); - }), -]; diff --git a/lib/go_router/v0/app/navigation/transition/fade_transition_page.dart b/lib/go_router/v0/app/navigation/transition/fade_transition_page.dart deleted file mode 100644 index 552171b..0000000 --- a/lib/go_router/v0/app/navigation/transition/fade_transition_page.dart +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2021, the Flutter project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:flutter/material.dart'; - -class FadeTransitionPage extends Page { - final Widget child; - final Duration duration; - - const FadeTransitionPage({ - super.key, - required this.child, - this.duration = const Duration(milliseconds: 300), - }); - - @override - Route createRoute(BuildContext context) => - PageBasedFadeTransitionRoute(this); -} - -class PageBasedFadeTransitionRoute extends PageRoute { - final FadeTransitionPage _page; - - PageBasedFadeTransitionRoute(this._page) : super(settings: _page); - - @override - Color? get barrierColor => null; - - @override - String? get barrierLabel => null; - - @override - Duration get transitionDuration => _page.duration; - - @override - bool get maintainState => true; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - var curveTween = CurveTween(curve: Curves.easeIn); - return FadeTransition( - opacity: animation.drive(curveTween), - child: (settings as FadeTransitionPage).child, - ); - } - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) => - child; -} diff --git a/lib/go_router/v0/app/navigation/transition/no_transition_page.dart b/lib/go_router/v0/app/navigation/transition/no_transition_page.dart deleted file mode 100644 index 291910b..0000000 --- a/lib/go_router/v0/app/navigation/transition/no_transition_page.dart +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2021, the Flutter project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:flutter/material.dart'; - -class NoTransitionPage extends Page { - final Widget child; - - const NoTransitionPage({ - super.key, - required this.child, - }); - - @override - Route createRoute(BuildContext context) => NoTransitionRoute(this); -} - -class NoTransitionRoute extends PageRoute { - - final NoTransitionPage _page; - - NoTransitionRoute(this._page) : super(settings: _page); - - @override - Color? get barrierColor => null; - - @override - String? get barrierLabel => null; - - @override - Duration get transitionDuration => const Duration(milliseconds: 0); - - @override - bool get maintainState => true; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return (settings as NoTransitionPage).child; - } - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) => - child; -} diff --git a/lib/go_router/v0/app/navigation/views/app_navigation.dart b/lib/go_router/v0/app/navigation/views/app_navigation.dart deleted file mode 100644 index adcab6c..0000000 --- a/lib/go_router/v0/app/navigation/views/app_navigation.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import '../router/app_router_delegate.dart'; -import 'app_navigation_rail.dart'; -import 'app_top_bar.dart'; - -class AppNavigation extends StatelessWidget { - const AppNavigation({super.key}); - - @override - Widget build(BuildContext context) { - double px1 = 1/View.of(context).devicePixelRatio; - return Scaffold( - body: Row( - children: [ - const AppNavigationRail(), - Expanded( - child: Column( - children: [ - const AppTopBar(), - Divider(height: px1,), - Expanded( - child: Router( - routerDelegate: router, - backButtonDispatcher: RootBackButtonDispatcher(), - ), - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/lib/go_router/v0/app/navigation/views/app_navigation_rail.dart b/lib/go_router/v0/app/navigation/views/app_navigation_rail.dart deleted file mode 100644 index 1ab9714..0000000 --- a/lib/go_router/v0/app/navigation/views/app_navigation_rail.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iroute/components/components.dart'; -import '../router/app_router_delegate.dart'; - -class AppNavigationRail extends StatefulWidget { - const AppNavigationRail({super.key}); - - @override - State createState() => _AppNavigationRailState(); -} - -class _AppNavigationRailState extends State { - - final List deskNavBarMenus = const [ - MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined), - MenuMeta(label: '计数器', icon: Icons.add_chart), - MenuMeta(label: '我的', icon: Icons.person), - MenuMeta(label: '设置', icon: Icons.settings), - ]; - - @override - void initState() { - super.initState(); - router.addListener(_onRouterChange); - } - - @override - void dispose() { - router.removeListener(_onRouterChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return DragToMoveWrap( - child: TolyNavigationRail( - items: deskNavBarMenus, - leading: const Padding( - padding: EdgeInsets.symmetric(vertical: 18.0), - child: FlutterLogo(), - ), - tail: Padding( - padding: const EdgeInsets.only(bottom: 6.0), - child: Text('V0.0.4',style: TextStyle(color: Colors.white,fontSize: 12),), - ), - backgroundColor: const Color(0xff3975c6), - onDestinationSelected: _onDestinationSelected, - selectedIndex: router.activeIndex, - ), - ); - - } - - void _onDestinationSelected(int index) { - if(index==1){ - router.setPathKeepLive(kDestinationsPaths[index]); - }else{ - router.path = kDestinationsPaths[index]; - } - } - - void _onRouterChange() { - setState(() {}); - } -} diff --git a/lib/go_router/v0/app/navigation/views/app_router_editor.dart b/lib/go_router/v0/app/navigation/views/app_router_editor.dart deleted file mode 100644 index 10d5701..0000000 --- a/lib/go_router/v0/app/navigation/views/app_router_editor.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:iroute/components/toly_ui/button/hover_icon_button.dart'; -import '../router/app_router_delegate.dart'; - -class AppRouterEditor extends StatefulWidget { - final ValueChanged? onSubmit; - const AppRouterEditor({super.key, this.onSubmit}); - - @override - State createState() => _AppRouterEditorState(); -} - -class _AppRouterEditorState extends State { - - final TextEditingController _controller = TextEditingController(); - - - @override - void initState() { - super.initState(); - _onRouteChange(); - router.addListener(_onRouteChange); - } - - void _onRouteChange() { - _controller.text=router.path; - } - - @override - void dispose() { - _controller.dispose(); - router.removeListener(_onRouteChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Stack( - alignment: Alignment.centerRight, - children: [ - SizedBox( - child: CupertinoTextField( - controller: _controller, - style: TextStyle(fontSize: 14), - padding: EdgeInsets.only(left:12,top: 6,bottom: 6,right: 32), - placeholder: '输入路由地址导航', - onSubmitted: widget.onSubmit, - decoration: BoxDecoration(color: Color(0xffF1F2F3),borderRadius: BorderRadius.circular(6)), - ), - ), - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: HoverIconButton( - icon: Icons.directions_outlined, - defaultColor: Color(0xff68696B), - onPressed:()=>widget.onSubmit?.call(_controller.text), - size: 20 - ), - ) - ], - ); - } -} diff --git a/lib/go_router/v0/app/navigation/views/app_top_bar.dart b/lib/go_router/v0/app/navigation/views/app_top_bar.dart deleted file mode 100644 index 1b95110..0000000 --- a/lib/go_router/v0/app/navigation/views/app_top_bar.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iroute/components/components.dart'; -import '../router/app_router_delegate.dart'; -import 'app_router_editor.dart'; - -class AppTopBar extends StatelessWidget { - const AppTopBar({super.key}); - - @override - Widget build(BuildContext context) { - return DragToMoveWrap( - child: Container( - alignment: Alignment.center, - height: 46, - child: Row( - children: [ - const SizedBox(width: 16), - const RouterIndicator(), - Expanded( - child: Row(children: [ - const Spacer(), - SizedBox( - width: 250, - child: AppRouterEditor( - onSubmit: (path) => router.path = path, - )), - const Padding( - padding: EdgeInsets.symmetric(vertical: 12.0), - child: VerticalDivider( - width: 32, - ), - ) - ])), - const WindowButtons() - ], - ), - ), - ); - } -} - -class RouterIndicator extends StatefulWidget { - const RouterIndicator({super.key}); - - @override - State createState() => _RouterIndicatorState(); -} - -Map kRouteLabelMap = { - '/color': '颜色板', - '/color/add': '添加颜色', - '/color/detail': '颜色详情', - '/counter': '计数器', - '/user': '我的', - '/settings': '系统设置', -}; - -class _RouterIndicatorState extends State { - @override - void initState() { - super.initState(); - router.addListener(_onRouterChange); - } - - @override - void dispose() { - router.removeListener(_onRouterChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return TolyBreadcrumb( - items: pathToBreadcrumbItems(router.path), - onTapItem: (item) { - if (item.to != null) { - router.path = item.to!; - } - }, - ); - } - - void _onRouterChange() { - setState(() {}); - } - - List pathToBreadcrumbItems(String path) { - Uri uri = Uri.parse(path); - List result = []; - String to = ''; - - String distPath = ''; - for (String segment in uri.pathSegments) { - distPath += '/$segment'; - } - - for (String segment in uri.pathSegments) { - to += '/$segment'; - String label = kRouteLabelMap[to] ?? '未知路由'; - result.add(BreadcrumbItem(to: to, label: label, active: to == distPath)); - } - return result; - } -} diff --git a/lib/go_router/v0/app/unit_app.dart b/lib/go_router/v0/app/unit_app.dart deleted file mode 100644 index 60d28f8..0000000 --- a/lib/go_router/v0/app/unit_app.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; -import 'navigation/router/app_router_delegate.dart'; -import 'navigation/views/app_navigation.dart'; -import 'navigation/views/app_navigation_rail.dart'; - -class UnitApp extends StatelessWidget { - const UnitApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData( - fontFamily: "宋体", - scaffoldBackgroundColor: Colors.white, - appBarTheme: const AppBarTheme( - elevation: 0, - iconTheme: IconThemeData(color: Colors.black), - titleTextStyle: TextStyle( - color: Colors.black, - fontSize: 18, - fontWeight: FontWeight.bold, - ))), - debugShowCheckedModeBanner: false, - home: AppNavigation() - ); - } -} - - diff --git a/lib/go_router/v0/pages/color/color_add_page.dart b/lib/go_router/v0/pages/color/color_add_page.dart deleted file mode 100644 index c6ca0cd..0000000 --- a/lib/go_router/v0/pages/color/color_add_page.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_colorpicker/flutter_colorpicker.dart'; - -class ColorAddPage extends StatefulWidget { - const ColorAddPage({super.key}); - - @override - State createState() => _ColorAddPageState(); -} - -class _ColorAddPageState extends State { - late Color _color; - - @override - void initState() { - super.initState(); - _color = randomColor; - } - - @override - Widget build(BuildContext context) { - String text = '# ${_color.value.toRadixString(16)}'; - return Scaffold( - bottomNavigationBar: Container( - margin: EdgeInsets.only(right:20,bottom: 20), - // color: Colors.redAccent, - child: Row( - textDirection:TextDirection.rtl, - children: [ - ElevatedButton(onPressed: (){ - Navigator.of(context).pop(_color); - - }, child: Text('添加')), - SizedBox(width: 12,), - OutlinedButton(onPressed: (){ - Navigator.of(context).pop(); - }, child: Text('取消')), - ], - ), - ), - body: Column( - // mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0,vertical: 16), - child: Row( - children: [ - Expanded(child: Text(text,style: TextStyle(color: _color,fontSize: 24,letterSpacing: 4),)), - Container( - margin: EdgeInsets.only(left: 10), - width: 40, - height: 40, - child: Icon( - Icons.sell_outlined, - color: Colors.white, - ), - decoration: BoxDecoration( - color: _color, - borderRadius: BorderRadius.circular(8), - ), - ), - ], - ), - ), - ColorPicker( - colorPickerWidth:200, - // enableAlpha: false, - displayThumbColor:true, - pickerColor: _color, - paletteType: PaletteType.hueWheel, - onColorChanged: changeColor, - - ), - ], - ), - ); - } - - Random _random = Random(); - - Color get randomColor { - return Color.fromARGB( - 255, - _random.nextInt(256), - _random.nextInt(256), - _random.nextInt(256), - ); - } - - void _selectColor() { - Navigator.of(context).pop(_color); - } - - void changeColor(Color value) { - _color = value; - setState(() { - - }); - } -} diff --git a/lib/go_router/v0/pages/color/color_detail_page.dart b/lib/go_router/v0/pages/color/color_detail_page.dart deleted file mode 100644 index 17fcd17..0000000 --- a/lib/go_router/v0/pages/color/color_detail_page.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -class ColorDetailPage extends StatelessWidget { - final Color color; - const ColorDetailPage({super.key, required this.color}); - - @override - Widget build(BuildContext context) { - - const TextStyle style = TextStyle( - fontSize: 32, - fontWeight: FontWeight.bold, - color: Colors.white - ); - String text = '# ${color.value.toRadixString(16)}'; - return Scaffold( - // appBar: AppBar( - // systemOverlayStyle: SystemUiOverlayStyle( - // statusBarColor: Colors.transparent, - // statusBarIconBrightness: Brightness.light - // ), - // iconTheme: IconThemeData(color: Colors.white), - // titleTextStyle:TextStyle(color: Colors.white,fontSize: 18) , - // backgroundColor: color, - // title: Text('颜色界面',),), - body: Container( - alignment: Alignment.center, - color: color, - child: Text(text ,style: style,), - ), - ); - } -} diff --git a/lib/go_router/v0/pages/color/color_page.dart b/lib/go_router/v0/pages/color/color_page.dart deleted file mode 100644 index 6764129..0000000 --- a/lib/go_router/v0/pages/color/color_page.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iroute/components/project/colors_panel.dart'; -import '../../app/navigation/router/app_router_delegate.dart'; - -class ColorPage extends StatefulWidget { - const ColorPage({super.key}); - - @override - State createState() => _ColorPageState(); -} - -class _ColorPageState extends State { - final List _colors = [ - Colors.red, Colors.black, Colors.blue, Colors.green, Colors.orange, - Colors.pink, Colors.purple, Colors.indigo, Colors.amber, Colors.cyan, - Colors.redAccent, Colors.grey, Colors.blueAccent, Colors.greenAccent, Colors.orangeAccent, - Colors.pinkAccent, Colors.purpleAccent, Colors.indigoAccent, Colors.amberAccent, Colors.cyanAccent, - ]; - - @override - Widget build(BuildContext context) { - return Scaffold( - // appBar: AppBar(title:const Text('颜色主页')), - floatingActionButton: FloatingActionButton( - onPressed: _toAddPage, - child: const Icon(Icons.add), - ), - body: Align( - alignment: Alignment.topCenter, - child: ColorsPanel( - colors: _colors, - onSelect: _selectColor, - ), - ), - ); - } - - void _selectColor(Color color){ - String value = color.value.toRadixString(16); - router.path = '/color/detail?color=$value'; - // router.setPathForData('/color/detail',color); - // router.setPathKeepLive('/color/detail?color=$value'); - - } - - void _toAddPage() async { - Color? color = await router.changePath('/color/add',forResult: true); - if (color != null) { - setState(() { - _colors.add(color); - }); - } - } -} \ No newline at end of file diff --git a/lib/go_router/v0/pages/counter/counter_page.dart b/lib/go_router/v0/pages/counter/counter_page.dart deleted file mode 100644 index b5b2e17..0000000 --- a/lib/go_router/v0/pages/counter/counter_page.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -class CounterPage extends StatefulWidget { - const CounterPage({super.key}); - - @override - State createState() => _CounterPageState(); -} - -class _CounterPageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), - ); - } -} \ No newline at end of file diff --git a/lib/go_router/v0/pages/empty/empty_page.dart b/lib/go_router/v0/pages/empty/empty_page.dart deleted file mode 100644 index b05f56f..0000000 --- a/lib/go_router/v0/pages/empty/empty_page.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; - -class EmptyPage extends StatelessWidget { - const EmptyPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - // appBar: AppBar( - // title: Text('界面走丢了'), - // ), - body: Scaffold( - body: Center( - child: Wrap( - spacing: 16, - crossAxisAlignment: WrapCrossAlignment.center, - direction: Axis.vertical, - children: [ - Icon(Icons.nearby_error,size: 64, color: Colors.grey), - Text( - '404 界面丢失', - style: TextStyle(fontSize: 24, color: Colors.grey), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/go_router/v0/pages/settings/settings_page.dart b/lib/go_router/v0/pages/settings/settings_page.dart deleted file mode 100644 index 0b53503..0000000 --- a/lib/go_router/v0/pages/settings/settings_page.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter/material.dart'; - -class SettingPage extends StatelessWidget { - const SettingPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body:Center(child: Text('SettingPage'))); - } -} diff --git a/lib/go_router/v0/pages/sort/sort_page.dart b/lib/go_router/v0/pages/sort/sort_page.dart deleted file mode 100644 index 8172cc0..0000000 --- a/lib/go_router/v0/pages/sort/sort_page.dart +++ /dev/null @@ -1,859 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/material.dart'; - -class SortPage extends StatefulWidget { - const SortPage({Key? key}) : super(key: key); - - @override - State createState() => _SortPageState(); -} - -class _SortPageState extends State { - //存放随机数组 - List numbers = []; - - //订阅流 - StreamController> streamController = StreamController(); - String currentSort = 'bubble'; - - //柱子的数量 -> 生成排序数组的长度 - double sampleSize = 0; - - //是否排序 - bool isSorted = false; - - //是否在排序中 - bool isSorting = false; - - //排序动画更新的速度 - int speed = 0; - - static int duration = 1500; - - String getTitle() { - switch (currentSort) { - case "bubble": - return "Bubble Sort"; - case "coctail": - return "Coctail Sort"; - case "comb": - return "Comb Sort"; - case "pigeonhole": - return "Pigeonhole Sort"; - case "shell": - return "Shell Sort"; - case "selection": - return "Selection Sort"; - case "cycle": - return "Cycle Sort"; - case "heap": - return "Heap Sort"; - case "insertion": - return "Insertion Sort"; - case "gnome": - return "Gnome Sort"; - case "oddeven": - return "OddEven Sort"; - case "quick": - return "Quick Sort"; - case "merge": - return "Merge Sort"; - } - return ""; - } - - reset() { - isSorted = false; - numbers = []; - for (int i = 0; i < sampleSize; ++i) { - numbers.add(Random().nextInt(500)); - } - streamController.add(numbers); - } - - Duration getDuration() { - return Duration(microseconds: duration); - } - - ///动画时间 - changeSpeed() { - if (speed >= 3) { - speed = 0; - duration = 1500; - } else { - speed++; - duration = duration ~/ 2; - } - setState(() {}); - } - - ///冒泡排序 - bubbleSort() async { - //控制需要进行排序的次数。每一轮循环都会确定一个数字的最终位置。 - for (int i = 0; i < numbers.length; ++i) { - //遍历当前未排序的元素,通过相邻的元素比较并交换位置来完成排序。 - for (int j = 0; j < numbers.length - i - 1; ++j) { - //如果 _numbers[j] 大于 _numbers[j + 1],则交换它们的位置,确保较大的元素移到右边。 - if (numbers[j] > numbers[j + 1]) { - int temp = numbers[j]; - numbers[j] = numbers[j + 1]; - numbers[j + 1] = temp; - } - //实现一个延迟,以便在ui上展示排序的动画效果 - await Future.delayed(getDuration(), () {}); - streamController.add(numbers); - } - } - } - - ///鸡尾酒排序(双向冒泡排序) - cocktailSort() async { - bool swapped = true; // 表示是否进行了交换 - int start = 0; // 当前未排序部分的起始位置 - int end = numbers.length; // 当前未排序部分的结束位置 - - // 开始排序循环,只有当没有进行交换时才会退出循环 - while (swapped == true) { - swapped = false; - - // 从左往右遍历需要排序的部分 - for (int i = start; i < end - 1; ++i) { - // 对每两个相邻元素进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果前面的元素大于后面的元素,则交换它们的位置 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - swapped = true; // 进行了交换 - } - - // 实现动画效果,延迟一段时间后更新数组状态 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - - // 如果没有进行交换,则说明已经排好序,退出循环 - if (swapped == false) break; - // 重设为false,准备进行下一轮排序 - swapped = false; - // 将end设置为上一轮排序的最后一个元素的位置 - end = end - 1; - - // 从右往左遍历需要排序的部分 - for (int i = end - 1; i >= start; i--) { - // 对每两个相邻元素进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果前面的元素大于后面的元素,则交换它们的位置 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - swapped = true; // 进行了交换 - } - - // 实现动画效果,延迟一段时间后更新数组状态 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - // 将start向右移一位,准备下一轮排序 - start = start + 1; - } - } - - ///梳排序(Comb Sort) - combSort() async { - int gap = numbers.length; - - bool swapped = true; - - // 当间隔不为1或存在交换时执行循环 - while (gap != 1 || swapped == true) { - // 通过缩小间隔来逐步将元素归位 - gap = getNextGap(gap); - swapped = false; - for (int i = 0; i < numbers.length - gap; i++) { - // 如果当前元素大于间隔位置上的元素,则交换它们的位置 - if (numbers[i] > numbers[i + gap]) { - int temp = numbers[i]; - numbers[i] = numbers[i + gap]; - numbers[i + gap] = temp; - swapped = true; - } - - // 实现一个延迟,以便在 UI 上展示排序的动画效果。 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - int getNextGap(int gap) { - // 根据当前间隔值计算下一个间隔值 - gap = (gap * 10) ~/ 13; - if (gap < 1) return 1; - return gap; - } - - ///鸽巢排序 - pigeonHole() async { - int min = numbers[0]; - int max = numbers[0]; - int range, i, j, index; - - // 找到数组中的最大值和最小值 - for (int a = 0; a < numbers.length; a++) { - if (numbers[a] > max) max = numbers[a]; - if (numbers[a] < min) min = numbers[a]; - } - - // 计算鸽巢的个数 - range = max - min + 1; - List p = List.generate(range, (i) => 0); - - // 将数字分配到各个鸽巢中 - for (i = 0; i < numbers.length; i++) { - p[numbers[i] - min]++; - } - - index = 0; - - // 将鸽巢中的数字取出,重新放回到数组中 - for (j = 0; j < range; j++) { - while (p[j]-- > 0) { - numbers[index++] = j + min; - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - ///希尔排序 - shellSort() async { - //定义变量 gap 并初始化为数组长度的一半。每次循环完成后将 gap 减半直到等于 0。 - for (int gap = numbers.length ~/ 2; gap > 0; gap ~/= 2) { - //遍历每个子序列并进行插入排序。初始时从第一个子序列的第二个元素开始,即 i = gap,以 gap 为步长逐个遍历每个子序列。 - for (int i = gap; i < numbers.length; i += 1) { - //将当前遍历到的元素赋值给它 - int temp = numbers[i]; - //内部使用一个 for 循环来实现插入排序。 - //循环开始时定义变量 j 并将其初始化为当前遍历到的元素的下标。通过不断比较前后相隔 gap 的元素大小并交换位置,将当前元素插入到正确的位置。 - int j; - for (j = i; j >= gap && numbers[j - gap] > temp; j -= gap) { - numbers[j] = numbers[j - gap]; - } - numbers[j] = temp; - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - ///选择排序 - selectionSort() async { - for (int i = 0; i < numbers.length; i++) { - for (int j = i + 1; j < numbers.length; j++) { - // 遍历未排序部分,内层循环控制变量 j - if (numbers[i] > numbers[j]) { - // 判断当前元素是否比后续元素小 - int temp = numbers[j]; - // 交换当前元素和后续较小的元素 - numbers[j] = numbers[i]; - numbers[i] = temp; - } - - await Future.delayed(getDuration(), () {}); - - streamController.add(numbers); - } - } - } - - ///循环排序 - cycleSort() async { - int writes = 0; - for (int cycleStart = 0; cycleStart <= numbers.length - 2; cycleStart++) { - int item = numbers[cycleStart]; - int pos = cycleStart; - - // 在未排序部分中寻找比当前元素小的元素个数 - for (int i = cycleStart + 1; i < numbers.length; i++) { - if (numbers[i] < item) pos++; - } - - // 如果当前元素已经在正确位置上,则跳过此次迭代 - if (pos == cycleStart) { - continue; - } - - // 将当前元素放置到正确的位置上,并记录写操作次数 - while (item == numbers[pos]) { - pos += 1; - } - if (pos != cycleStart) { - int temp = item; - item = numbers[pos]; - numbers[pos] = temp; - writes++; - } - - // 循环将位于当前位置的元素放置到正确的位置上 - while (pos != cycleStart) { - pos = cycleStart; - // 继续在未排序部分中寻找比当前元素小的元素个数 - for (int i = cycleStart + 1; i < numbers.length; i++) { - if (numbers[i] < item) pos += 1; - } - - // 将当前元素放置到正确的位置上,并记录写操作次数 - while (item == numbers[pos]) { - pos += 1; - } - if (item != numbers[pos]) { - int temp = item; - item = numbers[pos]; - numbers[pos] = temp; - writes++; - } - - // 添加延迟操作以展示排序过程 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - ///堆排序 - heapSort() async { - // 从最后一个非叶子节点开始,构建最大堆 - for (int i = numbers.length ~/ 2; i >= 0; i--) { - await heapify(numbers, numbers.length, i); - streamController.add(numbers); - } - - // 依次取出最大堆的根节点(最大值),并进行堆化 - for (int i = numbers.length - 1; i >= 0; i--) { - int temp = numbers[0]; - numbers[0] = numbers[i]; - numbers[i] = temp; - await heapify(numbers, i, 0); - streamController.add(numbers); - } - } - - heapify(List arr, int n, int i) async { - int largest = i; - int l = 2 * i + 1; // 左子节点索引 - int r = 2 * i + 2; // 右子节点索引 - - // 如果左子节点存在并且大于父节点,则更新最大值索引 - if (l < n && arr[l] > arr[largest]) largest = l; - - // 如果右子节点存在并且大于父节点或左子节点,则更新最大值索引 - if (r < n && arr[r] > arr[largest]) largest = r; - - // 如果最大值索引不等于当前节点索引,则交换节点值,并递归进行堆化 - if (largest != i) { - int temp = numbers[i]; - numbers[i] = numbers[largest]; - numbers[largest] = temp; - heapify(arr, n, largest); - } - - await Future.delayed(getDuration()); // 延迟操作,用于可视化排序过程 - } - - ///插入排序 - insertionSort() async { - for (int i = 1; i < numbers.length; i++) { - int temp = numbers[i]; // 将当前元素存储到临时变量 temp 中 - int j = i - 1; // j 表示已排序部分的最后一个元素的索引 - - // 在已排序部分从后往前查找,找到合适位置插入当前元素 - while (j >= 0 && temp < numbers[j]) { - numbers[j + 1] = numbers[j]; // 当前元素比已排序部分的元素小,将元素后移一位 - --j; // 向前遍历 - await Future.delayed(getDuration()); - streamController.add(numbers); // 更新排序结果 - } - - numbers[j + 1] = temp; // 插入当前元素到已排序部分的正确位置 - await Future.delayed(getDuration(), () {}); - streamController.add(numbers); // 更新排序结果 - } - } - - ///地精排序 (侏儒排序) - gnomeSort() async { - int index = 0; - - while (index < numbers.length) { - // 当 index 小于数组长度时执行循环 - if (index == 0) index++; - if (numbers[index] >= numbers[index - 1]) { - // 如果当前元素大于等于前面的元素,则将 index 加1 - index++; - } else { - // 否则,交换这两个元素,并将 index 减1(使得元素可以沉到正确位置) - int temp = numbers[index]; - numbers[index] = numbers[index - 1]; - numbers[index - 1] = temp; - index--; - } - await Future.delayed(getDuration()); - streamController.add(numbers); - } - - return; - } - - ///奇偶排序(Odd-Even Sort) - oddEvenSort() async { - bool isSorted = false; - - while (!isSorted) { - // 当 isSorted 为 false 时执行循环 - isSorted = true; // 先假设数组已经排好序 - - for (int i = 1; i <= numbers.length - 2; i = i + 2) { - // 对奇数索引位置进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果当前元素大于后面的元素,则交换它们的值 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - isSorted = false; // 若发生了交换,则说明数组仍未完全排序,将 isSorted 设为 false - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - - for (int i = 0; i <= numbers.length - 2; i = i + 2) { - // 对偶数索引位置进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果当前元素大于后面的元素,则交换它们的值 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - isSorted = false; - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - return; - } - - ///快速排序 - quickSort(int leftIndex, int rightIndex) async { - // 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置 - Future _partition(int left, int right) async { - // 选择中间位置的元素作为基准元素 - int p = (left + (right - left) / 2).toInt(); - - // 交换基准元素和最右边的元素 - var temp = numbers[p]; - numbers[p] = numbers[right]; - numbers[right] = temp; - await Future.delayed(getDuration()); - streamController.add(numbers); - - // 初始化游标 cursor - int cursor = left; - - // 遍历数组并根据基准元素将元素交换到左侧或右侧 - for (int i = left; i < right; i++) { - if (cf(numbers[i], numbers[right]) <= 0) { - // 如果当前元素小于等于基准元素,则交换它和游标位置的元素 - var temp = numbers[i]; - numbers[i] = numbers[cursor]; - numbers[cursor] = temp; - cursor++; - - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - - // 将基准元素放置在游标位置 - temp = numbers[right]; - numbers[right] = numbers[cursor]; - numbers[cursor] = temp; - - await Future.delayed(getDuration()); - streamController.add(numbers); - - return cursor; // 返回基准元素的索引位置 - } - - // 如果左索引小于右索引,则递归地对数组进行快速排序 - if (leftIndex < rightIndex) { - int p = await _partition(leftIndex, rightIndex); - - await quickSort(leftIndex, p - 1); // 对基准元素左侧的子数组进行快速排序 - - await quickSort(p + 1, rightIndex); // 对基准元素右侧的子数组进行快速排序 - } - } - - // 比较函数,用于判断两个元素的大小关系 - cf(int a, int b) { - if (a < b) { - return -1; // 若 a 小于 b,则返回 -1 - } else if (a > b) { - return 1; // 若 a 大于 b,则返回 1 - } else { - return 0; // 若 a 等于 b,则返回 0 - } - } - - ///归并排序 - mergeSort(int leftIndex, int rightIndex) async { - // 定义一个名为 merge 的异步函数,用于合并两个有序子数组 - Future merge(int leftIndex, int middleIndex, int rightIndex) async { - // 计算左侧子数组和右侧子数组的大小 - int leftSize = middleIndex - leftIndex + 1; - int rightSize = rightIndex - middleIndex; - - // 创建左侧子数组和右侧子数组 - List leftList = List.generate(leftSize, (index) => 0); - List rightList = List.generate(rightSize, (index) => 0); - - // 将原始数组中的元素分别复制到左侧子数组和右侧子数组中 - for (int i = 0; i < leftSize; i++) { - leftList[i] = numbers[leftIndex + i]; - } - for (int j = 0; j < rightSize; j++) { - rightList[j] = numbers[middleIndex + j + 1]; - } - - // 初始化游标和索引 - int i = 0, j = 0; - int k = leftIndex; - - // 比较左侧子数组和右侧子数组的元素,并按顺序将较小的元素放入原始数组中 - while (i < leftSize && j < rightSize) { - if (leftList[i] <= rightList[j]) { - numbers[k] = leftList[i]; - i++; - } else { - numbers[k] = rightList[j]; - j++; - } - - await Future.delayed(getDuration()); - streamController.add(numbers); - - k++; - } - - // 将左侧子数组或右侧子数组中剩余的元素放入原始数组中 - while (i < leftSize) { - numbers[k] = leftList[i]; - i++; - k++; - - await Future.delayed(getDuration()); - streamController.add(numbers); - } - - while (j < rightSize) { - numbers[k] = rightList[j]; - j++; - k++; - - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - - // 如果左索引小于右索引,则递归地对数组进行归并排序 - if (leftIndex < rightIndex) { - // 计算中间索引位置 - int middleIndex = (rightIndex + leftIndex) ~/ 2; - - // 分别对左侧子数组和右侧子数组进行归并排序 - await mergeSort(leftIndex, middleIndex); - await mergeSort(middleIndex + 1, rightIndex); - - await Future.delayed(getDuration()); - streamController.add(numbers); - - // 合并两个有序子数组 - await merge(leftIndex, middleIndex, rightIndex); - } - } - - checkAndResetIfSorted() async { - if (isSorted) { - reset(); - await Future.delayed(const Duration(milliseconds: 200)); - } - } - - sort() async { - setState(() { - isSorting = true; - }); - - await checkAndResetIfSorted(); - - Stopwatch stopwatch = Stopwatch()..start(); - - switch (currentSort) { - case "bubble": - await bubbleSort(); - break; - case "coctail": - await cocktailSort(); - break; - case "comb": - await combSort(); - break; - case "pigeonhole": - await pigeonHole(); - break; - case "shell": - await shellSort(); - break; - case "selection": - await selectionSort(); - break; - case "cycle": - await cycleSort(); - break; - case "heap": - await heapSort(); - break; - case "insertion": - await insertionSort(); - break; - case "gnome": - await gnomeSort(); - break; - case "oddeven": - await oddEvenSort(); - break; - case "quick": - await quickSort(0, sampleSize.toInt() - 1); - break; - case "merge": - await mergeSort(0, sampleSize.toInt() - 1); - break; - } - - stopwatch.stop(); - - print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms."); - setState(() { - isSorting = false; - isSorted = true; - }); - } - - setSort(String type) { - setState(() { - currentSort = type; - }); - } - - @override - void initState() { - super.initState(); - // reset(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - sampleSize = MediaQuery.of(context).size.width / 2; - for (int i = 0; i < sampleSize; ++i) { - //随机往数组中填值 - numbers.add(Random().nextInt(500)); - } - setState(() {}); - } - - @override - void dispose() { - streamController.close(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text( - "当前选择的是:${getTitle()}", - style: const TextStyle(fontSize: 14), - ), - actions: [ - PopupMenuButton( - initialValue: currentSort, - itemBuilder: (ctx) { - return const [ - PopupMenuItem( - value: 'bubble', - child: Text("Bubble Sort — 冒泡排序"), - ), - PopupMenuItem( - value: 'coctail', - child: Text("Coctail Sort — 鸡尾酒排序(双向冒泡排序)"), - ), - PopupMenuItem( - value: 'comb', - child: Text("Comb Sort — 梳排序"), - ), - PopupMenuItem( - value: 'pigeonhole', - child: Text("pigeonhole Sort — 鸽巢排序"), - ), - PopupMenuItem( - value: 'shell', - child: Text("shell Sort — 希尔排序"), - ), - PopupMenuItem( - value: 'selection', - child: Text("Selection Sort — 选择排序"), - ), - PopupMenuItem( - value: 'cycle', - child: Text("CycleSort — 循环排序"), - ), - PopupMenuItem( - value: 'heap', - child: Text("HeapSort — 堆排序"), - ), - PopupMenuItem( - value: 'insertion', - child: Text("InsertionSort — 插入排序"), - ), - PopupMenuItem( - value: 'gnome', - child: Text("GnomeSort — 地精排序 (侏儒排序)"), - ), - PopupMenuItem( - value: 'oddeven', - child: Text("OddEvenSort — 奇偶排序"), - ), - PopupMenuItem( - value: 'quick', - child: Text("QuickSort — 快速排序"), - ), - PopupMenuItem( - value: 'merge', - child: Text("MergeSort — 归并排序"), - ), - ]; - }, - onSelected: (String value) { - reset(); - setSort(value); - }, - ) - ], - ), - body: StreamBuilder( - initialData: numbers, - stream: streamController.stream, - builder: (context, snapshot) { - List numbers = snapshot.data as List; - int counter = 0; - return Row( - children: numbers.map((int num) { - counter++; - return CustomPaint( - painter: BarPainter( - width: MediaQuery.of(context).size.width / sampleSize, - value: num, - index: counter, - ), - ); - }).toList(), - ); - }, - ), - bottomNavigationBar: BottomAppBar( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton( - onPressed: isSorting - ? null - : () { - reset(); - setSort(currentSort); - }, - child: const Text("重置")), - ElevatedButton( - onPressed: isSorting ? null : sort, child: const Text("开始排序")), - ElevatedButton( - onPressed: isSorting ? null : changeSpeed, - child: Text( - "${speed + 1}x", - style: const TextStyle(fontSize: 20), - ), - ), - ], - ), - ), - ); - } -} - -class BarPainter extends CustomPainter { - //宽度 - final double width; - - //高度(数组中对应的值) - final int value; - - //位置索引 - final int index; - - BarPainter({required this.width, required this.value, required this.index}); - - @override - void paint(Canvas canvas, Size size) { - Paint paint = Paint(); - if (value < 500 * .10) { - paint.color = Colors.blue.shade100; - } else if (value < 500 * .20) { - paint.color = Colors.blue.shade200; - } else if (value < 500 * .30) { - paint.color = Colors.blue.shade300; - } else if (value < 500 * .40) { - paint.color = Colors.blue.shade400; - } else if (value < 500 * .50) { - paint.color = Colors.blue.shade500; - } else if (value < 500 * .60) { - paint.color = Colors.blue.shade600; - } else if (value < 500 * .70) { - paint.color = Colors.blue.shade700; - } else if (value < 500 * .80) { - paint.color = Colors.blue.shade800; - } else if (value < 500 * .90) { - paint.color = Colors.blue.shade900; - } else { - paint.color = const Color(0xFF011E51); - } - - paint.strokeWidth = width; - paint.strokeCap = StrokeCap.round; - - canvas.drawLine( - Offset(index * width, 0), - Offset( - index * width, - value.ceilToDouble(), - ), - paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return true; - } -} diff --git a/lib/go_router/v0/pages/user/user_page.dart b/lib/go_router/v0/pages/user/user_page.dart deleted file mode 100644 index aba9710..0000000 --- a/lib/go_router/v0/pages/user/user_page.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter/material.dart'; - -class UserPage extends StatelessWidget { - const UserPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body:Center(child: Text('UserPage'))); - } -}