I am creating a custom button widget MyButton
by composing ElevatedButton
that will contains a Text
widget as Child. It has a boolean isLoading
property that will be used to configure the type of Button to build. When isLoading = true
it will return a grey loading button having width same as the width of text provided prior to this loading button build. My question is how can I get the width
of child Text
widget before the build method is called or invoked again by setState, so that I can use that width
to create the loading button of same size?
Loading Button will always be called after default Button has been built with child Text
widget. (i.e. isLoading
can be true only when widget is rebuilding not initially).
Here's the code:
class MyButton extends StatefulWidget {
final String text;
final bool isLoading;
final VoidCallback onPressed;
MyButton({Key key, @required this.text, @required this.isLoading, this.onPressed}) : super(key: key);
@override
State<StatefulWidget> createState() => _MyButtonState();
}
class _MyButtonState extends State<MyButton> {
double _width; // Width of the [Text] child widget that will be used to get the [Container] of same width for loading button
@override
void initState() {
super.initState();
_width = 150;
}
@override
void didUpdateWidget(covariant MyButton oldWidget) {
super.didUpdateWidget(oldWidget);
/*
Calculate the width....
*/
}
@override
Widget build(BuildContext context) {
// I want to get the width of this child
Widget child = Text(
widget.text,
style: TextStyle(color: Colors.black),
);
if (widget.isLoading) {
child = Container(
height: 60,
width: _width, // this should be same as [Text] width
color: Colors.grey,
);
}
return ElevatedButton(
child: child,
onPressed: !widget.isLoading ? widget.onPressed : null,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
!widget.isLoading ? Colors.red : Colors.grey,
),
),
);
}
}
Here is the demo of what I want to do but size of button when isLoading = true
is absolute for now.
I have checked other posts and questions but could not find useful way to solve my usecase. Using GlobalKey
is a solution but is not the recommended way and using RenderObject
I was not able to solve this as in that case we can only get the intrinsic heights and widths of the parent.
I think there might be some other way to do this other than using GlobalKey and RenderObject.
CodePudding user response:
Rather than creating a Container for that I would prefer you just hide the text so that size of button will be same as previous. Try below code :
Widget build(BuildContext context) {
return ElevatedButton(
child: Opacity(
opacity: !widget.isLoading ? 1 : 0,
child: Text(
widget.text,
style: TextStyle(color: Colors.black),
),
),
onPressed: !widget.isLoading ? widget.onPressed : null,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
!widget.isLoading ? Colors.red : Colors.grey,
),
),
);
}
CodePudding user response:
We can use the add postFrameCallback
to WidgetBinding instance to get the size of the widget from the context inside postFrameCallback
.
NOTE: This may not be the efficient appraoch.
Here's the code that worked in my case.
@override
void initState() {
super.initState();
getWidth();
_width = _width ?? 150;
}
void getWidth() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (context?.size is Size) {
_width = context.size.width;
print(context.size.toString());
}
});
}
@override
void didUpdateWidget(covariant MyButton oldWidget) {
getWidth();
super.didUpdateWidget(oldWidget);
}