Reputation: 1518
I currently face an issue where I route to an authentication guard view as my default route. My authentication guard view:
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';
/// The [ViewAuthGuard] decides whether to display the [LandingScreenView] or the [TabsScreenView].
class ViewAuthGuard extends StatelessWidget {
@override
Widget build(BuildContext context) {
print('ViewAuthGuard build called: $context');
FirebaseAuthService authService = Provider.of<AuthService>(context, listen: false);
return AsyncStreamWidget<User>(
stream: authService.onAuthStateChanged,
child: (User user) => TabsScreenView(),
emptyWidget: LandingScreenView(),
loadingWidget: null,
errorWidget: null,
);
}
}
and my AsyncStreamWidget
:
import 'package:flutter/material.dart';
import '../../../models/base_model.dart';
import '../../error/future_error.dart';
import '../../loading.dart';
class AsyncStreamWidget<T extends BaseModel> extends StatelessWidget {
final Stream<T> 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<T>(
initialData: initialData,
stream: stream,
builder: (_, AsyncSnapshot<T> 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.');
}
},
);
}
}
The FirebaseAuthService
wraps the auth.FirebaseAuth.instance
. My stream is constructed as follows:
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<User> get onAuthStateChanged => _firebaseAuth.authStateChanges().map(_userFromFirebase);
I currently provide all my services above the ViewAuthGuard
.
I wrapped my Material app with a ThemeProvider ChangeNotifier (in case that could be an issue).
My issue is that all widgets below the ViewAuthGuard
are rebuild and their state is reset. This occurs to me while developing. When a hot reload occurs, all the children are rebuild. The TabsScreenView
contains the initial navigation for my flutter app and always reset to index zero during development.
Question: How do I avoid the unnecessary reloads at this point?
What I tested so far:
TabsScreenView
with FutureBuilder / StreamBuilder and set it as the default route (Route Guards in Flutter)didComponentUpdate
and pushing named routes on User changePlease drop me a comment if you need more information, code, console prints or else to support me. Thank you!
Upvotes: 0
Views: 1067
Reputation: 1518
I was able to fix it myself. For anyone interested, this was the process:
The stream
is the pivot element which decides if the AsyncStreamWidget
rebuilds its child or not. So I did check if the stream
changed across hot reloads by printing its hashCode
attribute (and yes, it did change).
Subsequently, I did change my ViewAuthGuard
to a StatefulWidget
and used the didChangeDependencies
method to store the stream
in the state.
(I also started instantiating the widgets in the initState
method and store them in the state as well so that they are created only once.)
EDIT And I should mention that my ViewAuthGuard
has no dependencies. So it is not rebuilt due to a change of dependencies.
Upvotes: 0