From 8b5cbca7651076c057fa163af923e62542ad88b4 Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Mon, 9 Jul 2018 16:47:12 -0700 Subject: [PATCH] Rebuilding BottomNavigationBar when items.length changes (#19179) --- .../src/material/bottom_navigation_bar.dart | 25 ++++++++++-- .../material/bottom_navigation_bar_test.dart | 38 +++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/packages/flutter/lib/src/material/bottom_navigation_bar.dart b/packages/flutter/lib/src/material/bottom_navigation_bar.dart index 3f7e1c9ec1..5eebd993f0 100644 --- a/packages/flutter/lib/src/material/bottom_navigation_bar.dart +++ b/packages/flutter/lib/src/material/bottom_navigation_bar.dart @@ -303,7 +303,7 @@ class _BottomNavigationTile extends StatelessWidget { } class _BottomNavigationBarState extends State with TickerProviderStateMixin { - List _controllers; + List _controllers = []; List _animations; // A queue of color splashes currently being animated. @@ -315,9 +315,13 @@ class _BottomNavigationBarState extends State with TickerPr static final Tween _flexTween = new Tween(begin: 1.0, end: 1.5); - @override - void initState() { - super.initState(); + void _resetState() { + for (AnimationController controller in _controllers) + controller.dispose(); + for (_Circle circle in _circles) + circle.dispose(); + _circles.clear(); + _controllers = new List.generate(widget.items.length, (int index) { return new AnimationController( duration: kThemeAnimationDuration, @@ -335,6 +339,12 @@ class _BottomNavigationBarState extends State with TickerPr _backgroundColor = widget.items[widget.currentIndex].backgroundColor; } + @override + void initState() { + super.initState(); + _resetState(); + } + void _rebuild() { setState(() { // Rebuilding when any of the controllers tick, i.e. when the items are @@ -385,6 +395,13 @@ class _BottomNavigationBarState extends State with TickerPr @override void didUpdateWidget(BottomNavigationBar oldWidget) { super.didUpdateWidget(oldWidget); + + // No animated segue if the length of the items list changes. + if (widget.items.length != oldWidget.items.length) { + _resetState(); + return; + } + if (widget.currentIndex != oldWidget.currentIndex) { switch (widget.type) { case BottomNavigationBarType.fixed: diff --git a/packages/flutter/test/material/bottom_navigation_bar_test.dart b/packages/flutter/test/material/bottom_navigation_bar_test.dart index a0422c1ec3..9348c5ed11 100644 --- a/packages/flutter/test/material/bottom_navigation_bar_test.dart +++ b/packages/flutter/test/material/bottom_navigation_bar_test.dart @@ -602,6 +602,44 @@ void main() { semantics.dispose(); }); + testWidgets('BottomNavigationBar handles items.length changes', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/10322 + + Widget buildFrame(int itemCount) { + return new MaterialApp( + home: new Scaffold( + bottomNavigationBar: new BottomNavigationBar( + type: BottomNavigationBarType.fixed, + currentIndex: 0, + items: new List.generate(itemCount, (int itemIndex) { + return new BottomNavigationBarItem( + icon: const Icon(Icons.android), + title: new Text('item $itemIndex'), + ); + }), + ), + ), + ); + } + + await tester.pumpWidget(buildFrame(3)); + expect(find.text('item 0'), findsOneWidget); + expect(find.text('item 1'), findsOneWidget); + expect(find.text('item 2'), findsOneWidget); + expect(find.text('item 3'), findsNothing); + + await tester.pumpWidget(buildFrame(4)); + expect(find.text('item 0'), findsOneWidget); + expect(find.text('item 1'), findsOneWidget); + expect(find.text('item 2'), findsOneWidget); + expect(find.text('item 3'), findsOneWidget); + + await tester.pumpWidget(buildFrame(2)); + expect(find.text('item 0'), findsOneWidget); + expect(find.text('item 1'), findsOneWidget); + expect(find.text('item 2'), findsNothing); + expect(find.text('item 3'), findsNothing); + }); } Widget boilerplate({ Widget bottomNavigationBar, @required TextDirection textDirection }) {