diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 2b001a91f29..6cb60b37791 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -2219,7 +2219,7 @@ class TabPageSelectorIndicator extends StatelessWidget { /// /// If a [TabController] is not provided, then there must be a /// [DefaultTabController] ancestor. -class TabPageSelector extends StatefulWidget { +class TabPageSelector extends StatelessWidget { /// Creates a compact widget that indicates which tab has been selected. const TabPageSelector({ super.key, @@ -2256,73 +2256,6 @@ class TabPageSelector extends StatefulWidget { /// Defaults to [BorderStyle.solid] if value is not specified. final BorderStyle? borderStyle; - @override - State createState() => _TabPageSelectorState(); -} - -class _TabPageSelectorState extends State { - TabController? _previousTabController; - TabController get _tabController { - final TabController? tabController = widget.controller ?? DefaultTabController.maybeOf(context); - assert(() { - if (tabController == null) { - throw FlutterError( - 'No TabController for $runtimeType.\n' - 'When creating a $runtimeType, you must either provide an explicit TabController ' - 'using the "controller" property, or you must ensure that there is a ' - 'DefaultTabController above the $runtimeType.\n' - 'In this case, there was neither an explicit controller nor a default controller.', - ); - } - return true; - }()); - return tabController!; - } - - CurvedAnimation? _animation; - - @override - void initState() { - super.initState(); - _setAnimation(); - } - - @override - void didUpdateWidget (TabPageSelector oldWidget) { - super.didUpdateWidget(oldWidget); - if (_previousTabController?.animation != _tabController.animation) { - _setAnimation(); - } - if (_previousTabController != _tabController) { - _previousTabController = _tabController; - } - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - if (_previousTabController?.animation != _tabController.animation) { - _setAnimation(); - } - if (_previousTabController != _tabController) { - _previousTabController = _tabController; - } - } - - void _setAnimation() { - _animation?.dispose(); - _animation = CurvedAnimation( - parent: _tabController.animation!, - curve: Curves.fastOutSlowIn, - ); - } - - @override - void dispose() { - _animation?.dispose(); - super.dispose(); - } - Widget _buildTabIndicator( int tabIndex, TabController tabController, @@ -2357,27 +2290,44 @@ class _TabPageSelectorState extends State { return TabPageSelectorIndicator( backgroundColor: background, borderColor: selectedColorTween.end!, - size: widget.indicatorSize, - borderStyle: widget.borderStyle ?? BorderStyle.solid, + size: indicatorSize, + borderStyle: borderStyle ?? BorderStyle.solid, ); } @override Widget build(BuildContext context) { - final Color fixColor = widget.color ?? Colors.transparent; - final Color fixSelectedColor = widget.selectedColor ?? Theme.of(context).colorScheme.secondary; + final Color fixColor = color ?? Colors.transparent; + final Color fixSelectedColor = selectedColor ?? Theme.of(context).colorScheme.secondary; final ColorTween selectedColorTween = ColorTween(begin: fixColor, end: fixSelectedColor); final ColorTween previousColorTween = ColorTween(begin: fixSelectedColor, end: fixColor); + final TabController? tabController = controller ?? DefaultTabController.maybeOf(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context); + assert(() { + if (tabController == null) { + throw FlutterError( + 'No TabController for $runtimeType.\n' + 'When creating a $runtimeType, you must either provide an explicit TabController ' + 'using the "controller" property, or you must ensure that there is a ' + 'DefaultTabController above the $runtimeType.\n' + 'In this case, there was neither an explicit controller nor a default controller.', + ); + } + return true; + }()); + final Animation animation = CurvedAnimation( + parent: tabController!.animation!, + curve: Curves.fastOutSlowIn, + ); return AnimatedBuilder( - animation: _animation!, + animation: animation, builder: (BuildContext context, Widget? child) { return Semantics( - label: localizations.tabLabel(tabIndex: _tabController.index + 1, tabCount: _tabController.length), + label: localizations.tabLabel(tabIndex: tabController.index + 1, tabCount: tabController.length), child: Row( mainAxisSize: MainAxisSize.min, - children: List.generate(_tabController.length, (int tabIndex) { - return _buildTabIndicator(tabIndex, _tabController, selectedColorTween, previousColorTween); + children: List.generate(tabController.length, (int tabIndex) { + return _buildTabIndicator(tabIndex, tabController, selectedColorTween, previousColorTween); }).toList(), ), ); diff --git a/packages/flutter/test/material/page_selector_test.dart b/packages/flutter/test/material/page_selector_test.dart index 9e8721d3926..8e988eda443 100644 --- a/packages/flutter/test/material/page_selector_test.dart +++ b/packages/flutter/test/material/page_selector_test.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; const Color kSelectedColor = Color(0xFF00FF00); const Color kUnselectedColor = Colors.transparent; @@ -87,10 +86,7 @@ void main() { expect(indicatorColors(tester), const [kUnselectedColor, kUnselectedColor, kSelectedColor]); }); - testWidgets('PageSelector responds correctly to TabController.animateTo()', - // TODO(polina-c): remove when fixed https://github.com/flutter/flutter/issues/145600 [leak-tracking-opt-in] - experimentalLeakTesting: LeakTesting.settings.withTracked(classes: const ['CurvedAnimation']), - (WidgetTester tester) async { + testWidgets('PageSelector responds correctly to TabController.animateTo()', (WidgetTester tester) async { final TabController tabController = TabController( vsync: const TestVSync(), length: 3,