I am using ValueListenableBuilder
to watch some values in several animated custom widgets. Those are using several values, based on their children sizes, to animate depending actions.
My problem is listening to more than one value. I have to nest them.
The following is a reduced example to explain:
class MyWidget extends StatelessWidget {
final ValueNotifier<double> _height1 = ValueNotifier<double>(40);
final ValueNotifier<double> _height2 = ValueNotifier<double>(120);
final ValueNotifier<double> _height3 = ValueNotifier<double>(200);
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
builder: (BuildContext context, double h1, Widget child) {
return ValueListenableBuilder(
builder: (BuildContext context, double h2, Widget child) {
return ValueListenableBuilder(
builder: (BuildContext context, double h3, Widget child) {
return GestureDetector(
// i am using h1, h2 and h3 here ...
child: Stack(
children: <Widget>[
Positioned(
left: 0,
right: 0,
bottom: value,
child: Column(
children: <Widget>[
Container(height: _height1.value),
Container(height: _height2.value),
Container(height: _height3.value),
],
),
),
],
),
);
},
valueListenable: _height3,
child: null);
},
valueListenable: _height2,
child: null,
);
},
valueListenable: _height1,
child: null,
);
}
}
https://github.com/flutter/flutter/issues/40906#issuecomment-533383128 give a short hint to Listenable.merge
, but I have no idea how to use this.
ValueNotifiers can hold data of type int, String, double, boolean and even you can use your own Datatype (using class). import 'package:flutter/material. dart';ValueNotifier<int> buttonClickedTimes =ValueNotifier(0);
ValueListenableBuilder will listen for changes to a value notifier and automatically rebuild its children when the value changes. If you have more complex data, create a custom notifier for your data classes by extending ValueNotifier.
ValueNotifier is a special type of class that extends Changenotifier, which can hold a single value and notifies the widgets which are listening to it whenever its holding value gets change. ChangeNotifier is a class that provides change notification to its listeners.
There's nothing built-in.
You could use Listenable.merge
. But it has flaws:
Instead, we can use composition: we can write a stateless widget that combines 2+ ValueListenableBuilders into one, and use that.
It'd be used this way:
ValueListenable<SomeValue> someValueListenable;
ValueListenable<AnotherValue> anotherValueListenable;
ValueListenableBuilder2<SomeValue, AnotherValue>(
someValueListenable,
anotherValueListenable,
builder: (context, someValue, anotherValue, child) {
return Text('$someValue $anotherValue');
},
child: ...,
);
Where the code of such ValueListenableBuilder2
is:
class ValueListenableBuilder2<A, B> extends StatelessWidget {
ValueListenableBuilder2(
this.first,
this.second, {
Key key,
this.builder,
this.child,
}) : super(key: key);
final ValueListenable<A> first;
final ValueListenable<B> second;
final Widget child;
final Widget Function(BuildContext context, A a, B b, Widget child) builder;
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<A>(
valueListenable: first,
builder: (_, a, __) {
return ValueListenableBuilder<B>(
valueListenable: second,
builder: (context, b, __) {
return builder(context, a, b, child);
},
);
},
);
}
}
UPDATE: null safety version:
class ValueListenableBuilder2<A, B> extends StatelessWidget {
const ValueListenableBuilder2({
required this.first,
required this.second,
Key? key,
required this.builder,
this.child,
}) : super(key: key);
final ValueListenable<A> first;
final ValueListenable<B> second;
final Widget? child;
final Widget Function(BuildContext context, A a, B b, Widget? child) builder;
@override
Widget build(BuildContext context) => ValueListenableBuilder<A>(
valueListenable: first,
builder: (_, a, __) {
return ValueListenableBuilder<B>(
valueListenable: second,
builder: (context, b, __) {
return builder(context, a, b, child);
},
);
},
);
}
You cannot use ValueListenableBuilder with Listenable.merge because merge returns only a Listenable and no ValueListenable and ValueListenableBuilder expects a Value Changenotifier. You can use AnimatedBuilder instead which despite its name is just a ListenableBuilder.
class MyWidget extends StatelessWidget {
final ValueNotifier<double> _height1 = ValueNotifier<double>(40);
final ValueNotifier<double> _height2 = ValueNotifier<double>(120);
final ValueNotifier<double> _height3 = ValueNotifier<double>(200);
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: Listenable.merge([_height1, _height2, _height3]),
builder: (BuildContext context, _) {
return GestureDetector(
// i am using h1, h2 and h3 here ...
child: Stack(
children: <Widget>[
Positioned(
left: 0,
right: 0,
bottom: value,
child: Column(
children: <Widget>[
Container(height: _height1.value),
Container(height: _height2.value),
Container(height: _height3.value),
],
),
),
],
),
);
},
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With