The Differences Between Provider Pattern and Bloc Pattern
By: Dlt Labs
Tags: Technology, Ios Development, Dltlabs, Programming, Android Development

Building a login screen using Provider Pattern

What are we trying to achieve here?

We are trying to write the code in which as soon as user starts typing in the text field, the text field keeps showing the error message to the user as the email or password he is typing is a valid email or password.

Based on that, if email and password match, the regex will allow the UI button to be enabled, making it clickable.

login_screen.dart class LoginScreenWithProvider extends StatelessWidget {



@override

Widget build(BuildContext context) {





return ChangeNotifierProvider<LoginProvider>(



create: (context) => LoginProvider(),

child: Scaffold(

appBar: AppBar(title: Text(‘Login’)),

body: Padding(

padding: const EdgeInsets.all(8.0),



child: ListView(

children: [



// Email Field

Consumer<LoginProvider>(builder: (context, provider, child) {

return TextField(

decoration: InputDecoration(

labelText: “Email”,

errorText: provider.email.error,

),

onChanged: (String value) {

print(‘provider.email.error ${provider.email.error}’);

provider.changeEmail(value);

},

);

}),





],

),

)),

);

}

}

Note: To do this, we will have to keep rebuilding the widget (in our case it is a text field) but not the whole widget tree to comply with programming best practices.

You need to wrap the Scaffold widget inside the ChangeNotifierProvider widget, so that we have the instance of Provider object to listen the changes.

Most importantly, the widget you want to rebuild should be the child of the Consumer widget, because it listens for changes in data should any event happen, so that it can rebuild and update the widget on change in data.

The best use of the Consumer widget is to use it as deep as possible within the widget tree.

The best practise here would be to rebuild the desired widget in the widget tree which needs the update. The best way to do that is to use the Consumer as deep as possible.

Now we check for change notifier or provider class:

class LoginProvider with ChangeNotifier {



ValidationModel _email = ValidationModel(null,null);



//Getters

ValidationModel get email => _email;



bool get isValid {

if (_email.value != null){

return true;

} else {

return false;

}

}



//Setters

void changeEmail(String value){



if (ValidatorType.email.hasMatch(value)){

_email=ValidationModel(value,null);



} else if (value.isEmpty){

_email=ValidationModel(null,null);



} else {

_email=ValidationModel(null, “Enter a valid email”);



}

notifyListeners();



}



@override

void dispose() {

super.dispose();

}



}

As of now, for the sake of simplicity, we have picked Email text field only to check how it works. On every keyboard hit we call the changeEmail method to check if the email typed matches the regex or not. NotifyListeners() will keep notifying the Consumer widget to rebuild the text field widget and errorText will be updated accordingly.

The password field also works in the same way:

ListView(

children: [



// Email Field

Consumer<LoginProvider>(builder: (context, provider, child) {

return TextField(

decoration: InputDecoration(

labelText: “Email”,

errorText: provider.email.error,

),

onChanged: (String value) {

provider.changeEmail(value);

},

);

}),



// Password Field

Consumer<LoginProvider>(builder: (context, provider, child) {

return TextField(

decoration: InputDecoration(

labelText: “Password”,

errorText: provider.password.error,

),

onChanged: (String value) {

provider.changePassword(value);

},

);

}),



//Update Indicator

Consumer<LoginProvider>(builder: (context, provider, child) {

return Container(

height: 150,

child: Center(

child: provider.state == ViewState.Idle

? Container()

: CircularProgressIndicator(),

),

);

}),



//Submit button will be enabled on correct format of email and password

Consumer<LoginProvider>(builder: (context, provider, child) {

return RaisedButton(

color: Colors.blue,

disabledColor: Colors.grey,

child: Text(

‘Submit’,

style: TextStyle(color: Colors.white),

),

onPressed: (!provider.isValid)

? null

: () {

provider

.submitLogin()

.then((LoginResponse response) {

print(‘response: ${response.token} ‘);



Navigator.push(

context,

MaterialPageRoute(

builder: (context) => DashboardScreen()));

});

},

);

})

],

)

The login provider class will be:

login_provider.dart class LoginProvider with ChangeNotifier implements LoaderState {



LoginProvider(){

setState(ViewState.Idle);

}





ValidationModel _email = ValidationModel(null,null);

ValidationModel _password = ValidationModel(null,null);



//Getters

ValidationModel get email => _email;

ValidationModel get password => _password;





bool get isValid {

if (_password.value != null && _email.value != null){

return true;

} else {

return false;

}

}



//Setters

void changeEmail(String value){



if (ValidatorType.email.hasMatch(value)){

_email=ValidationModel(value,null);



} else if (value.isEmpty){

_email=ValidationModel(null,null);



} else {

_email=ValidationModel(null, “Enter a valid email”);



}

notifyListeners();



}



void changePassword(String value){



if (ValidatorType.password.hasMatch(value)){

_password=ValidationModel(value,null);

} else if (value.isEmpty){

_password=ValidationModel(null,null);

} else {

_password=ValidationModel(null, “Must have at least 8 characters”);

}

notifyListeners();

}



ViewState _state;



@override

void setState(ViewState viewState) {

_state = viewState;

notifyListeners();

}



@override

ViewState get state => _state;



Future<LoginResponse> submitLogin() async {



setState(ViewState.Busy);

final Mapable response = await apiClient.serverDataProvider.login(_email.value, _password.value,);

setState(ViewState.Idle);

if (response is LoginResponse) {

print(‘response.token ${response.token}’);



return response;

}

}



@override

void dispose() {



super.dispose();

}



}

Upon typing of email and password, isValid is getting updated. If isValid is received as true by Consumer with RaisedButton child, then the button gets enabled, else it will remain disabled.