Reputation: 313
While flutter is growing, there are much recommendations and "ways how to". I am not sure what is correct as i am really new to the framework.
Say, i have the following code (hardly reduced):
@override
Widget build(BuildContext context) {
return GestureDetector(
// heights are needed here for animation values
child: Stack(
children: <Widget>[
Positioned(
left: 0,
right: 0,
bottom: value,
child: Column( // overall height
children: <Widget>[
Container(height: 60), // 1
Container(height: 150), // 2
Container(height: 200),
],
),
),
],
));
}
In the GestureDetector
i am animating some widget actions, depending on different heights of it`s children.
The height
of children //1
and //2
(and third) will be set hardcoded while developing, but, because there are several such elements and custom widgets, i want to get them automatically. Later on, this is better maintenable all in all, because mostly every widget anywhere has some animations.
So, i can use GlobalKey
, or maybe BoxConstraints
, but - all in all - what is the cheapest way to do that if building as a custom widget?
To close the question: in GestureDetector
i am able to rebuild after the initial layout is done and after every rebuild.
I am just not sure about which way is safe and has minimum costs (having those widgets several times in the code).
About //1
and //2
: //1
is the initial animation begin:
value, //2
is a value in the middle to stop, // overall height
is animation end:
value. Just to explain.
At least: i am using states_rebuilder
inside all of that.
Upvotes: 5
Views: 4102
Reputation: 1014
Inspired by the accepted answer of @Thang Mai I made a similar stateless solution:
class ChildSizeNotifier extends StatefulWidget {
final Widget Function(BuildContext context, Size size, Widget child) builder;
final Widget child;
ChildSizeNotifier({
Key? key,
required this.builder,
this.child,
}) : super(key: key) {}
@override
State<ChildSizeNotifier> createState() => _ChildSizeNotifierState();
}
class _ChildSizeNotifierState extends State<ChildSizeNotifier> {
final ValueNotifier<Size> _notifier = ValueNotifier(const Size(0, 0));
@override
void dispose() {
_notifier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback(
(_) {
notifier.value = (context.findRenderObject() as RenderBox).size;
},
);
return ValueListenableBuilder(
valueListenable: notifier,
builder: builder,
child: child,
);
}
}
Use it like this
ChildSizeNotifier(
builder: (context, size, child) {
// size is the size of the text
return Text(size.height > 50 ? 'big' : 'small');
},
)
Upvotes: 2
Reputation: 1061
We just can get the size of a widget after it's rendered unless it is a constant. We can use ValueNotifier to send the size of the child after it rendered.
Here is the completed example for it.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ValueNotifier<Size> _size = ValueNotifier<Size>(Size(0, 0));
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("example")
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ValueListenableBuilder(
builder: (BuildContext context, Size value, Widget child) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('Height of the child: ${value.height}'),
Text('Width of the child: ${value.width}'),
SizeTrackingWidget(
child: Container(
height: MediaQuery.of(context).size.width*0.4,
width: MediaQuery.of(context).size.width*0.3,
color: Colors.red
),
sizeValueNotifier: _size,
)
],
);
},
valueListenable: _size,
child: null,
)
],
),
),
);
}
}
class SizeTrackingWidget extends StatefulWidget {
Widget child;
ValueNotifier<Size> sizeValueNotifier;
SizeTrackingWidget({Key key, @required this.sizeValueNotifier, @required this.child}) : super(key: key);
@override
_SizeTrackingWidgetState createState() => _SizeTrackingWidgetState();
}
class _SizeTrackingWidgetState extends State<SizeTrackingWidget> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
_getSize();
});
}
_getSize() {
RenderBox renderBox = context.findRenderObject();
widget.sizeValueNotifier.value = renderBox.size;
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
Upvotes: 4