How to render home page after authenticating user in flutter? - flutter

I'm using flutter to make a mobile app.
In app.dart, the code controls the main routing.
class App extends StatelessWidget {
const App({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primaryColor: hexToColor("#6a1717")),
home: BlocBuilder<AuthenticationBloc, AuthenticationDataState>(
builder: (context, state) {
if (state.state == AuthenticationState.uninitialized) {
return SplashPage();
} else if (state.state == AuthenticationState.authenticated) {
print("now home page");
return HomePage();
} else if (state.state == AuthenticationState.unauthenticated) {
return LoginPage();
} else if (state.isLoading) {
return LoadingIndicator();
} else {
return null;
}
},
),
);
}
}
And in login bloc handler, yielded logged_in event after log in.
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final AuthenticationBloc authenticationBloc;
LoginBloc({
#required this.authenticationBloc,
}) : assert(authenticationBloc != null),
super(LoginInitial());
#override
Stream<LoginState> mapEventToState(LoginEvent event) async* {
if (event is LoginButtonPressed){
yield LoginLoading();
try {
final user = await userController.authenticate(
email: event.email,
password: event.password
);
if(user != null){
if(user.accessToken != null){
authenticationBloc.add(LoggedIn(user: user));
yield LoginInitial();
}else{
yield NotValid();
}
}else{
yield NotRegistered();
}
}catch(error){
yield LoginFailure(error: AppException.unknown(message: error.toString()));
}
}
}
}
When debugging, I checked out that the command "print("now home page");" is executed, and the processor went in home page actually but the login screen does not disapper and the home page is not rendered.
I don't know what's going on in flutter. Please help me.
Login page:
class LoginPage extends StatelessWidget {
const LoginPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
// resizeToAvoidBottomInset: false,
body: BlocProvider(
create: (context) {
return LoginBloc(
authenticationBloc: BlocProvider.of<AuthenticationBloc>(context),
);
},
child: LoginForm(),
),
);
}
}
Home Page:
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
...
}
}

Related

Flutter Bloc I Can't Logout

I'm just started with flutter bloc. I want to make a movie listing app, create your own lists and share your friends etc.
The problem is, when i tapped to logout button, UI does not update.
Here's where i try to change the UI. If the state is Authenticated(), I'm returning WatchlistNavBar(), if the state is Unauthenticated() I'm returning WelcomeView() for login or register.
If the user has submitted email and password correctly, WatchlistNavBar() is building. Everything works fine. But when the user tries the logout, WelcomeView() does not build.
By the way BlocNavigate() class is called in MaterialApp()'s home property.
class BlocNavigate extends StatelessWidget {
const BlocNavigate({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocBuilder<AuthBloc, AuthState>(
builder: (context, state) {
if (state is Loading) {
return const LoadingWidget();
} else if (state is Authenticated) {
return const WatchlistNavBar();
} else if (state is Unauthenticated) {
return const WelcomeView();
} else {
return const SignInView();
}
},
);
}
}
AuthBloc:
class AuthBloc extends Bloc<AuthEvent, AuthState> {
AuthRepository authRepository = AuthRepository();
AuthBloc(this.authRepository) : super(AuthInitial()) {
on<AuthenticationStarted>(_onAuthStarted);
on<AuthenticationSignedOut>(_onSignOut);
}
_onAuthStarted(AuthenticationStarted event, Emitter<AuthState> emit) async {
UserModel user = await authRepository.getCurrentUser().first;
if (user.uid != "uid") {
emit(Authenticated());
} else {
emit(Unauthenticated());
}
}
_onSignOut(AuthenticationSignedOut event, Emitter<AuthState> emit) async {
authRepository.signOut();
emit(Unauthenticated());
}
}
AuthState:
abstract class AuthState extends Equatable {
const AuthState();
#override
List<Object> get props => [];
}
class AuthInitial extends AuthState {}
class Authenticated extends AuthState {}
class Unauthenticated extends AuthState {}
class Loading extends AuthState {}
And this is the logout button, where i add AuthenticationSignedOut() to AuthBloc():
class LogoutButton extends StatelessWidget {
const LogoutButton({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return IconButton(
icon: const Icon(Icons.exit_to_app, color: Colors.black),
onPressed: () {
context.read<AuthBloc>().add(AuthenticationSignedOut());
});
}
}
My main function and MaterialApp():
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
Bloc.observer = AppBlocObserver();
runApp(
MultiBlocProvider(providers: [
BlocProvider(create: (context) => FormBloc()),
BlocProvider(create: (context) => DatabaseBloc(DatabaseRepositoryImpl())),
BlocProvider(
create: (context) =>
AuthBloc(AuthRepository())..add(const AuthenticationStarted())),
BlocProvider(
create: (context) => FavoritesBloc()..add(const FavoritesLoad()))
], child: const WatchlistApp()),
);
}
class WatchlistApp extends StatelessWidget {
const WatchlistApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Watchlist',
theme: WatchlistTheme.mainTheme,
home: const BlocNavigate(),
);
}
}
As i said, i'm new to flutter bloc and don't know exactly what I'm doing wrong. If you need more information please let me know.

Firebase Auth - authStateChanges() called multiple times in simple Flutter Web app

I am developing a simple app that makes the user sign in anonymously using Firebase Auth. But when I refresh the browser (Chrome), I can see that the Home screen is rendering twice in a few milliseconds.
I have seen other questions for older Firebase versions, but I did not find the solution.
class AppRoot extends StatelessWidget {
const AppRoot({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasError) {
return const Text('Error!');
} else if (snapshot.hasData) {
print("HOME"); // calling twice -> calling HomeScreen initState twice -> fetching resources twice...
return const HomeScreen();
} else {
return Container();
}
}),
);
}
}
Main:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
if (FirebaseAuth.instance.currentUser == null) {
try {
await FirebaseAuth.instance.signInAnonymously();
} catch (e) {
print(e);
}
}
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Auth Test',
theme: ThemeData(),
home: const AppRoot(),
);
}
}
Why is authStateChanges rendering HomeScreen twice? It should only once after the user logs in anonymously.

What is the purpose of authentication state unknown in flutter_bloc?

I was going through flutter_bloc documentation https://bloclibrary.dev/#/flutterlogintutorial, I saw that AuthenticationState has 3 states
enum AuthenticationStatus { authenticated, unauthenticated, unknown }
I couldn't understand the purpose of unknown state.
Here is the full code
bloc state
enum AuthenticationStatus { authenticated, unauthenticated, unknown }
class AuthenticationState extends Equatable {
const AuthenticationState._({
this.status = AuthenticationStatus.unknown,
this.user = User.empty,
});
const AuthenticationState.unknown() : this._();
const AuthenticationState.authenticated(User user)
: this._(status: AuthenticationStatus.authenticated, user: user);
const AuthenticationState.unauthenticated()
: this._(status: AuthenticationStatus.unauthenticated, user: User.empty);
final AuthenticationStatus status;
final User user;
#override
List<Object> get props => [status, user];
}
bloc_implementation
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
AuthenticationBloc({
#required AuthenticationRepository authenticationRepository,
}) : assert(authenticationRepository != null),
_authenticationRepository = authenticationRepository,
super(const AuthenticationState.unknown()) {
_userSubscription = _authenticationRepository.userStream.listen(
(user) => add(AuthenticationUserChanged(user)),
);
}
final AuthenticationRepository _authenticationRepository;
StreamSubscription<User> _userSubscription;
#override
Stream<AuthenticationState> mapEventToState(
AuthenticationEvent event,
) async* {
if (event is AuthenticationUserChanged) {
yield _mapAuthenticationUserChangedToState(event);
} else if (event is AuthenticationLogoutRequested) {
unawaited(_authenticationRepository.logOut());
}
}
#override
Future<void> close() {
_userSubscription?.cancel();
return super.close();
}
AuthenticationState _mapAuthenticationUserChangedToState(
AuthenticationUserChanged event,
) =>
event.user != User.empty
? AuthenticationState.authenticated(event.user)
: const AuthenticationState.unauthenticated();
}
UI
import 'package:authentication_repository/authentication_repository.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_login/authentication/authentication.dart';
import 'package:flutter_login/home/home.dart';
// import 'package:flutter_login/login/login.dart';
import 'package:flutter_login/splash/splash.dart';
import 'package:flutter_login/splash/view/spash_page.dart';
import 'package:user_repository/user_repository.dart';
import 'authentication/bloc/authentication_bloc.dart';
import 'home/view/home_page.dart';
import 'login/view/login_page.dart';
class App extends StatelessWidget {
const App({
Key? key,
required this.authenticationRepository,
required this.userRepository,
}) : super(key: key);
final AuthenticationRepository authenticationRepository;
final UserRepository userRepository;
#override
Widget build(BuildContext context) {
return RepositoryProvider.value(
value: authenticationRepository,
child: BlocProvider(
create: (_) => AuthenticationBloc(
authenticationRepository: authenticationRepository,
userRepository: userRepository,
),
child: AppView(),
),
);
}
}
class AppView extends StatefulWidget {
#override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView> {
final _navigatorKey = GlobalKey<NavigatorState>();
NavigatorState get _navigator => _navigatorKey.currentState!;
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navigatorKey,
builder: (context, child) {
return BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
switch (state.status) {
case AuthenticationStatus.authenticated:
_navigator.pushAndRemoveUntil<void>(
HomePage.route(),
(route) => false,
);
break;
case AuthenticationStatus.unauthenticated:
_navigator.pushAndRemoveUntil<void>(
LoginPage.route(),
(route) => false,
);
break;
default:
break;
}
},
child: child,
);
},
onGenerateRoute: (_) => SplashPage.route(),
);
}
}
I think it is to display a splash screen, but we need to edit android and ios native code to display splash screen right?
Please help me to understand its purpose.

Flutter how to wait until Future function complete

I wrote a short flutter app that have a variable that need to be initialize before I send him to another function, so I wrote a function that initializing the variable as the app started. but for some reason the code isn't waiting for the function to end and I get the "LateInitializeError" error. Someone know how can I make the code wait until the function is finished and the variable has updated?.
this is my code:
// imports...
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: AuthenticationWrapper(),
);
}
}
class AuthenticationWrapper extends StatefulWidget {
const AuthenticationWrapper({Key? key}) : super(key: key);
#override
_AuthenticationWrapperState createState() => _AuthenticationWrapperState();
}
class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
final FirebaseAuth _auth = FirebaseAuth.instance;
late Map<String, dynamic> data;
#override
void initState() {
super.initState();
getUserDataFunc().then((Map<String, dynamic> value) {
data = value; // here I am updating the variable
});
}
Future<Map<String, dynamic>> getUserDataFunc() async {
return getUserData.getUserInfo("GGji5pJyJHQevdtlhqKDKjrVOwq1");
}
#override
Widget build(BuildContext context) {
if (_auth.currentUser != null) {
return FutureBuilder<bool>(
future: loginFunctions.isBarber(_auth.currentUser!.uid),
builder: (BuildContext context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const LoadingPage();
case ConnectionState.none:
return const Text("");
case ConnectionState.active:
case ConnectionState.done:
if (snapshot.data == true) {
return const BarberHomePage();
} else {
return ClientHomePage(
uid: _auth.currentUser!.uid,
);
}
}
},
);
} else {
return ClientHomePage(
uid: "GGji5pJyJHQevdtlhqKDKjrVOwq1", userData: data); // here I am want to send that variable, and I get the error
}
}
}
The build function will run at least once before any async task. That means that ClientHomePage will always be built before data is initialized. I would just pass it as a future and have a future builder in ClientHomePage as well.
class AuthenticationWrapper extends StatefulWidget {
const AuthenticationWrapper({Key? key}) : super(key: key);
#override
_AuthenticationWrapperState createState() => _AuthenticationWrapperState();
}
class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
final FirebaseAuth _auth = FirebaseAuth.instance;
Future<Map<String, dynamic>> getUserDataFunc() async {
return getUserData.getUserInfo("GGji5pJyJHQevdtlhqKDKjrVOwq1");
}
#override
Widget build(BuildContext context) {
if (_auth.currentUser != null) {
return FutureBuilder<bool>(
future: loginFunctions.isBarber(_auth.currentUser!.uid),
builder: (BuildContext context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const LoadingPage();
case ConnectionState.none:
return const Text("");
case ConnectionState.active:
case ConnectionState.done:
if (snapshot.data == true) {
return const BarberHomePage();
} else {
return ClientHomePage(
uid: _auth.currentUser!.uid,
);
}
}
},
);
} else {
return ClientHomePage(
uid: "GGji5pJyJHQevdtlhqKDKjrVOwq1", userData: getUserDataFunc());
}
}
}
class ClientHomePage extends StatefulWidget {
const ClientHomePage({Key? key, this.data}) : super(key: key);
Future<Map<String, dynamic>> data;
#override
_ClientHomePageState createState() => _ClientHomePageState();
}
class _ClientHomePageState extends State<ClientHomePage> {
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: data,
builder: (BuildContext context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const LoadingPage();
case ConnectionState.none:
return const Text("");
case ConnectionState.active:
case ConnectionState.done:
return ClientHomePageContent(
data: data,
);
}
},
);
}
}

Flutter bloc not adding event second time

Hi I am using bloc named AuthenticationBloc to store the application auth status.
First of all here is the main.dart file.
void main() async{
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp>{
#override
Widget build(BuildContext context) => BlocProvider(
create: (context) => AuthenticationBloc(),
child: Authentication(),
);
}
authentication.dart is like this.
#override
void initState() {
authenticationBloc = BlocProvider.of<AuthenticationBloc>(context);
authenticationBloc.add(AppLoaded());
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (BuildContext context, AuthenticationState state) {
if (state is AuthenticationInitial) {
return SplashScreen();
}
if (state is AuthenticationAuthenticated) {
return new Home();
}
if (state is AuthenticationNotAuthenticated) {
return MultiBlocProvider(
providers: [
BlocProvider<LoginBloc>(create: (context) => LoginBloc(authenticationBloc)),
],
child:new LoginPage(),
);
}
if (state is AuthenticationLoading) {
return SplashScreen();
}
return SplashScreen();
},
),
);
}
Now login_bloc.dart file is.
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final AuthenticationBloc _authenticationBloc;
final repository = ApiRepository();
LoginBloc(AuthenticationBloc authenticationBloc)
: assert(authenticationBloc != null),
_authenticationBloc = authenticationBloc,
super(LoginInitial());
#override
Stream<LoginState> mapEventToState(LoginEvent event) async* {
if (event is LoginInWithEmailButtonPressed) {
yield* _mapLoginWithEmailToState(event);
}
}
Stream<LoginState> _mapLoginWithEmailToState(LoginInWithEmailButtonPressed event) async* {
yield LoginLoading();
try {
if (true) {
await Future.delayed(const Duration(seconds: 2), (){});
MySharedPreferences.instance.setStringValue('id', '1');
_authenticationBloc.add(UserLoggedIn(user: User(name:"abc",email:"abc#gmail.com")));
yield LoginSuccess();
yield LoginInitial();
} else {
yield LoginFailure(error: 'Something very weird just happened');
}
} on AuthenticationException catch (e) {
yield LoginFailure(error: e.message);
} catch (err) {
yield LoginFailure(error: err.message ?? 'An unknown error occured');
}
}
}
}
In login screen page when i log in then it successfully change the authentication-bloc state and navigated to home.
In home when i do like this
_authenticationBloc.add(UserLoggedOut()); the it successfully log out.
But after that when i click login it dispatch the event but the line
_authenticationBloc.add(UserLoggedIn(user: User(name:"abc",email:"abc#gmail.com"))); in login-bloc not working and so its not going to home.
Kindly help me why it is not dispatching the event to the bloc.
I am stucked at this point.
Thanks in advance.