Skip to content

Instantly share code, notes, and snippets.

@adewaleafolabi
Last active May 12, 2020 14:59
Show Gist options
  • Save adewaleafolabi/f6478aec7b58edb041dbb2a08830c1a6 to your computer and use it in GitHub Desktop.
Save adewaleafolabi/f6478aec7b58edb041dbb2a08830c1a6 to your computer and use it in GitHub Desktop.
Mobx Issue
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:line_icons/line_icons.dart';
import 'package:mobx/mobx.dart';
import 'package:wyr_app/app_store.dart';
import 'package:wyr_app/home_page.dart';
import 'package:wyr_app/login_store.dart';
import 'package:wyr_app/theme.dart';
class LoginPage extends StatefulWidget {
final AppStore appStore;
const LoginPage({Key key, this.appStore}) : super(key: key);
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
LoginStore store;
ReactionDisposer _disposer;
ReactionDisposer _navigationDisposer;
_LoginPageState();
@override
void initState() {
super.initState();
store = LoginStore(widget.appStore);
store.setupValidations();
_disposer = reaction(
(_) => store.message,
(AppMessage result) => _scaffoldKey.currentState.showSnackBar(SnackBar(
backgroundColor:
result.error ? Colors.red : Theme.of(context).accentColor,
content: Text(result.message),
)));
_navigationDisposer = reaction(
(_) => store.loggedIn,
(bool result) => Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => HomePage(
appStore: widget.appStore,
),
),
));
}
@override
void dispose() {
_disposer();
_navigationDisposer();
store.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final TextStyle _cardTextStyle = TextStyle(
fontSize: 35.0,
color: swatch1,
fontFamily: "Spartan",
letterSpacing: 2);
return Scaffold(
backgroundColor: Theme.of(context).primaryColor,
key: _scaffoldKey,
body: Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/logo.png",
height: 100,
),
SizedBox(
height: 50,
),
Observer(builder: (_) {
return TextFormField(
style: TextStyle(color: Colors.white),
keyboardType: TextInputType.emailAddress,
onChanged: (value) => store.setUsername(value),
decoration: InputDecoration(
focusColor: Colors.red,
fillColor: Theme.of(context).accentColor,
filled: true,
hintText: 'Username',
hintStyle: TextStyle(color: Colors.white),
errorText: store.error.username),
);
}),
SizedBox(
height: 15,
),
Observer(builder: (_) {
return TextFormField(
style: TextStyle(color: Colors.white),
keyboardType: TextInputType.text,
obscureText: true,
onChanged: (value) => store.setPassword(value),
decoration: InputDecoration(
fillColor: Theme.of(context).accentColor,
filled: true,
hintText: 'Password',
hintStyle: TextStyle(color: Colors.white),
errorText: store.error.password),
);
}),
SizedBox(
height: 15,
),
Observer(
builder: (_) => RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
color: Theme.of(context).accentColor,
textColor: Theme.of(context).primaryColor,
child: Padding(
padding: const EdgeInsets.all(15),
child: store.loading
? SizedBox(
width: 10,
height: 10,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white),
strokeWidth: 1,
),
)
: Text(
"Login",
style: TextStyle(
color: Colors.white,
),
),
),
onPressed: store.loading
? () {}
: () {
store.validateAll();
if (store.loginValidationPassed) {
store.login();
}
},
)),
SizedBox(
height: 30,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(right: 10),
child: FloatingActionButton(
heroTag: 'Google',
child: Icon(LineIcons.google),
onPressed: () {},
),
),
Padding(
padding: const EdgeInsets.only(right: 10),
child: FloatingActionButton(
heroTag: 'Facebook',
child: Icon(LineIcons.facebook_official),
onPressed: () {},
),
),
Padding(
padding: const EdgeInsets.only(right: 10),
child: FloatingActionButton(
heroTag: 'Twitter',
child: Icon(LineIcons.twitter),
onPressed: () {},
),
),
],
)
],
),
),
);
}
}
import 'package:flutter/cupertino.dart';
import 'package:grpc/grpc.dart';
import 'package:mobx/mobx.dart';
import 'package:string_validator/string_validator.dart';
import 'package:wyr_app/app_store.dart';
import 'package:jwt_decode/jwt_decode.dart';
import 'package:wyr_app/service/grpc_common.dart';
import 'package:wyr_app/service/wyr.pbgrpc.dart';
part 'login_store.g.dart';
class AppMessage {
bool error;
String message;
AppMessage(this.error, this.message);
}
class LoginStore = _LoginStore with _$LoginStore;
enum LoginFormType { LOGIN, INITIATE_RESET, COMPLETE_RESET }
abstract class _LoginStore with Store {
@observable
LoginFormType loginFormType = LoginFormType.LOGIN;
@observable
String username = '';
@observable
String password = '';
@observable
String resetToken = '';
@observable
bool loading = false;
@observable
bool loggedIn = false;
@observable
AppMessage message;
final AppStore applicationStore;
final LoginFormErrorState error = LoginFormErrorState();
final PublicClient authClient = PublicClient(GrpcClientSingleton().client);
_LoginStore(this.applicationStore);
@action
void setUsername(String value) {
username = value;
}
@action
void setResetToken(String value) {
resetToken = value;
}
@action
void setPassword(String value) {
password = value;
}
@action
void setProcessing(bool value) {
loading = value;
}
@action
void setLoggedIn(bool value) {
loggedIn = value;
}
@action
void setLoginFormType(LoginFormType value) {
loginFormType = value;
}
@action
void validateUsername(String value) {
error.username = isEmail(value) ? null : 'Not a valid username';
}
@action
void validatePassword(String value) {
error.password = value.isNotEmpty ? null : 'Password cannot be blank';
}
@action
void validateResetToken(String value) {
error.resetToken = value.isNotEmpty ? null : 'Not a valid reset token';
}
@action
void setMessage(AppMessage value) => this.message = value;
@action
// ignore: avoid_void_async
Future login() async {
validateAll();
if (!loginValidationPassed) {
return;
}
loading = true;
try {
UserLoginRequest request = UserLoginRequest();
request.email = username;
request.password = password;
print(request.username);
final response = await ObservableFuture(authClient.userLogin(request));
if (!response.hasJwtToken()) {
error.authenticationError = 'invalid account';
}
print(Jwt.parseJwt(response.jwtToken));
error.authenticationError = null;
loggedIn = true;
setMessage(AppMessage(false, 'login successful'));
this.message = AppMessage(false, 'login successful');
} on GrpcError catch (err) {
print(err);
error.authenticationError = err.message;
setMessage(AppMessage(true, err.message));
this.message = AppMessage(true, err.message);
} catch (err) {
print(err);
setMessage(AppMessage(true, err.message));
this.message = AppMessage(true, err.message);
}
this.loading = false;
return;
}
@computed
bool get loginValidationPassed => !error.hasLoginValidationErrors;
@computed
bool get initPasswordResetValidationPassed =>
!error.hasPasswordResetInitiateValidationErrors;
@computed
bool get completePasswordResetValidationPassed =>
!error.hasPasswordResetCompleteValidationErrors;
List<ReactionDisposer> _disposers;
void setupValidations() {
_disposers = [
reaction((_) => username, validateUsername),
reaction((_) => password, validatePassword),
reaction((_) => resetToken, validateResetToken)
];
}
void dispose() {
for (final d in _disposers) {
d();
}
}
void validateAll() {
validatePassword(password);
validateUsername(username);
}
}
class LoginFormErrorState = _LoginFormErrorState with _$LoginFormErrorState;
abstract class _LoginFormErrorState with Store {
@observable
String username;
@observable
String resetToken;
@observable
String password;
@observable
String authenticationError;
@computed
bool get hasLoginValidationErrors => username != null || password != null;
@computed
bool get hasPasswordResetInitiateValidationErrors => username != null;
@computed
bool get hasPasswordResetCompleteValidationErrors =>
resetToken != null || password != null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment