flutter TabBarView BUG无法无动画跳转 修复

siman 1 小时前 5 0

TabBarView BUG 无法无动画跳转 我的需求是在个别时候需要无动画跳转发现TabBar可以  TabBarView 却不能;下面是解决方法



可以修改TabBarView的 _warpToCurrentIndex方法

  1.   void _warpToCurrentIndex() {
  2.     if (!mounted || _pageController!.page == _currentIndex!.toDouble()) {
  3.       return;
  4.     }

  5.     // 关键点:如果 animation.value 已经等于 index,说明是 duration: Duration.zero    瞬间跳转
  6.     if (_controller!.animation!.value == _controller!.index.toDouble()) {
  7.       _jumpToPage(_currentIndex!);
  8.       if (mounted) {
  9.         setState(() {
  10.           _updateChildren();
  11.         });
  12.       }
  13.       return;
  14.     }

  15.     final bool adjacentDestination = (_currentIndex! - _controller!.previousIndex).abs() == 1;
  16.     if (adjacentDestination) {
  17.       _warpToAdjacentTab(_controller!.animationDuration);
  18.     } else {
  19.       _warpToNonAdjacentTab(_controller!.animationDuration);
  20.     }
  21.   }
复制代码


这样就可以了 abController.index=1;  或者  tabController.animateTo(duration: Duration.zero,1);  都可以瞬间跳转其他动画保持不变

复制可用代码

  1. import 'package:flutter/foundation.dart';
  2. import 'package:flutter/gestures.dart' show DragStartBehavior;
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/rendering.dart';






  5. class NewTabBarView extends StatefulWidget {
  6.   /// Creates a page view with one child per tab.
  7.   ///
  8.   /// The length of [children] must be the same as the [controller]'s length.
  9.   const NewTabBarView({
  10.     super.key,
  11.     required this.children,
  12.     this.controller,
  13.     this.physics,
  14.     this.dragStartBehavior = DragStartBehavior.start,
  15.     this.viewportFraction = 1.0,
  16.     this.clipBehavior = Clip.hardEdge,
  17.   });

  18.   /// This widget's selection and animation state.
  19.   ///
  20.   /// If [TabController] is not provided, then the value of [DefaultTabController.of]
  21.   /// will be used.
  22.   final TabController? controller;

  23.   /// One widget per tab.
  24.   ///
  25.   /// Its length must match the length of the [TabBar.tabs]
  26.   /// list, as well as the [controller]'s [TabController.length].
  27.   final List<Widget> children;

  28.   /// How the page view should respond to user input.
  29.   ///
  30.   /// For example, determines how the page view continues to animate after the
  31.   /// user stops dragging the page view.
  32.   ///
  33.   /// The physics are modified to snap to page boundaries using
  34.   /// [PageScrollPhysics] prior to being used.
  35.   ///
  36.   /// Defaults to matching platform conventions.
  37.   final ScrollPhysics? physics;

  38.   /// {@macro flutter.widgets.scrollable.dragStartBehavior}
  39.   final DragStartBehavior dragStartBehavior;

  40.   /// {@macro flutter.widgets.pageview.viewportFraction}
  41.   final double viewportFraction;

  42.   /// {@macro flutter.material.Material.clipBehavior}
  43.   ///
  44.   /// Defaults to [Clip.hardEdge].
  45.   final Clip clipBehavior;

  46.   @override
  47.   State<NewTabBarView> createState() => _NewTabBarViewState();
  48. }

  49. class _NewTabBarViewState extends State<NewTabBarView> {
  50.   TabController? _controller;
  51.   PageController? _pageController;
  52.   late List<Widget> _childrenWithKey;
  53.   int? _currentIndex;
  54.   int _warpUnderwayCount = 0;
  55.   int _scrollUnderwayCount = 0;
  56.   bool _debugHasScheduledValidChildrenCountCheck = false;

  57.   // If the TabBarView is rebuilt with a new tab controller, the caller should
  58.   // dispose the old one. In that case the old controller's animation will be
  59.   // null and should not be accessed.
  60.   bool get _controllerIsValid => _controller?.animation != null;

  61.   void _updateTabController() {
  62.     final TabController? newController = widget.controller ?? DefaultTabController.maybeOf(context);
  63.     assert(() {
  64.       if (newController == null) {
  65.         throw FlutterError(
  66.           'No TabController for ${widget.runtimeType}.\n'
  67.               'When creating a ${widget.runtimeType}, you must either provide an explicit '
  68.               'TabController using the "controller" property, or you must ensure that there '
  69.               'is a DefaultTabController above the ${widget.runtimeType}.\n'
  70.               'In this case, there was neither an explicit controller nor a default controller.',
  71.         );
  72.       }
  73.       return true;
  74.     }());

  75.     if (newController == _controller) {
  76.       return;
  77.     }

  78.     if (_controllerIsValid) {
  79.       _controller!.animation!.removeListener(_handleTabControllerAnimationTick);
  80.     }
  81.     _controller = newController;
  82.     if (_controller != null) {
  83.       _controller!.animation!.addListener(_handleTabControllerAnimationTick);
  84.     }
  85.   }

  86.   void _jumpToPage(int page) {
  87.     _warpUnderwayCount += 1;
  88.     _pageController!.jumpToPage(page);
  89.     _warpUnderwayCount -= 1;
  90.   }

  91.   Future<void> _animateToPage(int page, {required Duration duration, required Curve curve}) async {
  92.     _warpUnderwayCount += 1;
  93.     await _pageController!.animateToPage(page, duration: duration, curve: curve);
  94.     _warpUnderwayCount -= 1;
  95.   }





  96.   @override
  97.   void initState() {
  98.     super.initState();
  99.     _updateChildren();
  100.   }

  101.   @override
  102.   void didChangeDependencies() {
  103.     super.didChangeDependencies();
  104.     _updateTabController();
  105.     _currentIndex = _controller!.index;
  106.     if (_pageController == null) {
  107.       _pageController = PageController(
  108.         initialPage: _currentIndex!,
  109.         viewportFraction: widget.viewportFraction,
  110.       );
  111.     } else {
  112.       _pageController!.jumpToPage(_currentIndex!);
  113.     }
  114.   }

  115.   @override
  116.   void didUpdateWidget(NewTabBarView oldWidget) {
  117.     super.didUpdateWidget(oldWidget);
  118.     if (widget.controller != oldWidget.controller) {
  119.       _updateTabController();
  120.       _currentIndex = _controller!.index;
  121.       _jumpToPage(_currentIndex!);
  122.     }
  123.     if (widget.viewportFraction != oldWidget.viewportFraction) {
  124.       _pageController?.dispose();
  125.       _pageController = PageController(
  126.         initialPage: _currentIndex!,
  127.         viewportFraction: widget.viewportFraction,
  128.       );
  129.     }
  130.     // While a warp is under way, we stop updating the tab page contents.
  131.     // This is tracked in https://github.com/flutter/flutter/issues/31269.
  132.     if (widget.children != oldWidget.children && _warpUnderwayCount == 0) {
  133.       _updateChildren();
  134.     }
  135.   }

  136.   @override
  137.   void dispose() {
  138.     if (_controllerIsValid) {
  139.       _controller!.animation!.removeListener(_handleTabControllerAnimationTick);
  140.     }
  141.     _controller = null;
  142.     _pageController?.dispose();
  143.     // We don't own the _controller Animation, so it's not disposed here.
  144.     super.dispose();
  145.   }

  146.   void _updateChildren() {
  147.     _childrenWithKey = KeyedSubtree.ensureUniqueKeysForList(
  148.       widget.children.map<Widget>((Widget child) {
  149.         return Semantics(role: SemanticsRole.tabPanel, child: child);
  150.       }).toList(),
  151.     );
  152.   }

  153.   void _handleTabControllerAnimationTick() {
  154.     if (_scrollUnderwayCount > 0 || !_controller!.indexIsChanging) {
  155.       return;
  156.     } // This widget is driving the controller's animation.

  157.     if (_controller!.index != _currentIndex) {
  158.       _currentIndex = _controller!.index;
  159.       _warpToCurrentIndex();
  160.     }
  161.   }





  162.   void _warpToCurrentIndex() {
  163.     if (!mounted || _pageController!.page == _currentIndex!.toDouble()) {
  164.       return;
  165.     }

  166.     // 关键点:如果 animation.value 已经等于 index,说明是 duration: Duration.zero 的瞬间跳转
  167.     if (_controller!.animation!.value == _controller!.index.toDouble()) {
  168.       _jumpToPage(_currentIndex!);
  169.       if (mounted) {
  170.         setState(() {
  171.           _updateChildren();
  172.         });
  173.       }
  174.       return;
  175.     }

  176.     final bool adjacentDestination = (_currentIndex! - _controller!.previousIndex).abs() == 1;
  177.     if (adjacentDestination) {
  178.       _warpToAdjacentTab(_controller!.animationDuration);
  179.     } else {
  180.       _warpToNonAdjacentTab(_controller!.animationDuration);
  181.     }
  182.   }

  183.   Future<void> _warpToAdjacentTab(Duration duration) async {
  184.     if (duration == Duration.zero) {
  185.       _jumpToPage(_currentIndex!);
  186.     } else {
  187.       await _animateToPage(_currentIndex!, duration: duration, curve: Curves.ease);
  188.     }
  189.     if (mounted) {
  190.       setState(() {
  191.         _updateChildren();
  192.       });
  193.     }
  194.     return Future<void>.value();
  195.   }

  196.   Future<void> _warpToNonAdjacentTab(Duration duration) async {
  197.     final int previousIndex = _controller!.previousIndex;
  198.     assert((_currentIndex! - previousIndex).abs() > 1);

  199.     // initialPage defines which page is shown when starting the animation.
  200.     // This page is adjacent to the destination page.
  201.     final int initialPage = _currentIndex! > previousIndex
  202.         ? _currentIndex! - 1
  203.         : _currentIndex! + 1;

  204.     setState(() {
  205.       // Needed for `RenderSliverMultiBoxAdaptor.move` and kept alive children.
  206.       // For motivation, see https://github.com/flutter/flutter/pull/29188 and
  207.       // https://github.com/flutter/flutter/issues/27010#issuecomment-486475152.
  208.       _childrenWithKey = List<Widget>.of(_childrenWithKey, growable: false);
  209.       final Widget temp = _childrenWithKey[initialPage];
  210.       _childrenWithKey[initialPage] = _childrenWithKey[previousIndex];
  211.       _childrenWithKey[previousIndex] = temp;
  212.     });

  213.     // Make a first jump to the adjacent page.
  214.     _jumpToPage(initialPage);

  215.     // Jump or animate to the destination page.
  216.     if (duration == Duration.zero) {
  217.       _jumpToPage(_currentIndex!);
  218.     } else {
  219.       await _animateToPage(_currentIndex!, duration: duration, curve: Curves.ease);
  220.     }

  221.     if (mounted) {
  222.       setState(() {
  223.         _updateChildren();
  224.       });
  225.     }
  226.   }

  227.   void _syncControllerOffset() {
  228.     _controller!.offset = clampDouble(_pageController!.page! - _controller!.index, -1.0, 1.0);
  229.   }

  230.   // Called when the PageView scrolls
  231.   bool _handleScrollNotification(ScrollNotification notification) {
  232.     if (_warpUnderwayCount > 0 || _scrollUnderwayCount > 0) {
  233.       return false;
  234.     }

  235.     if (notification.depth != 0) {
  236.       return false;
  237.     }

  238.     if (!_controllerIsValid) {
  239.       return false;
  240.     }

  241.     _scrollUnderwayCount += 1;
  242.     final double page = _pageController!.page!;
  243.     if (notification is ScrollUpdateNotification && !_controller!.indexIsChanging) {
  244.       final bool pageChanged = (page - _controller!.index).abs() > 1.0;
  245.       if (pageChanged) {
  246.         _controller!.index = page.round();
  247.         _currentIndex = _controller!.index;
  248.       }
  249.       _syncControllerOffset();
  250.     } else if (notification is ScrollEndNotification) {
  251.       _controller!.index = page.round();
  252.       _currentIndex = _controller!.index;
  253.       if (!_controller!.indexIsChanging) {
  254.         _syncControllerOffset();
  255.       }
  256.     }
  257.     _scrollUnderwayCount -= 1;

  258.     return false;
  259.   }

  260.   bool _debugScheduleCheckHasValidChildrenCount() {
  261.     if (_debugHasScheduledValidChildrenCountCheck) {
  262.       return true;
  263.     }
  264.     WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
  265.       _debugHasScheduledValidChildrenCountCheck = false;
  266.       if (!mounted) {
  267.         return;
  268.       }
  269.       assert(() {
  270.         if (_controller!.length != widget.children.length) {
  271.           throw FlutterError(
  272.             "Controller's length property (${_controller!.length}) does not match the "
  273.                 "number of children (${widget.children.length}) present in TabBarView's children property.",
  274.           );
  275.         }
  276.         return true;
  277.       }());
  278.     }, debugLabel: 'TabBarView.validChildrenCountCheck');
  279.     _debugHasScheduledValidChildrenCountCheck = true;
  280.     return true;
  281.   }

  282.   @override
  283.   Widget build(BuildContext context) {
  284.     assert(_debugScheduleCheckHasValidChildrenCount());

  285.     return NotificationListener<ScrollNotification>(
  286.       onNotification: _handleScrollNotification,
  287.       child: PageView(
  288.         dragStartBehavior: widget.dragStartBehavior,
  289.         clipBehavior: widget.clipBehavior,
  290.         controller: _pageController,
  291.         physics: widget.physics == null
  292.             ? const PageScrollPhysics().applyTo(const ClampingScrollPhysics())
  293.             : const PageScrollPhysics().applyTo(widget.physics),
  294.         children: _childrenWithKey,
  295.       ),
  296.     );
  297.   }
  298. }
复制代码
评论
登录 后才能评论

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2026-8-28 23:31, Processed in 0.247414 second(s), 31 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表