mobxjs / mobx.dart

MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.
https://mobx.netlify.app
MIT License
2.39k stars 310 forks source link

Observer widget doesnt observe the new state (Flutter web) #959

Closed WowVir closed 9 months ago

WowVir commented 9 months ago

I have this base controller I usually use to extend all my controllers

`import 'package:mobx/mobx.dart'; import 'package:wow_vir_admin_dashboard/core/presentation/snakebars/bottom_snack_bar.dart'; import 'package:wow_vir_admin_dashboard/core/utils/enums.dart'; import 'package:wow_vir_admin_dashboard/data/errors/core_errors.dart'; import 'package:wow_vir_admin_dashboard/data/errors/custom_error.dart'; import 'package:wow_vir_admin_dashboard/data/repositories/abstract/i_logger.dart';

part 'base_controller.g.dart';

class BaseController extends BaseControllerBase with _$BaseController{ BaseController(super.logger); }

abstract class BaseControllerBase with Store {

final ILogger logger;

BaseControllerBase(this.logger);

@observable CustomError? error;

@observable bool isLoading=false;

@action Future onInit()async{}

@action void dispose(){}

@action Future runStorePrimaryFunction(final Future function, {final void Function(CustomError)? onCatchError })async{ try{ if(isLoading||error!=null) { return false; }

  isLoading=true;
  await function;
  return true;
}
on CustomError catch(e,stackTrace){
  if(onCatchError!=null) {
    onCatchError(e);
  }
  if(e is ServerError) {
    await logger.logCritical(exception:  e,stackTrace: stackTrace);
  }
  error=e;
  return false;
}
on Exception catch(e,stackTrace){
  error=ServerError();

  await logger.logCritical(exception:  e,stackTrace: stackTrace);
  return false;
}
finally{
  isLoading=false;
}

}

}`

for every form in my application I use a mixin to provide form functionality `

import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; import 'package:wow_vir_admin_dashboard/core/controllers/base_controller.dart'; import 'package:wow_vir_admin_dashboard/data/errors/api_error_model.dart';

mixin FormMixin on BaseController{ final GlobalKey formKey = GlobalKey(); Future submitFunction() ; Future afterSuccessSubmitting() ; List focusNodes=[];

void initForm({required final int fieldsNumber,final Map<String,String?>? initialValues}){ currentValues=ObservableMap.of(initialValues??{}); focusNodes=ObservableList.of(List.generate(fieldsNumber, (final index) =>FocusNode() )); }

@observable ObservableMap<String,String?> currentValues=ObservableMap();

@observable ObservableMap<String,String> validationErrors=ObservableMap();

@action void changeFormValue(final String fieldKey,final String? newValue)=> currentValues[fieldKey]=newValue;

@computed bool get canSubmit=> formKey.currentState!.validate();

@action Future submitForm()=>runStoreSecondaryFunction(Future(()async{ if(isLoading || !canSubmit){ return; }

formKey.currentState!.save();
validationErrors=ObservableMap();
isLoading=true;
print('$isLoading');

    await submitFunction();
isLoading=false;
await afterSuccessSubmitting();

} ),onCatchError: (final e){ isLoading=false; if(e is ApiErrorModel) { validationErrors=ObservableMap.of( e.validationErrors); } });

@override void dispose(){ for (var element in focusNodes) { element.dispose(); } } } `

and I extend it with my VendorDetailsController class

`

import 'package:injectable/injectable.dart'; import 'package:mobx/mobx.dart'; import 'package:wow_vir_admin_dashboard/core/controllers/base_controller.dart'; import 'package:wow_vir_admin_dashboard/core/controllers/controllers_mixins/form_mixin.dart'; import 'package:wow_vir_admin_dashboard/core/utils/enums.dart'; import 'package:wow_vir_admin_dashboard/data/errors/core_errors.dart';

part 'vendor_detailscontroller.g.dart'; @injectable class VendorDetailsController extends VendorDetailsControllerBase with $VendorDetailsController{ VendorDetailsController(super.logger);

}

abstract class VendorDetailsControllerBase extends BaseController with Store,FormMixin{

VendorDetailsControllerBase(super.logger){ initForm(fieldsNumber: 8); }

@override Future afterSuccessSubmitting()async { await Future.delayed(Duration(seconds: 10)); print('Ended'); }

@override Future submitFunction() async{ print('WISO HERE'); /if(currentValues['wowVirDeliveryCommission']==null &&currentValues['vendorDeliveryCommission']==null ) throw NotSelectedError(fieldName: 'Commission');/ }

}`

I am trying to submit the form so the value of isLoading would become true and showing a circular progress indicator instead of the button in the following widget

`import 'dart:ui';

import 'package:auto_route/annotations.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_vir_admin_dashboard/core/presentation/validators/is_not_empty_validator.dart'; import 'package:wow_vir_admin_dashboard/core/presentation/widgets/custom_app_bar.dart'; import 'package:wow_vir_admin_dashboard/core/presentation/widgets/custom_sized_box.dart'; import 'package:wow_vir_admin_dashboard/core/presentation/widgets/main_button.dart'; import 'package:wow_vir_admin_dashboard/core/presentation/widgets/text_fields/form_controller_text_field.dart'; import 'package:wow_vir_admin_dashboard/dependencies.dart'; import 'package:wow_vir_admin_dashboard/features/vendors/controllers/vendor_details_controller.dart';

@RoutePage() class VendorDetailsPage extends StatefulWidget {

const VendorDetailsPage({Key? key, }) : super(key: key);

@override State createState() => _VendorDetailsPageState(); } class _VendorDetailsPageState extends State {

late VendorDetailsController controller;

@override void initState() { controller = getIt(param1:null); super.initState(); }

@override Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar( context: context, barTitle: 'Vendor details', ), body: Form( key: controller.formKey, child: SingleChildScrollView( padding: EdgeInsets.all( 32.r), child: Column( children: [

            Row(
              children: [
                Expanded(
                  child: FormControllerTextField(title: 'Name',
                      validator: IsNotEmptyValidator(),
                      fieldIndex: 0,
                      formController: controller,
                      fieldKey: 'name', errorKey: 'name'),
                ),
                CustomSizedBox(width: 32,),
                Expanded(
                  child: FormControllerTextField(title: 'Name',
                      validator: IsNotEmptyValidator(),
                      fieldIndex: 1,
                      formController: controller,
                      fieldKey: 'name', errorKey: 'name'),
                ),

              ],
            ),

          ],
        ),
      ),
    ),

    bottomNavigationBar:  Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Observer(
          builder: (_) {
            print('rebuild ${controller.isLoading}');
        return MainButton(title: 'Submit', isLoading: controller.isLoading,
                onPressed: controller.submitForm);
          }
        ),
        CustomSizedBox(height: 16,),
      ],
    ),
  );

}

@override void dispose() { controller.dispose(); super.dispose(); } } `

it prints that isLoading=false and in the controller isLoading =true ! the same code would work perfectly with mobile apps