Created
November 25, 2018 02:39
-
-
Save felangel/00c0f98c8378e29d1eaebb08d481dba2 to your computer and use it in GitHub Desktop.
Login Bloc w/Navigation Sample
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:flutter/material.dart'; | |
import 'package:meta/meta.dart'; | |
import 'package:bloc/bloc.dart'; | |
import 'package:flutter_bloc/flutter_bloc.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Bloc Navigation Demo', | |
home: LoginPage(), | |
); | |
} | |
} | |
// Login Page | |
class LoginPage extends StatefulWidget { | |
@override | |
State<LoginPage> createState() => LoginPageState(); | |
} | |
class LoginPageState extends State<LoginPage> { | |
final _loginBloc = LoginBloc(); | |
final _usernameController = TextEditingController(); | |
final _passwordController = TextEditingController(); | |
@override | |
Widget build(BuildContext context) { | |
return BlocBuilder<LoginEvent, LoginState>( | |
bloc: _loginBloc, | |
builder: ( | |
BuildContext context, | |
LoginState loginState, | |
) { | |
if (_loginSucceeded(loginState)) { | |
_loginBloc.dispatch(LoggedIn()); | |
_onWidgetDidBuild(() { | |
Navigator.of(context).pushReplacement( | |
MaterialPageRoute( | |
builder: (context) { | |
return HomePage(); | |
}, | |
), | |
); | |
}); | |
} | |
if (_loginFailed(loginState)) { | |
_onWidgetDidBuild(() { | |
Scaffold.of(context).showSnackBar( | |
SnackBar( | |
content: Text('${loginState.error}'), | |
backgroundColor: Colors.red, | |
), | |
); | |
}); | |
} | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Login'), | |
), | |
body: _form(loginState), | |
); | |
}, | |
); | |
} | |
Widget _form(LoginState loginState) { | |
return Form( | |
child: Column( | |
children: [ | |
TextFormField( | |
decoration: InputDecoration(labelText: 'username'), | |
controller: _usernameController, | |
), | |
TextFormField( | |
decoration: InputDecoration(labelText: 'password'), | |
controller: _passwordController, | |
obscureText: true, | |
), | |
RaisedButton( | |
onPressed: | |
loginState.isLoginButtonEnabled ? _onLoginButtonPressed : null, | |
child: Text('Login'), | |
), | |
Container( | |
child: loginState.isLoading ? CircularProgressIndicator() : null, | |
), | |
], | |
), | |
); | |
} | |
bool _loginSucceeded(LoginState state) => state.token.isNotEmpty; | |
bool _loginFailed(LoginState state) => state.error.isNotEmpty; | |
void _onWidgetDidBuild(Function callback) { | |
WidgetsBinding.instance.addPostFrameCallback((_) { | |
callback(); | |
}); | |
} | |
_onLoginButtonPressed() { | |
_loginBloc.dispatch( | |
LoginButtonPressed( | |
username: _usernameController.text, | |
password: _passwordController.text, | |
), | |
); | |
} | |
} | |
// Home Page // | |
class HomePage extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Home'), | |
), | |
body: Center( | |
child: Text('Welcome Home'), | |
), | |
); | |
} | |
} | |
// Login Bloc // | |
abstract class LoginEvent {} | |
class LoginButtonPressed extends LoginEvent { | |
final String username; | |
final String password; | |
LoginButtonPressed({ | |
@required this.username, | |
@required this.password, | |
}); | |
@override | |
bool operator ==(Object other) => | |
identical(this, other) || | |
other is LoginButtonPressed && | |
runtimeType == other.runtimeType && | |
username == other.username && | |
password == other.password; | |
@override | |
int get hashCode => username.hashCode ^ password.hashCode; | |
} | |
class LoggedIn extends LoginEvent {} | |
class LoginState { | |
final bool isLoading; | |
final bool isLoginButtonEnabled; | |
final String error; | |
final String token; | |
const LoginState({ | |
@required this.isLoading, | |
@required this.isLoginButtonEnabled, | |
@required this.error, | |
@required this.token, | |
}); | |
factory LoginState.initial() { | |
return LoginState( | |
isLoading: false, | |
isLoginButtonEnabled: true, | |
error: '', | |
token: '', | |
); | |
} | |
factory LoginState.loading() { | |
return LoginState( | |
isLoading: true, | |
isLoginButtonEnabled: false, | |
error: '', | |
token: '', | |
); | |
} | |
factory LoginState.failure(String error) { | |
return LoginState( | |
isLoading: false, | |
isLoginButtonEnabled: true, | |
error: error, | |
token: '', | |
); | |
} | |
factory LoginState.success(String token) { | |
return LoginState( | |
isLoading: false, | |
isLoginButtonEnabled: true, | |
error: '', | |
token: token, | |
); | |
} | |
@override | |
String toString() => | |
'LoginState { isLoading: $isLoading, isLoginButtonEnabled: $isLoginButtonEnabled, error: $error, token: $token }'; | |
} | |
class LoginBloc extends Bloc<LoginEvent, LoginState> { | |
@override | |
LoginState get initialState => LoginState.initial(); | |
@override | |
Stream<LoginState> mapEventToState( | |
LoginState state, | |
LoginEvent event, | |
) async* { | |
if (event is LoginButtonPressed) { | |
yield LoginState.loading(); | |
try { | |
final token = await _getToken( | |
username: event.username, | |
password: event.password, | |
); | |
yield LoginState.success(token); | |
} catch (error) { | |
yield LoginState.failure(error.toString()); | |
} | |
} | |
if (event is LoggedIn) { | |
yield LoginState.initial(); | |
} | |
} | |
Future<String> _getToken({ | |
@required String username, | |
@required String password, | |
}) async { | |
await Future.delayed(Duration(milliseconds: 500)); | |
return 'token'; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment