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.