Flutter State Management

setState

setState will mix the state management with the UI code, only for simple app.

================================================================================

Bloc

  1. 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,
         );
     }
     }
    
  2. 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),
        ),
        );
    }
  1. 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(.....
    
  2. Example to call EmailSignInPage()

             Navigator.of(context).push(
               MaterialPageRoute<void>(
                 // fullscreenDialog: true,
                 builder: (context) => EmailSignInPage.create(context),
               ),
             );
    

================================================================================

ChangeNotifier state management

  1. 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();
     }
     }
    
  2. 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),
         ),
         );
     }
    
  3. no need stream builder anymore, just use the model in statefull widget, such as widget.emailChangeModel.isLoading.

================================================================================

End