content: import 'package:flutter/gestures.dart';
  import 'package:flutter/material.dart';
  import 'package:flutter/physics.dart';
  import 'dart:math' as math;
  import 'package:flutter/foundation.dart' show precisionErrorTolerance;
  
  
  class CustomTabBarView extends StatefulWidget {
    const CustomTabBarView({
      Key key,
      @required this.children,
      @required this.tabController,
      @required this.pageController,
      this.physics,
      this.dragStartBehavior = DragStartBehavior.start,
    })  : assert(children != null),
          assert(dragStartBehavior != null),
          super(key: key);
  
    final TabController tabController;
    final PageController pageController;
    final List<Widget> children;
    final ScrollPhysics physics;
    final DragStartBehavior dragStartBehavior;
  
    @override
    _CustomTabBarViewState createState() => _CustomTabBarViewState();
  }
  
  class _CustomTabBarViewState extends State<CustomTabBarView> {
    @override
    void dispose() {
      super.dispose();
      widget.tabController.dispose();
      widget.pageController.dispose();
    }
  
    @override
    Widget build(BuildContext context) {
      return PageView.builder(
        dragStartBehavior: widget.dragStartBehavior,
        physics: widget.physics,
        controller: widget.pageController,
        onPageChanged: (index) {
          widget.tabController.animateTo(index);
        },
        itemBuilder: (context, index) {
          return widget.children[index];
        },
        itemCount: widget.children.length,
      );
    }
  }
  
  
  //下面所有代码用于调整滑动速度问题，暂未使用
  class CustomPageScrollPhysics extends PageScrollPhysics {
    
    @override
    Simulation createBallisticSimulation(
        ScrollMetrics position, double velocity) {
      if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
          (velocity >= 0.0 && position.pixels >= position.maxScrollExtent))
        return super.createBallisticSimulation(position, velocity);
      final Tolerance tolerance = this.tolerance;
      final double target = _getTargetPixels(position, tolerance, velocity);
      if (target != position.pixels)
        return ScrollSpringSimulation(SpringDescription(
          mass: 8,
          stiffness: 150,
          damping: 15,
        ), position.pixels, target, velocity,
            tolerance: tolerance);
      return null;
    }
    double _getTargetPixels(ScrollMetrics position, Tolerance tolerance, double velocity) {
      double page = _getPage(position);
      if (velocity < -tolerance.velocity)
        page -= 0.5;
      else if (velocity > tolerance.velocity)
        page += 0.5;
      return _getPixels(position, page.roundToDouble());
    }
    double _getPage(ScrollMetrics position) {
      if (position is _PagePosition)
        return position.page;
      return position.pixels / position.viewportDimension;
    }
  
    double _getPixels(ScrollMetrics position, double page) {
      if (position is _PagePosition)
        return position.getPixelsFromPage(page);
      return page * position.viewportDimension;
    }
  
    @override
    bool get allowImplicitScrolling => false;
  }
  
  // ignore: unused_element
  class _PagePosition extends ScrollPositionWithSingleContext implements PageMetrics {
    _PagePosition({
      ScrollPhysics physics,
      ScrollContext context,
      this.initialPage = 0,
      bool keepPage = true,
      double viewportFraction = 1.0,
      ScrollPosition oldPosition,
    }) : assert(initialPage != null),
         assert(keepPage != null),
         assert(viewportFraction != null),
         assert(viewportFraction > 0.0),
         _viewportFraction = viewportFraction,
         _pageToUseOnStartup = initialPage.toDouble(),
         super(
           physics: physics,
           context: context,
           initialPixels: null,
           keepScrollOffset: keepPage,
           oldPosition: oldPosition,
         );
  
    final int initialPage;
    double _pageToUseOnStartup;
  
    @override
    double get viewportFraction => _viewportFraction;
    double _viewportFraction;
    set viewportFraction(double value) {
      if (_viewportFraction == value)
        return;
      final double oldPage = page;
      _viewportFraction = value;
      if (oldPage != null)
        forcePixels(getPixelsFromPage(oldPage));
    }
  
    // The amount of offset that will be added to [minScrollExtent] and subtracted
    // from [maxScrollExtent], such that every page will properly snap to the center
    // of the viewport when viewportFraction is greater than 1.
    //
    // The value is 0 if viewportFraction is less than or equal to 1, larger than 0
    // otherwise.
    double get _initialPageOffset => math.max(0, viewportDimension * (viewportFraction - 1) / 2);
  
    double getPageFromPixels(double pixels, double viewportDimension) {
      final double actual = math.max(0.0, pixels - _initialPageOffset) / math.max(1.0, viewportDimension * viewportFraction);
      final double round = actual.roundToDouble();
      if ((actual - round).abs() < precisionErrorTolerance) {
        return round;
      }
      return actual;
    }
  
    double getPixelsFromPage(double page) {
      return page * viewportDimension * viewportFraction + _initialPageOffset;
    }
  
    @override
    double get page {
      assert(
        pixels == null || (minScrollExtent != null && maxScrollExtent != null),
        'Page value is only available after content dimensions are established.',
      );
      return pixels == null ? null : getPageFromPixels(pixels.clamp(minScrollExtent, maxScrollExtent) as double, viewportDimension);
    }
  
    @override
    void saveScrollOffset() {
      PageStorage.of(context.storageContext)?.writeState(context.storageContext, getPageFromPixels(pixels, viewportDimension));
    }
  
    @override
    void restoreScrollOffset() {
      if (pixels == null) {
        final double value = PageStorage.of(context.storageContext)?.readState(context.storageContext) as double;
        if (value != null)
          _pageToUseOnStartup = value;
      }
    }
  
    @override
    bool applyViewportDimension(double viewportDimension) {
      final double oldViewportDimensions = this.viewportDimension;
      final bool result = super.applyViewportDimension(viewportDimension);
      final double oldPixels = pixels;
      final double page = (oldPixels == null || oldViewportDimensions == 0.0) ? _pageToUseOnStartup : getPageFromPixels(oldPixels, oldViewportDimensions);
      final double newPixels = getPixelsFromPage(page);
  
      if (newPixels != oldPixels) {
        correctPixels(newPixels);
        return false;
      }
      return result;
    }
  
    @override
    bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
      final double newMinScrollExtent = minScrollExtent + _initialPageOffset;
      return super.applyContentDimensions(
        newMinScrollExtent,
        math.max(newMinScrollExtent, maxScrollExtent - _initialPageOffset),
      );
    }
  
    @override
    PageMetrics copyWith({
      double minScrollExtent,
      double maxScrollExtent,
      double pixels,
      double viewportDimension,
      AxisDirection axisDirection,
      double viewportFraction,
    }) {
      return PageMetrics(
        minScrollExtent: minScrollExtent ?? this.minScrollExtent,
        maxScrollExtent: maxScrollExtent ?? this.maxScrollExtent,
        pixels: pixels ?? this.pixels,
        viewportDimension: viewportDimension ?? this.viewportDimension,
        axisDirection: axisDirection ?? this.axisDirection,
        viewportFraction: viewportFraction ?? this.viewportFraction,
      );
    }
  }
  