Logo
Published on
·8 min read

Creating a Progress Bar (Circle) for Displaying Progress - React Native

I apologize in advance for any awkward expressions in English.

English is not my native language, and I have relied on ChatGPT's assistance to proceed with the translation.

Overview

The "Simple Vocab Buddy" app features a Progress Circle displaying group study progress and quiz completion status. Let's explore how this was implemented.

Implemented Screen

The image below showcases the screen where the Progress Circle is applied in the current live app.

Implemented Screen with Progress Circle

Implementation Process

❗ The example code may have omitted or simplified styles and functions for the sake of the post.

1. Install react-native-svg

As we'll be implementing the Progress Circle using SVG, let's start by installing react-native-svg.

npm i react-native-svg

2. Create the Progress Circle Component

✨ I modified the code based on Making a progress circle in React.

The referenced code was originally written for React, so I made modifications to make it compatible with React Native and tailored it to my specific use case.

Pie.tsx

// https://dev.to/jackherizsmith/making-a-progress-circle-in-react-3o65

import Svg, {Circle, G, Text, TSpan} from 'react-native-svg';

const cleanPercentage = (percentage) => {
  const tooLow = !Number.isFinite(+percentage) || percentage < 0;
  const tooHigh = percentage > 100;
  return tooLow ? 0 : tooHigh ? 100 : +percentage;
};

const PieCircle = ({ color, pct, radius = 50, borderWidth = 2 }:{
  color: string;
  pct?: number;
  radius: number;
  borderWidth: number;
}) => {
  const circ = 2 * Math.PI * radius;
  const strokePct = ((100 - pct) * circ) / 100;
  return (
    <Circle
      r={radius}
      cx={radius + borderWidth}
      cy={radius + borderWidth}
      fill="transparent"
      fillRule={"nonzero"}
      stroke={pct > 0 ? color : ""}
      strokeWidth={borderWidth}
      strokeDasharray={circ}
      strokeDashoffset={pct ? strokePct : 0}
      strokeLinecap="round"
    ></Circle>
  );
};

const PieText = ({ percentage, title }) => {
  return (
      <Text
        x="50%"
        y="50%"
        textAnchor="middle"
      >
        <TSpan x="50%" dy={0} fontSize={11} fontWeight={'bold'} fill={'#151A20'}>{title}</TSpan>
        <TSpan x="50%" dy={14} fontSize={10} fill={'#151A20'}>{`${percentage}%`}</TSpan>
      </Text>
  );
};

const Pie = ({ percentage, color, title, radius = 50, borderWidth = 2}:{
  percentage: number;
  color: string;
  title: string;
  radius: number;
  borderWidth: number;
}) => {
  const pct = cleanPercentage(percentage);
  const fullSize = (radius * 2)+(borderWidth*2);
  return (
    <Svg width={fullSize} height={fullSize}>
      <G transform={`rotate(-90 ${radius + borderWidth} ${radius + borderWidth})`} fill={'white'}>
        <Circle
          r={radius}
          cx={radius + borderWidth}
          cy={radius + borderWidth}
          fill={'#FFFFFF'}
        ></Circle>
        <PieCircle borderWidth={borderWidth} radius={radius} color="#e3e3e3" pct={100} />
        <PieCircle borderWidth={borderWidth} radius={radius} color={color} pct={pct} />
      </G>
      <PieText percentage={pct} title={title} />
    </Svg>
  );
};

export default Pie;

3. Apply the Progress Circle

  • Use <Pie ... /> tag.
    • radius: Specify the radius size.
    • borderWidth: Specify the thickness of the Progress Circle.
    • percentage: Specify the percentage of the Progress Circle.
    • color: Specify the color of the Progress Circle.
    • title: Specify the text for the Progress Circle.
    <View>
      <TouchableOpacity
        accessibilityLabel={'Learning'}
        onPress={() => {}}>
        <Pie radius={28} borderWidth={4} percentage={getStudyRate()} color={'#4285F4'} title={'Learning'} />
      </TouchableOpacity>
      <View style={{width: 8}} />
      <TouchableOpacity
        accessibilityLabel={'Quiz'}
        onPress={() => {}}>
        <Pie radius={28} borderWidth={4} percentage={getQuizRate()} color={'#4285F4'} title={'Quiz'} />
      </TouchableOpacity>
    </View>