on
Flutter State Management
setState
setState will mix the state management with the UI code, only for simple app.
================================================================================
Bloc
-
build the model class which will be imutable, which means when state changes, there will be an new model instance:
enum FormType { signIn, register } class EmailSignModel { EmailSignModel({ this.email = "", this.password = "", this.formType = FormType.signIn, this.isLoading = false, this.submitted = false, }); final String email; final String password; final FormType formType; final bool isLoading; final bool submitted; EmailSignModel copyWith({ String? email, String? password, FormType? formType, bool? isLoading, bool? submitted, }) { return EmailSignModel( email: email ?? this.email, password: password ?? this.password, formType: formType ?? this.formType, isLoading: isLoading ?? this.isLoading, submitted: submitted ?? this.submitted, ); } } -
Build the bloc class and set up the stream for the model
// class EmailBloc is for email_sign page class EmailBloc { EmailSignModel _model = EmailSignModel(); final StreamController<EmailSignModel> _modelController = StreamController<EmailSignModel>.broadcast(); Stream<EmailSignModel> get modelStream => _modelController.stream; void dispose() { _modelController.close(); } void updateWith({ String? email, String? password, FormType? formType, bool? isLoading, bool? submitted, }) { // update model _model = _model.copyWith( email: email, password: password, formType: formType, isLoading: isLoading, submitted: submitted, ); // add updated model to _modelController _modelController.add(_model); } }
3.1. using the provider to inject the bloc class to the UI.dart
class EmailSignInPage extends StatefulWidget {
EmailSignInPage({Key? key, required this.emailBloc}) : super(key: key);
// initial the instance of EmailBloc class
final EmailBloc emailBloc;
static Widget create(BuildContext context) {
return Provider<EmailBloc>(
create: (_) => EmailBloc(),
dispose: (_, emailBloc) => emailBloc.dispose(),
child: EmailSignInPage();
3.2. OR using Comsumer replace the Provider.of(context)
class EmailSignInPage extends StatefulWidget {
EmailSignInPage({Key? key, required this.emailBloc}) : super(key: key);
// initial the instance of EmailBloc class
final EmailBloc emailBloc;
static Widget create(BuildContext context) {
return Provider<EmailBloc>(
create: (_) => EmailBloc(),
dispose: (_, emailBloc) => emailBloc.dispose(),
child: Consumer<EmailBloc>(
builder: (_, emailBloc, __) => EmailSignInPage(emailBloc: emailBloc),
),
);
}
-
using the stream builder to receive the state change, need to call Provider.of(context) first.
final emailBloc = Provider.of<EmailBloc>(context, listen: false); body: StreamBuilder<EmailSignModel>( stream: widget.emailBloc.modelStream, initialData: EmailSignModel(), builder: (context, snapshot) { return SingleChildScrollView(..... -
Example to call EmailSignInPage()
Navigator.of(context).push( MaterialPageRoute<void>( // fullscreenDialog: true, builder: (context) => EmailSignInPage.create(context), ), );
================================================================================
ChangeNotifier state management
-
Builde the model with Mixin ChangeNotifier
enum FormType { signIn, register } class EmailSignChangeModel with ChangeNotifier { EmailSignChangeModel({ this.email = "", this.password = "", this.formType = FormType.signIn, this.isLoading = false, this.submitted = false, }); String email; String password; FormType formType; bool isLoading; bool submitted; void updateWith({ String? email, String? password, FormType? formType, bool? isLoading, bool? submitted, }) { this.email = email ?? this.email; this.password = password ?? this.password; this.formType = formType ?? this.formType; this.isLoading = isLoading ?? this.isLoading; this.submitted = submitted ?? this.submitted; notifyListeners(); } } -
Inject the model to UI.dart
class EmailSignInChangePage extends StatefulWidget { EmailSignInChangePage({Key? key, required this.emailChangeModel}) : super(key: key); // initial the instance of EmailBloc class final EmailSignChangeModel emailChangeModel; static Widget create(BuildContext context) { return ChangeNotifierProvider<EmailSignChangeModel>( create: (_) => EmailSignChangeModel(), child: Consumer<EmailSignChangeModel>( builder: (_, emailChangeModel, __) => EmailSignInChangePage(emailChangeModel: emailChangeModel), ), ); } -
no need stream builder anymore, just use the model in statefull widget, such as widget.emailChangeModel.isLoading.
================================================================================
End