From 69882d9647e03ec4a9189ee1b0351abae508e938 Mon Sep 17 00:00:00 2001 From: Bogdan Lukin Date: Thu, 4 Feb 2021 06:06:05 +0700 Subject: [PATCH] Fix/ValueListenableBuilder rebuilds (#72707) --- .../src/widgets/value_listenable_builder.dart | 4 +- .../value_listenable_builder_test.dart | 44 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/widgets/value_listenable_builder.dart b/packages/flutter/lib/src/widgets/value_listenable_builder.dart index 1d9d32db9d8..d3b99e945d2 100644 --- a/packages/flutter/lib/src/widgets/value_listenable_builder.dart +++ b/packages/flutter/lib/src/widgets/value_listenable_builder.dart @@ -179,7 +179,9 @@ class _ValueListenableBuilderState extends State> { } void _valueChanged() { - setState(() { value = widget.valueListenable.value; }); + if (value != widget.valueListenable.value) { + setState(() { value = widget.valueListenable.value; }); + } } @override diff --git a/packages/flutter/test/widgets/value_listenable_builder_test.dart b/packages/flutter/test/widgets/value_listenable_builder_test.dart index e90658a57c8..f3b9d53a0df 100644 --- a/packages/flutter/test/widgets/value_listenable_builder_test.dart +++ b/packages/flutter/test/widgets/value_listenable_builder_test.dart @@ -9,6 +9,7 @@ import 'package:flutter/widgets.dart'; void main() { late SpyStringValueNotifier valueListenable; late Widget textBuilderUnderTest; + late int rebuildCount; Widget builderForValueListenable( ValueListenable valueListenable, @@ -18,6 +19,8 @@ void main() { child: ValueListenableBuilder( valueListenable: valueListenable, builder: (BuildContext context, String? value, Widget? child) { + rebuildCount += 1; + if (value == null) return const Placeholder(); return Text(value); @@ -29,6 +32,7 @@ void main() { setUp(() { valueListenable = SpyStringValueNotifier(null); textBuilderUnderTest = builderForValueListenable(valueListenable); + rebuildCount = 0; }); testWidgets('Null value is ok', (WidgetTester tester) async { @@ -58,6 +62,46 @@ void main() { expect(find.text('Dinesh'), findsOneWidget); }); + testWidgets('Widget does not rebuilds if value is the same', (WidgetTester tester) async { + const Duration duration = Duration(milliseconds: 100); + final AnimationController controller = AnimationController( + vsync: const TestVSync(), + duration: duration, + )..value = 0; + final Animation animation = TweenSequence(>[ + TweenSequenceItem(tween: ConstantTween('Gilfoyle'), weight: 1.0), + TweenSequenceItem(tween: ConstantTween('Dinesh'), weight: 1.0), + ]).animate(controller); + + final Finder finder1 = find.text('Gilfoyle'); + final Finder finder2 = find.text('Dinesh'); + + await tester.pumpWidget(builderForValueListenable(animation)); + + await tester.pump(); + expect(finder1, findsOneWidget); + expect(finder2, findsNothing); + expect(rebuildCount, equals(1)); + + controller.value = 0.3; + await tester.pump(); + expect(finder1, findsOneWidget); + expect(finder2, findsNothing); + expect(rebuildCount, equals(1)); + + controller.animateTo(0.6); + await tester.pumpAndSettle(duration); + expect(finder1, findsNothing); + expect(finder2, findsOneWidget); + expect(rebuildCount, equals(2)); + + controller.forward(); + await tester.pumpAndSettle(duration); + expect(finder1, findsNothing); + expect(finder2, findsOneWidget); + expect(rebuildCount, equals(2)); + }); + testWidgets('Can change listenable', (WidgetTester tester) async { await tester.pumpWidget(textBuilderUnderTest);