使用Flutter-Fire身份验证守卫来保护安全路由,并避免不必要的重建。

14 浏览
0 Comments

使用Flutter-Fire身份验证守卫来保护安全路由,并避免不必要的重建。

我现在面临的问题是,我将身份验证守卫视图设置为默认路由。

我的身份验证守卫视图:

import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

import '../models/user.dart';

import '../services/services.module.dart';

import '../widgets/common/async_stream.dart';

import 'landing_screen/landing_screen.dart';

import 'tabs_screen/tab_screen.dart';

/// [ViewAuthGuard]决定显示[LandingScreenView]还是[TabsScreenView]。

class ViewAuthGuard extends StatelessWidget {

@override

Widget build(BuildContext context) {

print('ViewAuthGuard build called: $context');

FirebaseAuthService authService = Provider.of(context, listen: false);

return AsyncStreamWidget(

stream: authService.onAuthStateChanged,

child: (User user) => TabsScreenView(),

emptyWidget: LandingScreenView(),

loadingWidget: null,

errorWidget: null,

);

}

}

和我的`AsyncStreamWidget`:

import 'package:flutter/material.dart';

import '../../../models/base_model.dart';

import '../../error/future_error.dart';

import '../../loading.dart';

class AsyncStreamWidget extends StatelessWidget {

final Stream stream;

final T initialData;

Widget _loading;

Widget _empty;

Widget Function(Object) _error;

Widget Function(T) child;

AsyncStreamWidget({

@required this.stream,

@required this.child,

this.initialData,

Widget loadingWidget,

Widget emptyWidget,

Widget Function(Object) errorWidget,

}) {

if (loadingWidget == null) {

_loading = Loading();

} else {

_loading = loadingWidget;

}

if (errorWidget == null) {

_error = (Object error) => FutureErrorWidget(error: error);

} else {

_error = errorWidget;

}

if (emptyWidget == null) {

_empty = Center(child: Text('No data available.'));

} else {

_empty = emptyWidget;

}

}

@override

Widget build(BuildContext context) {

return StreamBuilder(

initialData: initialData,

stream: stream,

builder: (_, AsyncSnapshot snapshot) {

switch (snapshot.connectionState) {

case ConnectionState.waiting:

return _loading;

break;

case ConnectionState.active: // check if different behavior is needed for active and done

case ConnectionState.done:

// error state

if (snapshot.hasError) {

// todo more throughout error checking and specialized error widget

return _error(snapshot.error);

}

// data state

if (snapshot.hasData) {

T data = snapshot.data;

return child(data);

}

// empty state

return _empty;

case ConnectionState.none:

default:

print('E: Received Future [$stream] was null or else.');

return _error('Unknown error.');

}

},

);

}

}

`FirebaseAuthService`包装了`auth.FirebaseAuth.instance`。我的流构造如下:

User _userFromFirebase(auth.User user) {

if (user == null) {

return null;

}

return User(

uid: user.uid,

email: user.email,

displayName: user.displayName,

photoUrl: user.photoURL,

);

}

@override

Stream get onAuthStateChanged => _firebaseAuth.authStateChanges().map(_userFromFirebase);

我目前在`ViewAuthGuard`之上提供了所有的服务。

我用ThemeProvider ChangeNotifier包装了我的Material App(如果这可能是一个问题)。

我的问题是,`ViewAuthGuard`之下的所有小部件都会重新构建,并且它们的状态会被重置。这在我开发时发生。当进行热重载时,所有的子部件都会重新构建。`TabsScreenView`包含我Flutter应用程序的初始导航,并且在开发过程中始终重置为索引零。

问题:我如何避免这个时候不必要的重新加载?

我测试了以下内容:

- 使用FutureBuilder / StreamBuilder包装了我的命名路由`TabsScreenView`,并将其设置为默认路由([Flutter中的路由守卫](https://stackoverflow.com/questions/51027858/route-guards-in-flutter))

- 在`didComponentUpdate`中监听流,并在用户更改时推送命名路由

- 上面提供的解决方案

如果您需要更多信息,代码,控制台打印或其他支持,请给我留言。谢谢!

0
0 Comments

问题的原因是,stream的改变会导致AsyncStreamWidget的重建。作者通过打印streamhashCode属性,发现在热重载过程中stream发生了改变。

解决方法是将ViewAuthGuard改为StatefulWidget,并使用didChangeDependencies方法将stream存储在状态中。此外,作者还将小部件的实例化放在initState方法中,并将它们存储在状态中,以确保它们只被创建一次。

需要注意的是,ViewAuthGuard没有任何依赖项,所以它不会因为依赖项的变化而重建。

0