Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ExtendedTabBar 在scrollDirection: Axis.vertical情况下indicator中 paint(Canvas canvas, Offset offset..) offset值异常 #28

Open
xietian0908 opened this issue May 29, 2023 · 17 comments

Comments

@xietian0908
Copy link

Version

extended_tabs: ^4.0.2

Platforms

Android

Device Model

galaxy tab A7 (Android 11)

flutter info

[!] Flutter (Channel unknown, 3.3.10, on Microsoft Windows [版本 10.0.19045.2965], locale zh-CN)
    ! Flutter version 3.3.10 on channel unknown at E:\flutter sdk\flutter
    ! Upstream repository unknown
    • Framework revision 135454af32 (6 months ago), 2022-12-15 07:36:55 -0800
    • Engine revision 3316dd8728
    • Dart version 2.18.6
    • DevTools version 2.15.0
    • Pub download mirror https://pub.flutter-io.cn
    • Flutter download mirror https://storage.flutter-io.cn

[√] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
    • Android SDK at C:\Users\Administrator\AppData\Local\Android\Sdk
    • Platform android-33, build-tools 33.0.2
    • ANDROID_HOME = C:\Users\Administrator\AppData\Local\Android\Sdk
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java      
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-b2043.56-9586694)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Users\Administrator\AppData\Local\Google\Chrome\Application\chrome.exe

[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.5.4)
    • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
    • Visual Studio Community 2022 version 17.5.33530.505
    • Windows 10 SDK version 10.0.22000.0

[√] Android Studio (version 2022.2)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-b2043.56-9586694)

[√] Connected device (4 available)
    • VRD W10 (mobile)  • RCJ6R20721000566 • android-arm64  • Android 10 (API 29)
    • Windows (desktop) • windows          • windows-x64    • Microsoft Windows [版本 10.0.19045.2965]
    • Chrome (web)      • chrome           • web-javascript • Google Chrome 112.0.5615.121
    • Edge (web)        • edge             • web-javascript • Microsoft Edge 113.0.1774.57

[√] HTTP Host Availability
    • All required HTTP hosts are available

How to reproduce?

30a5c109948d15a1788f9b3197b8a292.mp4

4939ed8c35baf1a90a903c16804e5fc

Logs

No response

Example code (optional)

No response

Contact

No response

@xietian0908
Copy link
Author

tabbar_v_square_indicator.zip
ExtendedTabBar(
// labelPadding: EdgeInsets.symmetric(horizontal: 50.w),
indicatorPadding: const EdgeInsets.all(0),
labelPadding: const EdgeInsets.only(bottom: 0),
isScrollable: true,
indicatorWeight: 0,
scrollDirection: Axis.vertical,
tabs: _tabs(read.airDevicesModelList),
indicator: TabbarVSquareIndicator(
length: read.airDevicesModelList.length,
),
);

@xietian0908
Copy link
Author

只有在快速滑动extendtabview 的时候点击extendtabbar才会出现,等待移动动画完成之后点击是没有问题的

@zmtzawqlp
Copy link
Member

zmtzawqlp commented May 29, 2023

  1. 错误信息呢?
  2. 提供可以运行的最小的例子

@xietian0908
Copy link
Author

xietian0908 commented May 29, 2023

  1. 错误信息呢?
  2. 提供可以运行的最小的例子
import 'package:extended_tabs/extended_tabs.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/src/widgets/placeholder.dart';

class ExtendedTabsBug extends StatefulWidget {
  const ExtendedTabsBug({super.key});

  @override
  State<ExtendedTabsBug> createState() => _ExtendedTabsBugState();
}

class _ExtendedTabsBugState extends State<ExtendedTabsBug> {
  int length = 16;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: DefaultTabController(
        length: length,
        child: Row(
          children: [
            ExtendedTabBar(
              // labelPadding: EdgeInsets.symmetric(horizontal: 50.w),
              indicatorPadding: const EdgeInsets.all(0),
              labelPadding: const EdgeInsets.only(bottom: 0),
              isScrollable: true,
              indicatorWeight: 0,
              scrollDirection: Axis.vertical,
              tabs: _tabs(),
              indicator: TabbarVSquareIndicator(
                length: length,
              ),
            ),
            Expanded(
              child: ExtendedTabBarView(
                scrollDirection: Axis.vertical,
                children: [
                  for (int i = 0; i < length; i++)
                    Container(
                      color: Colors.primaries[i],
                      alignment: Alignment.center,
                      child: Text('tabview $i'),
                    )
                ],
              ),
            )
          ],
        ),
      ),
    );
  }

  List<Widget> _tabs() {
    return List.generate(
      length,
      (index) => Container(
        height: 100,
        margin: const EdgeInsets.only(bottom: 16),
        alignment: Alignment.center,
        child: Text('tab $index'),
      ),
    );
  }
}

class TabbarVSquareIndicator extends Decoration {
  /// Height of the indicator. Defaults to 4
  final double height;
  final double width;

  /// topRight radius of the indicator, default to 5.
  final double topRightRadius;

  /// topLeft radius of the indicator, default to 5.
  final double topLeftRadius;

  /// bottomRight radius of the indicator, default to 0.
  final double bottomRightRadius;

  /// bottomLeft radius of the indicator, default to 0
  final double bottomLeftRadius;

  /// Color of the indicator, default set to [Colors.black]
  final Color color;

  /// Horizontal padding of the indicator, default set 0
  final double horizontalPadding;

  /// [PagingStyle] determines if the indicator should be fill or stroke, default to fill
  final PaintingStyle paintingStyle;

  /// StrokeWidth, used for [PaintingStyle.stroke], default set to 2
  final double strokeWidth;
  final int length;

  const TabbarVSquareIndicator({
    this.length = 0,
    this.height = 4,
    this.width = 10,
    this.topRightRadius = 2,
    this.topLeftRadius = 2,
    this.bottomRightRadius = 2,
    this.bottomLeftRadius = 2,
    this.color = Colors.red,
    this.horizontalPadding = 0,
    this.paintingStyle = PaintingStyle.fill,
    this.strokeWidth = 4,
  });

  @override
  CustomPainter createBoxPainter([VoidCallback? onChanged]) {
    return CustomPainter(
      this,
      onChanged,
      bottomLeftRadius: bottomLeftRadius,
      bottomRightRadius: bottomRightRadius,
      color: color,
      height: height,
      width: width,
      horizontalPadding: horizontalPadding,
      topLeftRadius: topLeftRadius,
      topRightRadius: topRightRadius,
      paintingStyle: paintingStyle,
      strokeWidth: strokeWidth,
      length: length,
    );
  }
}

class CustomPainter extends BoxPainter {
  final TabbarVSquareIndicator decoration;
  final double height;
  final double topRightRadius;
  final double topLeftRadius;
  final double bottomRightRadius;
  final double bottomLeftRadius;
  final Color color;
  final double horizontalPadding;
  final double strokeWidth;
  final PaintingStyle paintingStyle;
  final double width;
  final gradient = const LinearGradient(
    colors: [Color(0xff0C87F3), Color(0xff0961AE)],
    begin: Alignment.centerLeft,
    end: Alignment.centerRight,
  );
  final int length;

  CustomPainter(
    this.decoration,
    VoidCallback? onChanged, {
    required this.height,
    required this.topRightRadius,
    required this.topLeftRadius,
    required this.bottomRightRadius,
    required this.bottomLeftRadius,
    required this.color,
    required this.horizontalPadding,
    required this.paintingStyle,
    required this.strokeWidth,
    required this.width,
    required this.length,
  }) : super(onChanged);

  double yfun = 0;
  double yfun2 = 0;
  int index2 = 0;
  Path path = Path();

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
    Size mysize = Size(configuration.size!.width, configuration.size!.height - 16);
    Size mysizeReal = Size(configuration.size!.width, configuration.size!.height);
    radius = Size(configuration.size!.width, (configuration.size!.height - 16));

    // Offset myoffset = Offset(offset.dx, offset.dy);

    // final Rect rect = myoffset & mysize;
    final Paint paint = Paint()
      ..strokeCap = StrokeCap.round
      ..color = color
      ..style = paintingStyle
      ..strokeWidth = strokeWidth;

    int curIndex = (offset.dy / mysizeReal.height).round();
    if (axis == Axis.horizontal) {
      for (int i = 0; i < length; i++) {
        Rect rect = Rect.fromCenter(center: Offset(0, 0 + i * configuration.size!.width), width: radius.width, height: radius.height);
        RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(10));
        canvas.drawRRect(rrect, paint..color = const Color(0xff38424B));
      }
      curIndex = (offset.dx / mysizeReal.width).round();
      canvas.translate(mysize.width / 2, mysize.height / 2);
      // print('yfun index2:$index2 curIndex:$curIndex ${offset.dy} before:$before');
      if (yfun == 0) {
        yfun = mysizeReal.width;
      }
      if (curIndex != index2) {
        yfun2 = (curIndex) * mysizeReal.width;
        yfun = (curIndex - index2).abs() * mysizeReal.width;
        index2 = curIndex;
      }
      _canvasInitRectangle(
        canvas,
        offset.dx,
        yfun,
        paint: paint..color = Colors.green,
        path: path,
      );
    } else {
      canvas.translate(mysize.width / 2, mysize.height / 2);
      for (int i = 0; i < length; i++) {
        Rect rect = Rect.fromCenter(center: Offset(0, 0 + i * configuration.size!.height), width: radius.width, height: radius.height);
        RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(10));
        canvas.drawRRect(rrect, paint..color = const Color(0xff38424B));
      }
      curIndex = (offset.dy / mysizeReal.height).round();
      print('${offset.dy}');
      if (yfun == 0) {
        yfun = mysizeReal.height;
        before = (curIndex) * mysizeReal.height;
      }
      // if (!_isUsePosition(offset.dy, mysizeReal)) return;
      if (curIndex != index2) {
        yfun2 = (curIndex) * mysizeReal.height;
        yfun = (curIndex - index2).abs() * mysizeReal.height;
        index2 = curIndex;
      }
      _canvasInitRectangle(
        canvas,
        offset.dy,
        yfun,
        paint: paint..color = Colors.green,
        path: path,
      );
    }

    // print('=====y:${offset.dy} ${(offset.dy - yfun2).abs() / yfun} $yfun $yfun2');
    // if (flag) yfun = 0;
    // if (index2 != index.value) index2 = index.value;
    // path = Path()
    //   ..moveTo(0, y)
    //   ..relativeLineTo(mysize.width, 0)
    //   ..relativeLineTo(0, mysize.height)
    //   ..relativeLineTo(-mysize.width + yfun, 0)
    //   ..close();
    // canvas.drawPath(path, paint..color = color);
    // canvas.translate(0, mysize.height);
    // _canvasInit(
    //   canvas,
    //   offset.dy,
    //   yfun,
    //   paint: paint..color = Colors.green,
    //   path: path,
    // );
  }

  late double changeValue = radius.width / 2; //变化值
  Size radius = Size(60, 20);
  Size get ctrlRadius => Size(radius.width * mX, radius.height * mY); //控制点radius
  // final double mX = 0.551915024494; // 圆形
  final double mY = 1; // 圆形
  final double mX = 1; // 矩形
  // final double mY = 1.1; // 矩形
  Axis axis = Axis.vertical;
  double before = 0;
  bool isLeft(double offsetMove) {
    bool flag = true;
    if (before > offsetMove) flag = false;
    before = offsetMove;
    return flag;
  }

  Future<void> _canvasInitRectangle(
    Canvas canvas,
    double offsetMove,
    double offsetEnd, {
    required Path path,
    required Paint paint,
  }) async {
    double percent = 0;
    double cutOffsetLeft = 0; // 左 下
    double cutOffsetRight = 0;
    percent = (offsetMove - yfun2).abs() / yfun;
    if (axis == Axis.horizontal) {}
    if (!isLeft(offsetMove)) {
      if (percent > 0 && percent <= 0.5) {
        cutOffsetLeft = changeValue * percent;
      } else if (percent > 0.5 && percent < 1.0) {
        ///坐标恢复,原本的位置 + 位移距离✖系数,系数为: 0.5 ~ 0
        cutOffsetLeft = changeValue * (1 - percent);
      }
      radius = Size(radius.width - cutOffsetLeft, radius.height);
    } else {
      if (percent > 0 && percent <= 0.5) {
        cutOffsetRight = changeValue * percent;
      } else if (percent > 0.5 && percent < 1.0) {
        ///坐标恢复,原本的位置 + 位移距离✖系数,系数为: 0.5 ~ 0
        cutOffsetRight = changeValue * (1 - percent);
      }
      radius = Size(radius.width - cutOffsetRight, radius.height);
    }
    cutOffsetLeft = 0; // 左 下
    cutOffsetRight = 0;
    // print('percent ${isLeft(offsetMove)} $percent cutOffsetRight:$cutOffsetRight cutOffsetLeft:$cutOffsetLeft }');
    if (axis == Axis.vertical) {
      Rect rect = Rect.fromCenter(center: Offset(0, 0 + offsetMove), width: radius.width, height: radius.height);
      RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(10));
      canvas.drawRRect(rrect, paint);
    } else {
      Rect rect = Rect.fromCenter(center: Offset(0 + offsetMove, 0), width: radius.width, height: radius.height);
      RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(10));
      canvas.drawRRect(rrect, paint);
    }
  }

  Future<void> _canvasInit(
    Canvas canvas,
    double offsetMove,
    double offsetEnd, {
    required Path path,
    required Paint paint,
  }) async {
    double percent = 0;
    double cutOffsetLeft = 0; // 左 下
    double cutOffsetRight = 0;
    percent = (offsetMove - yfun2).abs() / yfun;
    if (axis == Axis.horizontal) {}
    if (!isLeft(offsetMove)) {
      if (percent > 0 && percent <= 0.5) {
        cutOffsetLeft = changeValue * percent;
      } else if (percent > 0.5 && percent < 1.0) {
        ///坐标恢复,原本的位置 + 位移距离✖系数,系数为: 0.5 ~ 0
        cutOffsetLeft = changeValue * (1 - percent);
      }
      radius = Size(radius.width - cutOffsetLeft, radius.height);
    } else {
      if (percent > 0 && percent <= 0.5) {
        cutOffsetRight = changeValue * percent;
      } else if (percent > 0.5 && percent < 1.0) {
        ///坐标恢复,原本的位置 + 位移距离✖系数,系数为: 0.5 ~ 0
        cutOffsetRight = changeValue * (1 - percent);
      }
      radius = Size(radius.width - cutOffsetRight, radius.height);
    }
    cutOffsetLeft = 0; // 左 下
    cutOffsetRight = 0;
    // print('percent ${isLeft(offsetMove)} $percent cutOffsetRight:$cutOffsetRight cutOffsetLeft:$cutOffsetLeft }');
    if (axis == Axis.vertical) {
      Offset topPoint = Offset(0, -radius.height + offsetMove - cutOffsetRight);
      Offset topPointEnd = Offset(radius.width, -radius.height - cutOffsetRight);
      Offset topPointLeft = Offset(radius.width - ctrlRadius.width, -radius.height);
      Offset topPointRight = Offset(ctrlRadius.width, 0 + cutOffsetRight);

      Offset rPoint = Offset(radius.width, radius.height + cutOffsetRight);
      Offset rPointTop = Offset(radius.width, radius.height - ctrlRadius.height + cutOffsetRight);
      Offset rPointBottom = Offset(0, ctrlRadius.height);

      Offset bottomPoint = Offset(-radius.width, radius.height + cutOffsetLeft);
      Offset bottomPointLeft = Offset(-ctrlRadius.width, 0 - cutOffsetLeft);
      Offset bottomPointRight = Offset(-(radius.width - ctrlRadius.width), radius.height);

      Offset lPoint = Offset(-radius.width, -radius.height - cutOffsetLeft);
      Offset lPointTop = Offset(0, -ctrlRadius.height);
      Offset lPointBottom = Offset(-radius.width, -(radius.height - ctrlRadius.height) - cutOffsetLeft);

      path.reset();
      path.moveTo(topPoint.dx, topPoint.dy);
      path.relativeCubicTo(topPointRight.dx, topPointRight.dy, rPointTop.dx, rPointTop.dy, rPoint.dx, rPoint.dy);
      path.relativeCubicTo(rPointBottom.dx, rPointBottom.dy, bottomPointRight.dx, bottomPointRight.dy, bottomPoint.dx, bottomPoint.dy);
      path.relativeCubicTo(bottomPointLeft.dx, bottomPointLeft.dy, lPointBottom.dx, lPointBottom.dy, lPoint.dx, lPoint.dy);
      path.relativeCubicTo(lPointTop.dx, lPointTop.dy, topPointLeft.dx, topPointLeft.dy, topPointEnd.dx, topPointEnd.dy);
      canvas.drawPath(path, paint);
    } else {
      Offset topPoint = Offset(0 + offsetMove, -radius.height);
      Offset topPointEnd = Offset(radius.width + cutOffsetRight, -radius.height);
      Offset topPointLeft = Offset(radius.width - ctrlRadius.width, -radius.height);
      Offset topPointRight = Offset(ctrlRadius.width, 0);

      Offset rPoint = Offset(radius.width + cutOffsetLeft, radius.height);
      Offset rPointTop = Offset(radius.width + cutOffsetLeft, radius.height - ctrlRadius.height);
      Offset rPointBottom = Offset(0, ctrlRadius.height);

      Offset bottomPoint = Offset(-radius.width - cutOffsetLeft, radius.height);
      Offset bottomPointLeft = Offset(-ctrlRadius.width, 0);
      Offset bottomPointRight = Offset(-(radius.width - ctrlRadius.width) - cutOffsetLeft, radius.height);

      Offset lPoint = Offset(-radius.width - cutOffsetRight, -radius.height);
      Offset lPointTop = Offset(0, -ctrlRadius.height);
      Offset lPointBottom = Offset(-radius.width - cutOffsetRight, -(radius.height - ctrlRadius.height));

      path.reset();
      path.moveTo(topPoint.dx, topPoint.dy);
      path.relativeCubicTo(topPointRight.dx, topPointRight.dy, rPointTop.dx, rPointTop.dy, rPoint.dx, rPoint.dy);
      path.relativeCubicTo(rPointBottom.dx, rPointBottom.dy, bottomPointRight.dx, bottomPointRight.dy, bottomPoint.dx, bottomPoint.dy);
      path.relativeCubicTo(bottomPointLeft.dx, bottomPointLeft.dy, lPointBottom.dx, lPointBottom.dy, lPoint.dx, lPoint.dy);
      path.relativeCubicTo(lPointTop.dx, lPointTop.dy, topPointLeft.dx, topPointLeft.dy, topPointEnd.dx, topPointEnd.dy);
      canvas.drawPath(path, paint);
    }
  }

  /// 是否是有效的移动点
  bool _isUsePosition(double dy, Size mysizeReal) {
    bool flag = true;
    // print('$before $dy ${(before - dy).abs()} ${mysizeReal.height}');
    if ((before - dy).abs() > mysizeReal.height / 4) flag = false;
    before = dy;
    return flag;
  }
}

@xietian0908
Copy link
Author

这个问题只有在快速滑动tabview的时候,在移动动画未结束之前点击,tabbar才会发生,这个没有报错信息只有中间的值变化突然有问题了offset.dy
image

@zmtzawqlp
Copy link
Member

你可以自己debug下。出错的时候的堆栈信息,看看问题出在哪里

@xietian0908
Copy link
Author

a36d58a4c2332341c2d736a1d80e8e2
controller.indexIsChanging为true的情况下就会出错

@zmtzawqlp
Copy link
Member

zmtzawqlp commented May 30, 2023

这个属性表示正在动画,主要是你写的 TabbarVSquareIndicator 逻辑过于复杂。你用自带的 ColorTabIndicator 有这个问题吗?

@xietian0908
Copy link
Author

xietian0908 commented May 30, 2023

class ExtendedTabsStackoverflow extends StatefulWidget {
  const ExtendedTabsStackoverflow({super.key});

  @override
  State<ExtendedTabsStackoverflow> createState() => _ExtendedTabsStackoverflowState();
}

class _ExtendedTabsStackoverflowState extends State<ExtendedTabsStackoverflow> {
  int length = 16;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: DefaultTabController(
        length: length,
        child: Row(
          children: [
            ExtendedTabBar(
              // labelPadding: EdgeInsets.symmetric(horizontal: 50.w),
              indicatorPadding: const EdgeInsets.all(0),
              labelPadding: const EdgeInsets.only(bottom: 0),
              isScrollable: true,
              indicatorWeight: 0,
              scrollDirection: Axis.vertical,
              tabs: _tabs(),
              indicator: TabbarVSquareIndicator(
                length: length,
              ),
            ),
            Expanded(
              child: ExtendedTabBarView(
                scrollDirection: Axis.vertical,
                children: [
                  for (int i = 0; i < length; i++)
                    Container(
                      color: Colors.primaries[i],
                      alignment: Alignment.center,
                      child: Text('tabview $i'),
                    )
                ],
              ),
            )
          ],
        ),
      ),
    );
  }

  List<Widget> _tabs() {
    return List.generate(
      length,
      (index) => Container(
        height: 100,
        margin: const EdgeInsets.only(bottom: 16),
        alignment: Alignment.center,
        child: Text('tab $index'),
      ),
    );
  }
}

class TabbarVSquareIndicator extends Decoration {
  final Color color;
  final PaintingStyle paintingStyle;
  final int length;

  const TabbarVSquareIndicator({
    this.length = 0,
    this.color = Colors.red,
    this.paintingStyle = PaintingStyle.fill,
  });

  @override
  CustomPainter createBoxPainter([VoidCallback? onChanged]) {
    return CustomPainter(
      this,
      onChanged,
      paintingStyle: paintingStyle,
      color: color,
      length: length,
    );
  }
}

class CustomPainter extends BoxPainter {
  final TabbarVSquareIndicator decoration;
  final Color color;
  final PaintingStyle paintingStyle;
  final int length;

  CustomPainter(
    this.decoration,
    VoidCallback? onChanged, {
    required this.color,
    required this.paintingStyle,
    required this.length,
  }) : super(onChanged);

  Path path = Path();
  Size radius = const Size(60, 20);

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
    Size mysize = Size(configuration.size!.width, configuration.size!.height - 16);
    radius = Size(configuration.size!.width, (configuration.size!.height - 16));
    // Offset myoffset = Offset(offset.dx, offset.dy);
    // final Rect rect = myoffset & mysize;
    final Paint paint = Paint()
      ..strokeCap = StrokeCap.round
      ..color = color
      ..style = paintingStyle
      ..strokeWidth = 4;

    canvas.translate(mysize.width / 2, mysize.height / 2);
    for (int i = 0; i < length; i++) {
      Rect rect = Rect.fromCenter(center: Offset(0, 0 + i * configuration.size!.height), width: radius.width, height: radius.height);
      RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(10));
      canvas.drawRRect(rrect, paint..color = const Color(0xff38424B));
    }
    print('offset ==> ${offset.dy}');
    Rect rect = Rect.fromCenter(center: Offset(0, 0 + offset.dy), width: radius.width, height: radius.height);
    RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(10));
    canvas.drawRRect(rrect, paint..color = color);
  }
}

抱歉 我这里弄了个简单的demo 也是会出现视频中item突然到第一个再回来的情况

@xietian0908
Copy link
Author

https://github.com/fluttercandies/extended_tabs/assets/64937500/71d52bab-a9bb-4ebe-a4bf-78d353332147
我用demo for web 里面的例子也复现了,这个问题在水平方向也同样存在;多tab情况下更明显

@zmtzawqlp
Copy link
Member

web 的代码比较老了。用官方的 tab 会有这种问题吗?

@xietian0908
Copy link
Author

xietian0908 commented May 31, 2023

import 'package:flutter/material.dart';
import 'package:extended_tabs/extended_tabs.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const ExtendedTabsStackoverflow(),
    );
  }
}

class ExtendedTabsStackoverflow extends StatefulWidget {
  const ExtendedTabsStackoverflow({super.key});

  @override
  State<ExtendedTabsStackoverflow> createState() => _ExtendedTabsStackoverflowState();
}

class _ExtendedTabsStackoverflowState extends State<ExtendedTabsStackoverflow> {
  int length = 16;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: DefaultTabController(
        length: length,
        child: Column(
          children: [
            ExtendedTabBar(
              // labelPadding: EdgeInsets.symmetric(horizontal: 50.w),
              indicatorPadding: const EdgeInsets.all(0),
              labelPadding: const EdgeInsets.only(bottom: 0),
              isScrollable: true,
              indicatorWeight: 0,
              // scrollDirection: Axis.vertical,
              tabs: _tabs(),
              indicator: TabbarVSquareIndicator(
                length: length,
              ),
            ),
            Expanded(
              child: ExtendedTabBarView(
                // scrollDirection: Axis.vertical,
                children: [
                  for (int i = 0; i < length; i++)
                    Container(
                      color: Colors.primaries[i],
                      alignment: Alignment.center,
                      child: Text('tabview $i'),
                    )
                ],
              ),
            )
          ],
        ),
      ),
    );
  }

  List<Widget> _tabs() {
    return List.generate(
      length,
      (index) => Container(
        height: 100,
        width: 100,
        margin: const EdgeInsets.only(bottom: 16),
        alignment: Alignment.center,
        child: Text('tab $index'),
      ),
    );
  }
}

class TabbarVSquareIndicator extends Decoration {
  final Color color;
  final PaintingStyle paintingStyle;
  final int length;

  const TabbarVSquareIndicator({
    this.length = 0,
    this.color = Colors.red,
    this.paintingStyle = PaintingStyle.fill,
  });

  @override
  CustomPainter createBoxPainter([VoidCallback? onChanged]) {
    return CustomPainter(
      this,
      onChanged,
      paintingStyle: paintingStyle,
      color: color,
      length: length,
    );
  }
}

class CustomPainter extends BoxPainter {
  final TabbarVSquareIndicator decoration;
  final Color color;
  final PaintingStyle paintingStyle;
  final int length;

  CustomPainter(
    this.decoration,
    VoidCallback? onChanged, {
    required this.color,
    required this.paintingStyle,
    required this.length,
  }) : super(onChanged);

  Path path = Path();
  Size radius = const Size(60, 20);

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
    Size mysize = Size(configuration.size!.width, configuration.size!.height - 16);
    radius = Size(configuration.size!.width, (configuration.size!.height - 16));
    // Offset myoffset = Offset(offset.dx, offset.dy);
    // final Rect rect = myoffset & mysize;
    final Paint paint = Paint()
      ..strokeCap = StrokeCap.round
      ..color = color
      ..style = paintingStyle
      ..strokeWidth = 4;

    canvas.translate(mysize.width / 2, mysize.height / 2);
    for (int i = 0; i < length; i++) {
      Rect rect = Rect.fromCenter(center: Offset(0 + i * configuration.size!.width, 0), width: radius.width, height: radius.height);
      RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(10));
      canvas.drawRRect(rrect, paint..color = const Color(0xff38424B));
    }
    print('offset ==> ${offset.dy}');
    Rect rect = Rect.fromCenter(center: Offset(0 + offset.dx, 0), width: radius.width, height: radius.height);
    RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(10));
    canvas.drawRRect(rrect, paint..color = color);
  }
}
官方只有水平的 我测试了extendedtabbar extendedtabview 、和 tabbar tabview 例子中换一下 官方的是没有问题的

@zmtzawqlp
Copy link
Member

你不是说水平也有问题吗? 水平跟官方的代码一模一样的

@xietian0908
Copy link
Author

6a1964c00278d800860263ec2751cf9
这是水平的

@zmtzawqlp
Copy link
Member

zmtzawqlp commented May 31, 2023

用你的例子在真机上面,没有重现。水平和垂直都没有重现

@xietian0908
Copy link
Author

我就是用真机android平板测的, extended_tabs: ^4.0.2;然后那个异常好像必须得是连续移动两格移动未结束的时候点击tabbar会出现;你可以看那个视频都是同一套代码只是换了个widget

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants