My app's MyApp
widget, which returns MaterialApp
(as done in virtually every Flutter app) is rebuild whenever the build function on the home
widget is called. I need to know why this happens, as it greatly reduces the performance of my app.
I use a StreamProvider (the Riverpod implementation of StreamBuilder) to either show my app's HomePage
, LandingPage
or loading screen (called PseudoSplashScreen
for historical reasons), depending on whether a user is logged in or not, or whether the stream is waiting.
My main.dart
contains, among other things:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.instance.unsubscribeFromTopic('allUsers');
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
debugPrint("Returning MaterialApp");
return MaterialApp(
title: 'MyApp',
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
debugShowCheckedModeBanner: false,
theme: themeDataLight(),
darkTheme: themeDataDark(),
home: const ReDirector(),
);
}
}
class ReDirector extends ConsumerWidget {
const ReDirector({Key? key}) : super(key: key);
static const LandingPage landingPage = LandingPage();
static const PseudoSplashScreen pseudoSplashScreen = PseudoSplashScreen();
@override
Widget build(BuildContext context, WidgetRef ref) {
debugPrint("Building Redirector");
return ref.watch(authStreamProvider).when(
data: (data) {
debugPrint(data.toString());
if (data != null && data == AuthResultStatus.successful) {
debugPrint("Returning Homepage");
return Container(
width: double.infinity,
height: double.infinity,
color: Colors.blue,
);
} else {
debugPrint("AuthStreamProvider returned $data");
// When logging in, it is set to true. Hence, set it to false to prevent
// the isInAsync overlay from showing when logging out.
ref.read(landingPageProvider).isInAsync = false;
return landingPage;
}
},
error: (e, tb) {
debugPrint("Error in the AuthChecker");
debugPrint("$e\n$tb");
// When logging in, it is set to true. Hence, set it to false to prevent
// the isInAsync overlay from showing on error
ref.read(landingPageProvider).isInAsync = false;
return landingPage;
},
loading: () {
debugPrint("Returning PseudoSplashScreen");
return pseudoSplashScreen;
},
);
}
}
The Stream is derived from FirebaseAuth.instance.authStateChanges
but is expanded to check some extra details on the user:
final authStreamProvider = StreamProvider.autoDispose<AuthResultStatus?>((ref) {
return FirebaseAuth.instance
.authStateChanges()
.asyncExpand((User? user) async* {
AuthResultStatus? result;
if (user != null) {
final IdTokenResult idTokenResult = await user.getIdTokenResult();
if (user.emailVerified && idTokenResult.claims!['approved'] == true) {
ref.read(userDataProvider).initialize(user, idTokenResult.claims!);
result = AuthResultStatus.successful;
} else {
result = AuthResultStatus.undefined;
}
}
debugPrint("AuthStreamProvider is yielding $result");
yield result;
});
});
Where AuthResultStatus
is an enum. Now I would expect that while the stream is loading, the PseudoSplashScreen is shown, and when the Stream fires an AuthResultStatus.successful
, the HomePage
is shown. This is indeed what happens, but somehow my Redirector
is rebuild about a second after the HomePage is shown. In fact, the build function of MyApp
is called again! Regarding the debugPrints in the code, the console shows this:
I/flutter (22428): Returning MaterialApp
I/flutter (22428): Building Redirector
I/flutter (22428): Returning PseudoSplashScreen
I/flutter (22428): Creating new userdatamodel
I/flutter (22428): CURRENTUSER: Wessel van Dam
I/flutter (22428): AuthStreamProvider is yielding AuthResultStatus.successful
I/flutter (22428): Building Redirector
I/flutter (22428): AuthResultStatus.successful
I/flutter (22428): Returning Homepage
I/flutter (22428): Returning MaterialApp
I/flutter (22428): Building Redirector
I/flutter (22428): AuthResultStatus.successful
I/flutter (22428): Returning Homepage
Note that the rebuilding of the Redirector is not due to a new firing event of the Stream, because then you would expect another print of Returning successful ARS
. However, this rebuild of the Redirector is pretty annoying as building the HomePage is a pretty intense process. This rebuild causes a the screen to flicker. Could anyone tell me why the Redirector's build function is called again in this sequence? If that can be prevented, the user experience for my app would be greatly improved.