我想在我的Flatter应用程序上显示类似Instagram的故事,并想通过用户头像周围的边框显示用户上传的故事数量.

假设一个用户上传了3个故事,我将在头像图像周围显示3条圆形边界线,用相等数量的空格分隔;如果一个用户上传80个故事,我会显示80条小的圆形边界线,它们之间用相等数量的空格隔开.

我试着用pub.dev个插件来做这个,比如

仅举几个例子,但我似乎无法准确计算空间&破折号以满足上述要求.

下面是一个例子:

FDottedLine(
  color: Colors.black,
  strokeWidth: 2.0,
  dottedLength: 30,
  space: 4,
  corner: FDottedLineCorner.all(100.0),
  child: Padding(
    padding: const EdgeInsets.all(3.0),
    child: SizedBox.square(
      dimension: 0.055.h,
      child: ClipRRect(
        borderRadius: BorderRadius.circular(100),
        child: ImageBox.network(
          photo: user.photo.getOrEmpty,
          elevation: 2,
          replacement: Image.asset(AppAssets.defaultUserImage(user.gender.getOrNull)),
          borderRadius: BorderRadius.circular(100),
        ),
      ),
    ),
  ),
),

无论我如何调整dottedLength&space个参数,我不能得到相同数量的空格和破折号.

我也试过使用Path()CustomPainter(),但我几乎不知道如何使用它.

This is an image of what i'm tried to achieve

你知道我如何使用CustomPainter()或一个插件来实现这一点吗?

推荐答案

感谢您发布您的所有try ,因为这让我直接跳到CustomPath()进行try

这种方法(可能)(测试不好)的效果是drawArc

其逻辑就是根据层数画出一条弧线,并在留出一定空间后开始下一条弧线

下面的代码循环计算楼层数,以绘制每个楼层弧,并在将一些值(楼层之间的间距)添加到下一个弧形位置(在圆圈上)的起点后,开始下一个楼层弧(如果楼层>;1).

    for(int i =0;i<numberOfStories;i++){
        canvas.drawArc(
            rect,
            inRads(startOfArcInDegree),
            inRads(arcLength),
            false,
            Paint()
             ..color = i==0||i==1?Colors.grey:Colors.teal
             ..strokeWidth =14.0
             ..style = PaintingStyle.stroke

  );

  
           startOfArcInDegree += arcLength + spaceLength;
}

Result

完整代码和详细解释:

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

class DottedBorder extends CustomPainter {
  //number of stories
  final int numberOfStories;
  //length of the space arc (empty one)
  final int spaceLength;
  //start of the arc painting in degree(0-360)
  double startOfArcInDegree = 0;

  DottedBorder({required this.numberOfStories, this.spaceLength = 10});

  //drawArc deals with rads, easier for me to use degrees
  //so this takes a degree and change it to rad
  double inRads(double degree){
    return (degree * pi)/180;
  }

  @override
  bool shouldRepaint(DottedBorder oldDelegate) {
    return true;
  }

  @override
  void paint(Canvas canvas, Size size) {

    //circle angle is 360, remove all space arcs between the main story arc (the number of spaces(stories) times the  space length
    //then subtract the number from 360 to get ALL arcs length
    //then divide the ALL arcs length by number of Arc (number of stories) to get the exact length of one arc
    double arcLength = (360 - (numberOfStories * spaceLength))/numberOfStories;


    //be careful here when arc is a negative number
    //that happens when the number of spaces is more than 360
    //feel free to use what logic you want to take care of that
    //note that numberOfStories should be limited too here
    if(arcLength<=0){
      arcLength = 360/spaceLength -1;
    }


    Rect rect = Rect.fromLTWH(0, 0, size.width, size.height);

    //looping for number of stories to draw every story arc
    for(int i =0;i<numberOfStories;i++){
      //printing the arc
      canvas.drawArc(
          rect,
          inRads(startOfArcInDegree),
          //be careful here is:  "double sweepAngle", not "end"
          inRads(arcLength),
          false,
          Paint()
          //here you can compare your SEEN story index with the arc index to make it grey
            ..color = i==0||i==1?Colors.grey:Colors.teal
            ..strokeWidth =14.0
            ..style = PaintingStyle.stroke

      );

      //the logic of spaces between the arcs is to start the next arc after jumping the length of space
      startOfArcInDegree += arcLength + spaceLength;
    }




  }
}



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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Arcs etc')),
      body:Center(
          child: Stack(
            alignment: Alignment.center,
            children: [
              SizedBox(
                width: 300,height: 300,

                child: CustomPaint(
                                    painter:  DottedBorder(numberOfStories: 13,spaceLength:4 ),
              ),),
              Container(child:const Center(child: Text("Some Image",style: TextStyle(fontSize: 18,color: Colors.black),)),width: 270,height: 270,decoration: const BoxDecoration(color: Colors.purple,shape: BoxShape.circle),)
            ],
          )

      )
    );
  }
}

void main() {
  runApp(
    const MaterialApp(
      home: DottedBorderExample(),
    ),
  );
}

Flutter相关问答推荐

build_runner显示错误没有为类ClassDeclaration定义getter宏关键字'

什么是dart :这只是dart ?

就像Flutter 中的头文件一样?

脚本具有不受支持的MIME类型(';Text/html';).(messaging/failed-service-worker-registration)

Flutter /dart 列表.第一个子类列表中的Where()错误

如何在Date Time类中只显示日期?

Flutter Android Google Sign-in error:ApiException:10在确认软件包名称、SHA-1 fingerprint 和设置同意页面后出现异常

从future 列表到流列表Flutter Firebase

处理 flutter/dart 模型中的空值

更改底部导航栏下方 Flutter Snackbar 的位置

提供者、存储库和服务之间有什么区别?

如何建立最近的文件列表?

Listview 如何转到下一行/新行?

容器在发布模式下显示灰色框(抖动)

如何在 Flutter 中将 ScaffoldMessenger 中的 Snackbar 显示为 Widget?

Flutter 中出现错误空判断运算符用于空值

使用 onPressed 切换到另一个屏幕无法正常工作

如何在 flutter 中更改日期 Select 器 colored颜色 ?

try 为两个下拉值添加共享首选项,但在 Flutter 上显示错误断言失败:?

Flutter - 如何调用此函数?