成功验证后如何处理已验证的路由及其重定向?

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 中,您可以定义一个 MiddlewareredirectGuard 来检查用户是否可以访问此页面或其children,否则只给出重定向到的路由。 您也可以在 example 项目中看到这一点。 如果您授予访问 Child 4 的权限,它将转到 Child 4 否则您将被导航到 Child 2.