You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter实现可旋转半圆形无限轮播指示器技术咨询

实现可旋转的无限半圆形轮播指示器

需求说明

  • 实现支持顺时针、逆时针旋转的无限半圆形轮播指示器
  • 要求选中状态的彩色标记始终处于中间位置

当前实现代码(可在Dart Pad运行)

//Full circle with colored dast at the bottom
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Dashed Circle Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: Scaffold(
        appBar: AppBar(title: Text('Dashed Circle')),
        body: Center(child: DashedCircleWithControls()),
      ),
    );
  }
}

class DashedCircleWithControls extends StatefulWidget {
  @override
  _DashedCircleWithControlsState createState() => _DashedCircleWithControlsState();
}

class _DashedCircleWithControlsState extends State<DashedCircleWithControls> 
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _rotationAngle = 0.0;
  final double _rotationStep = 0.7;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 200),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _rotateForward() {
    setState(() {
      _rotationAngle += _rotationStep;
      _controller.forward(from: 0.0);
    });
  }

  void _rotateBackward() {
    
    setState(() {
      _rotationAngle -= _rotationStep;
      _controller.forward(from: 0.0);
    });
    
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Stack(
          alignment: Alignment.center,
          children: [
            // 旋转的虚线圆环
            AnimatedBuilder(
              animation: _controller,
              builder: (context, child) {
                return Transform.rotate(
                  angle: _rotationAngle,
                  child: CustomPaint(
                    size: Size(200, 200),
                    painter: RotatingDashedCirclePainter(),
                  ),
                );
              },
            ),
//             固定的彩色标记(始终在右侧)
            CustomPaint(
              size: Size(200, 200),
              painter: FixedColoredDashPainter(),
            ),
          ],
        ),
        SizedBox(height: 20),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            IconButton(
              icon: Icon(Icons.remove, size: 32),
              onPressed: _rotateBackward,
              color: Colors.red,
            ),
            SizedBox(width: 40),
            IconButton(
              icon: Icon(Icons.add, size: 32),
              onPressed: _rotateForward,
              color: Colors.green,
            ),
          ],
        ),
      ],
    );
  }
}

class RotatingDashedCirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = size.width / 2 - 10;
    final paint = Paint()
      ..color = Colors.grey
      ..strokeWidth = 2
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    const dashCount = 38;
    const dashAngle = 2 * 3.14159 / dashCount;
    const dashLength = 0.7;

    for (int i = 0; i < dashCount; i++) {
      final startAngle = i * dashAngle;
      
      // 跳过绘制右侧(3点钟位置)的虚线
//       if (i >= 0 && i <= 15) continue;
      
      canvas.drawArc(
        Rect.fromCircle(center: center, radius: radius),
        startAngle,
        dashAngle * dashLength,
        false,
        paint,
      );
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

class FixedColoredDashPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = size.width / 2 - 10;
    final paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 6
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    const dashCount = 38;
    const dashAngle = 2 * 3.14 / dashCount;
    const dashLength = 0.7;

    // 只绘制右侧(3点钟位置)的彩色标记
    // Flutter坐标系中0弧度对应右侧
    canvas.drawArc(
      Rect.fromCircle(center: center, radius: radius),
      (3.141592654 / 2.0) - 0.05, // 起始于0弧度(右侧)
      dashAngle * dashLength,
      false,
      paint,
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

内容的提问来源于stack exchange,提问作者Muhammad Faizan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 10:53:17