成功验证后如何处理已验证的路由及其重定向?
How to handle authenticated routes and their redirects after successful auth?
Flutter Web(Navigator 2.0/Router API):
认证成功后如何处理认证路由及其重定向?
例如
我的系统上有这些路线
/book/xyz (authenticated user)
/author/abc/book/xyz (authenticated user)
/authentication (non-authenticated user)
/info (non-authenticated user)
如果用户直接打开这个URL,我想让用户先登录,然后路由会被重定向到..
/authentication
登录后,我希望用户导航之前打开的 URL 如果还有其他主页..
似乎这种事情可能会有所帮助,任何想法 - 我们如何才能实现类似的事情?
我尝试了几个 Navigation 2.0 / Router 的示例 API,是的,我可以稍微理解这些概念..
有些参考资料,我看过了..
https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade
https://github.com/orestesgaolin/navigator_20_example
https://github.com/flutter/flutter/tree/master/dev/benchmarks/test_apps/stocks
这里是如何使用 VRouter >=1.2
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:vrouter/vrouter.dart';
void main() {
runApp(BooksApp());
}
class Book {
final String title;
final Author author;
Book(this.title, this.author);
}
class Author {
String name;
Author(this.name);
}
class AppState extends ChangeNotifier {
bool _isAuthenticated = false;
bool get isAuthenticated => _isAuthenticated;
void authenticate() {
if (isAuthenticated) return;
_isAuthenticated = true;
notifyListeners();
}
}
class BooksApp extends StatelessWidget {
final List<Book> books = [
Book('Stranger in a Strange Land', Author('Robert A. Heinlein')),
Book('Foundation', Author('Isaac Asimov')),
Book('Fahrenheit 451', Author('Ray Bradbury')),
];
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => AppState(),
child: Builder(
builder: (BuildContext context) {
return VRouter(
routes: [
VWidget(path: '/login', widget: AuthenticationScreen()),
VWidget(path: '/info', widget: InfoWidget()),
VGuard(
beforeEnter: (vRedirector) =>
authenticationCheck(context, vRedirector: vRedirector),
stackedRoutes: [
VWidget(
path: '/',
widget: BooksListScreen(books: books),
stackedRoutes: [
VWidget(
path: r'book/:bookId(\d+)',
widget: Builder(builder: (BuildContext context) {
return BookDetailsScreen(
book: books[int.parse(context.vRouter.pathParameters['bookId']!)],
);
}),
),
],
),
VWidget(
path: '/authors',
widget: AuthorsListScreen(authors: books.map((e) => e.author).toList()),
stackedRoutes: [
VWidget(
path: r'/author/:authorId(\d+)',
widget: Builder(builder: (BuildContext context) {
return AuthorDetailsScreen(
author: books[int.parse(context.vRouter.pathParameters['authorId']!)]
.author,
);
}),
),
],
),
],
),
],
);
},
),
);
}
Future<void> authenticationCheck(BuildContext context, {required VRedirector vRedirector}) async {
if (!Provider.of<AppState>(context, listen: false).isAuthenticated) {
vRedirector.to('/login', queryParameters: {'redirectedFrom': '${vRedirector.toUrl}'});
}
}
}
class AuthenticationScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
Provider.of<AppState>(context, listen: false).authenticate();
context.vRouter.to(context.vRouter.queryParameters['redirectedFrom'] == null
? '/'
: context.vRouter.queryParameters['redirectedFrom']!);
},
child: Text('Click to login'),
),
);
}
}
class InfoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text('Some info but actually there is nothing'),
);
}
}
class BooksListScreen extends StatelessWidget {
final List<Book> books;
BooksListScreen({required this.books});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
for (var book in books)
ListTile(
title: Text(book.title),
subtitle: Text(book.author.name),
onTap: () => context.vRouter.to('/book/${books.indexOf(book)}'),
)
],
),
);
}
}
class AuthorsListScreen extends StatelessWidget {
final List<Author> authors;
AuthorsListScreen({required this.authors});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
ElevatedButton(
onPressed: () => context.vRouter.to('/'),
child: Text('Go to Books Screen'),
),
for (var author in authors)
ListTile(
title: Text(author.name),
onTap: () => context.vRouter.to('/author/${authors.indexOf(author)}'),
)
],
),
);
}
}
class BookDetailsScreen extends StatelessWidget {
final Book book;
BookDetailsScreen({required this.book});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(book.title, style: Theme.of(context).textTheme.headline6),
ElevatedButton(
onPressed: () {
context.vRouter.to('/author/${context.vRouter.pathParameters['bookId']}');
},
child: Text(book.author.name),
),
],
),
),
);
}
}
class AuthorDetailsScreen extends StatelessWidget {
final Author author;
AuthorDetailsScreen({required this.author});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(author.name, style: Theme.of(context).textTheme.headline6),
],
),
),
);
}
}
诀窍是使用 VGuard
,在输入 stackedRoutes
之前检查用户是否已通过身份验证。
我使用 queryParameters
来存储它重定向的用户所在的位置,但是如果您不希望用户重定向的位置出现在 url 中,您可以使用 historyState
。话虽如此,在那种情况下我仍然更喜欢 queryParameters
,因为它允许 link 共享。
您可以使用 qlevar_router 来做到这一点。
从示例中,在 link 中,您可以定义一个 Middleware 和 redirectGuard 来检查用户是否可以访问此页面或其children,否则只给出重定向到的路由。
您也可以在 example 项目中看到这一点。
如果您授予访问 Child 4
的权限,它将转到 Child 4
否则您将被导航到 Child 2
.
Flutter Web(Navigator 2.0/Router API): 认证成功后如何处理认证路由及其重定向?
例如 我的系统上有这些路线
/book/xyz (authenticated user)
/author/abc/book/xyz (authenticated user)
/authentication (non-authenticated user)
/info (non-authenticated user)
如果用户直接打开这个URL,我想让用户先登录,然后路由会被重定向到..
/authentication
登录后,我希望用户导航之前打开的 URL 如果还有其他主页..
似乎这种事情可能会有所帮助,任何想法 - 我们如何才能实现类似的事情?
我尝试了几个 Navigation 2.0 / Router 的示例 API,是的,我可以稍微理解这些概念..
有些参考资料,我看过了..
https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade https://github.com/orestesgaolin/navigator_20_example https://github.com/flutter/flutter/tree/master/dev/benchmarks/test_apps/stocks
这里是如何使用 VRouter >=1.2
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:vrouter/vrouter.dart';
void main() {
runApp(BooksApp());
}
class Book {
final String title;
final Author author;
Book(this.title, this.author);
}
class Author {
String name;
Author(this.name);
}
class AppState extends ChangeNotifier {
bool _isAuthenticated = false;
bool get isAuthenticated => _isAuthenticated;
void authenticate() {
if (isAuthenticated) return;
_isAuthenticated = true;
notifyListeners();
}
}
class BooksApp extends StatelessWidget {
final List<Book> books = [
Book('Stranger in a Strange Land', Author('Robert A. Heinlein')),
Book('Foundation', Author('Isaac Asimov')),
Book('Fahrenheit 451', Author('Ray Bradbury')),
];
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => AppState(),
child: Builder(
builder: (BuildContext context) {
return VRouter(
routes: [
VWidget(path: '/login', widget: AuthenticationScreen()),
VWidget(path: '/info', widget: InfoWidget()),
VGuard(
beforeEnter: (vRedirector) =>
authenticationCheck(context, vRedirector: vRedirector),
stackedRoutes: [
VWidget(
path: '/',
widget: BooksListScreen(books: books),
stackedRoutes: [
VWidget(
path: r'book/:bookId(\d+)',
widget: Builder(builder: (BuildContext context) {
return BookDetailsScreen(
book: books[int.parse(context.vRouter.pathParameters['bookId']!)],
);
}),
),
],
),
VWidget(
path: '/authors',
widget: AuthorsListScreen(authors: books.map((e) => e.author).toList()),
stackedRoutes: [
VWidget(
path: r'/author/:authorId(\d+)',
widget: Builder(builder: (BuildContext context) {
return AuthorDetailsScreen(
author: books[int.parse(context.vRouter.pathParameters['authorId']!)]
.author,
);
}),
),
],
),
],
),
],
);
},
),
);
}
Future<void> authenticationCheck(BuildContext context, {required VRedirector vRedirector}) async {
if (!Provider.of<AppState>(context, listen: false).isAuthenticated) {
vRedirector.to('/login', queryParameters: {'redirectedFrom': '${vRedirector.toUrl}'});
}
}
}
class AuthenticationScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
Provider.of<AppState>(context, listen: false).authenticate();
context.vRouter.to(context.vRouter.queryParameters['redirectedFrom'] == null
? '/'
: context.vRouter.queryParameters['redirectedFrom']!);
},
child: Text('Click to login'),
),
);
}
}
class InfoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text('Some info but actually there is nothing'),
);
}
}
class BooksListScreen extends StatelessWidget {
final List<Book> books;
BooksListScreen({required this.books});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
for (var book in books)
ListTile(
title: Text(book.title),
subtitle: Text(book.author.name),
onTap: () => context.vRouter.to('/book/${books.indexOf(book)}'),
)
],
),
);
}
}
class AuthorsListScreen extends StatelessWidget {
final List<Author> authors;
AuthorsListScreen({required this.authors});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
ElevatedButton(
onPressed: () => context.vRouter.to('/'),
child: Text('Go to Books Screen'),
),
for (var author in authors)
ListTile(
title: Text(author.name),
onTap: () => context.vRouter.to('/author/${authors.indexOf(author)}'),
)
],
),
);
}
}
class BookDetailsScreen extends StatelessWidget {
final Book book;
BookDetailsScreen({required this.book});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(book.title, style: Theme.of(context).textTheme.headline6),
ElevatedButton(
onPressed: () {
context.vRouter.to('/author/${context.vRouter.pathParameters['bookId']}');
},
child: Text(book.author.name),
),
],
),
),
);
}
}
class AuthorDetailsScreen extends StatelessWidget {
final Author author;
AuthorDetailsScreen({required this.author});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(author.name, style: Theme.of(context).textTheme.headline6),
],
),
),
);
}
}
诀窍是使用 VGuard
,在输入 stackedRoutes
之前检查用户是否已通过身份验证。
我使用 queryParameters
来存储它重定向的用户所在的位置,但是如果您不希望用户重定向的位置出现在 url 中,您可以使用 historyState
。话虽如此,在那种情况下我仍然更喜欢 queryParameters
,因为它允许 link 共享。
您可以使用 qlevar_router 来做到这一点。
从示例中,在 link 中,您可以定义一个 Middleware 和 redirectGuard 来检查用户是否可以访问此页面或其children,否则只给出重定向到的路由。
您也可以在 example 项目中看到这一点。
如果您授予访问 Child 4
的权限,它将转到 Child 4
否则您将被导航到 Child 2
.