Home > Enterprise >  In which method, should call state management methods of the class in Flutter bloc with rxdart?
In which method, should call state management methods of the class in Flutter bloc with rxdart?

Time:01-06

Firstly, I want to ask is from which method of Stateful widget should I call state management methods. I need to choice two place init() method or build(). I don't exactly know which method is the appropriate method to call state management methods. Let me try to explain with examples to understand of my question.

I use rxdart for dependency injection, used Stream and build with bloc pattern. Then used global scope instead of single scope. So I build another class that extend InheritedWidget and predefine each of the of(context) from the ancestor widget. Then called bloc methods (state management) from each build() method of their needed UI class (Stateful or stateless).

In here, my problem is when every time I called from build method, some of the function build again and again, unless I need to do. So I fix with another way, that is declare, initialized the bloc class and call respective bloc class function with object from init method of stateful class. That is work really. But some of the article said, don't should call from init method and only should call from build method. I am anxiety with that principles and calling from init method not work as global scope (Ex: that init stream not work for another widgets). How should I do with that? please tell or guide me with something.

Here is my code flow to better understanding. I show with count down example bloc that work like When count down become 0, the button will appear and press again that button count down again and work same process again.

That is state management bloc

class OtpLoginModuleBloc {

  bool isHideResendButton = true;

  final _buttonTimerController = PublishSubject<bool>();
  final _textTimerController = PublishSubject<int>();

  Stream<bool> get buttonTimerStream => _buttonTimerController.stream;
  Stream<int> get textTimerStream => _textTimerController.stream;


  void toAppearResendButtonCountown({required int second}) {
    var duration = const Duration(seconds: 1);
    Timer.periodic(
      duration,
      (Timer timer) {
        if (second == 0) {
          isHideResendButton = !isHideResendButton;
          _buttonTimerController.sink.add(isHideResendButton);
          timer.cancel();
        } else {
          second--;
        }
        _textTimerController.sink.add(second);
      },
    );
  }
}

Here is ancestor class

class _PreModuleState extends State<PreModule> {
  @override
  Widget build(BuildContext context) {
    return LoginModuleProvider(
      child: OtpLoginModuleProivder(
        child: MaterialApp(
          theme: themeData(),
          home: const Scaffold(
            body: SplashScreen(),
          ),
          onGenerateRoute: RouteGenerator.route,
        ),
      ),
    );
  }
}

Here is provider class for global scope

class OtpLoginModuleProivder extends InheritedWidget {
  final OtpLoginModuleBloc loginOtpModuleBloc;

  OtpLoginModuleProivder({Key? key, required Widget child})
      : loginOtpModuleBloc = OtpLoginModuleBloc(),
        super(key: key, child: child);

  @override
  bool updateShouldNotify(OtpLoginModuleProivder oldWidget) => true;

  static OtpLoginModuleBloc of(context) {
    return (context.dependOnInheritedWidgetOfExactType<OtpLoginModuleProivder>()
            as OtpLoginModuleProivder)
        .loginOtpModuleBloc;
  }
}

And my problem is here ...

Should I call like that

final OtpLoginModuleBloc otpLoginModuleBloc = OtpLoginModuleBloc();

 @override
  void initState() {
    otpLoginModuleBloc.toAppearResendButtonCountown(second: otpCountingTime);
    super.initState();
  }

@override
Widget build(BuildContext context) {
    return Stack(
      children: [
        Column(
          children: [
            const OtpLoginHeader(),
            const SizedBox(
              height: margin50,
            ),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: margin100),
              child: buildPinCodeTextField(context, otpLoginModuleBloc),
            ),
            const SizedBox(
              height: margin30,
            ),
            StreamBuilder(
              initialData: otpCountingTime,
              stream: otpLoginModuleBloc.textTimerStream,
              builder: (context, AsyncSnapshot snapshot) {
                return Padding(
                  padding: const EdgeInsets.symmetric(horizontal: margin80),
                  child: snapshot.data == 0
                      ? sendOtpButton(otpLoginModuleBloc, otpCountingTime)
                      : Text('Request new OTP in ${snapshot.data}'),
                );
              },
            ),
          ],
        ),
      ],
    );
  }

OR

Widget build(BuildContext context) {
    final otpLoginModuleBloc = OtpLoginModuleProivder.of(context);
    otpLoginModuleBloc.toAppearResendButtonCountown(second: otpCountingTime);
    return Stack(
      children: [
        Column(
          children: [
            const OtpLoginHeader(),
            const SizedBox(
              height: margin50,
            ),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: margin100),
              child: buildPinCodeTextField(context, otpLoginModuleBloc),
            ),
            const SizedBox(
              height: margin30,
            ),
            StreamBuilder(
              initialData: otpCountingTime,
              stream: otpLoginModuleBloc.textTimerStream,
              builder: (context, AsyncSnapshot snapshot) {
                return Padding(
                  padding: const EdgeInsets.symmetric(horizontal: margin80),
                  child: snapshot.data == 0
                      ? sendOtpButton(otpLoginModuleBloc, otpCountingTime)
                      : Text('Request new OTP in ${snapshot.data}'),
                );
              },
            ),
          ],
        ),
      ],
    );
  }

That is my all of my question... Please help me and guide me ...

CodePudding user response:

Ofc you should go for the latter. use like this. This way, you can use _otpLoginModuleBloc within the class.

class _ExampleWidgetState extends State<ExampleWidget> {
  late OtpLoginModuleBloc _otpLoginModuleBloc

  @override
  Widget build(BuildContext context) {
    _otpLoginModuleBloc = OtpLoginModuleProivder.of(context);
    return Container();
  }
}

In addition, if you need to run the otpLoginModuleBloc.toAppearResendButtonCountown(second: otpCountingTime); only ONCE, then use below.

class ExampleWidget extends StatefulWidget {
  const ExampleWidget({super.key});

  @override
  State<ExampleWidget> createState() => _ExampleWidgetState();
}

class _ExampleWidgetState extends State<ExampleWidget> {
  late OtpLoginModuleBloc _otpLoginModuleBloc

  final _otpCountingTime = 1;

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _otpLoginModuleBloc.toAppearResendButtonCountown(second: _otpCountingTime);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    _otpLoginModuleBloc = OtpLoginModuleProivder.of(context);
    return Container();
  }
}

This should answer your questions. Thank you.

Oh and don't forget to add dispose method inside the OtpLoginModuleBloc that cancels Timer and closes both _buttonTimerController and _textTimerController in case you don't need the bloc anymore.

  • Related