Flutter - Is this an efficient way to animate a page progress indicator?

Viewed 86

Basically, I want to do like a progress bar as below.

The recording is taken straight from the code below.

This recording is taken straight from the code.

This is my current solution :

two_pagination_progress.dart

BlocBuilder<TwoPaginationProgressCubit, TwoPaginationProgressState>
    buildTwoPaginationProgress(double screenWidth) {
  return BlocBuilder<TwoPaginationProgressCubit, TwoPaginationProgressState>(
    builder: (cubit, context) => TwoPaginationProgress(
      screenWidth: screenWidth,
      leftSideLength:
          cubit.read<TwoPaginationProgressCubit>().state.leftSideLength,
      firstCircleLength:
          cubit.read<TwoPaginationProgressCubit>().state.firstCircleLength,
      firstCircleOpacity:
          cubit.read<TwoPaginationProgressCubit>().state.firstCircleOpacity,
      firstCircleNumber:
          cubit.read<TwoPaginationProgressCubit>().state.firstCircleNumber,
      firstCircleNumberSize:
          cubit.read<TwoPaginationProgressCubit>().state.firstCircleNumberSize,
      secondCircleLength:
          cubit.read<TwoPaginationProgressCubit>().state.secondCircleLength,
      secondCircleOpacity:
          cubit.read<TwoPaginationProgressCubit>().state.secondCircleOpacity,
      secondCircleNumber:
          cubit.read<TwoPaginationProgressCubit>().state.secondCircleNumber,
      secondCircleNumberSize:
          cubit.read<TwoPaginationProgressCubit>().state.secondCircleNumberSize,
    ),
  );
}

class TwoPaginationProgress extends StatelessWidget {
  const TwoPaginationProgress({
    Key? key,
    // Others
    required this.leftSideLength,
    required this.screenWidth,
    // First circle attributes
    required this.firstCircleLength,
    required this.firstCircleOpacity,
    required this.firstCircleNumber,
    required this.firstCircleNumberSize,
    // Second circle attributes
    required this.secondCircleOpacity,
    required this.secondCircleLength,
    required this.secondCircleNumber,
    required this.secondCircleNumberSize,
  }) : super(key: key);

  final double leftSideLength;
  final double screenWidth;

  final double firstCircleLength;
  final double firstCircleOpacity;
  final int firstCircleNumber;
  final double firstCircleNumberSize;

  final double secondCircleLength;
  final double secondCircleOpacity;
  final int secondCircleNumber;
  final double secondCircleNumberSize;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        AnimatedContainer(
          duration: const Duration(milliseconds: 250),
          width: leftSideLength,
          height: screenWidth * 0.0845,
          curve: Curves.easeOutExpo,
        ),
        Row(
          children: [
            AnimatedOpacity(
              opacity: firstCircleOpacity,
              duration: const Duration(milliseconds: 250),
              curve: Curves.easeOutExpo,
              child: Material(
                color: kcTertiaryColor,
                borderRadius: BorderRadius.circular(100),
                child: AnimatedContainer(
                  duration: const Duration(milliseconds: 250),
                  curve: Curves.easeOutExpo,
                  alignment: Alignment.center,
                  width: firstCircleLength,
                  height: firstCircleLength,
                  child: AnimatedDefaultTextStyle(
                    duration: const Duration(milliseconds: 250),
                    curve: Curves.easeOutExpo,
                    style: ktsHeadingParagraph.copyWith(
                        fontSize: firstCircleNumberSize,
                        color: Colors.black,
                        fontWeight: FontWeight.w700),
                    child: Text('$firstCircleNumber'),
                  ),
                ),
              ),
            ),
            horizontalSpaceTiny,
            horizontalSpaceTiny,
            AnimatedOpacity(
              opacity: secondCircleOpacity,
              duration: const Duration(milliseconds: 250),
              curve: Curves.easeOutExpo,
              child: Material(
                color: kcTertiaryColor,
                borderRadius: BorderRadius.circular(100),
                child: AnimatedContainer(
                  duration: const Duration(milliseconds: 250),
                  curve: Curves.easeOutExpo,
                  alignment: Alignment.center,
                  width: secondCircleLength,
                  height: secondCircleLength,
                  child: AnimatedDefaultTextStyle(
                    duration: const Duration(milliseconds: 250),
                    curve: Curves.easeOutExpo,
                    style: ktsHeadingParagraph.copyWith(
                        fontSize: secondCircleNumberSize,
                        color: Colors.black,
                        fontWeight: FontWeight.w700),
                    child: Text('$secondCircleNumber'),
                  ),
                ),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

two_pagination_progress_cubit.dart

class TwoPaginationProgressCubit extends Cubit<TwoPaginationProgressState> {
  final double screenWidth;
  final double screenHeight;

  TwoPaginationProgressCubit({
    required this.screenHeight,
    required this.screenWidth,
  }) : super(TwoPaginationProgressState(
          currentProgressIndex: 1,
          screenHeight: screenHeight,
          screenWidth: screenWidth,
          halfScreen: screenWidth / 2,
          leftSideLength: (screenWidth / 2) - (screenWidth * 0.0845 / 2),
          firstCircleLength: screenWidth * 0.0845,
          firstCircleOpacity: 1,
          firstCircleNumber: 1,
          firstCircleNumberSize: 16,
          secondCircleLength: screenWidth * 0.0555,
          secondCircleOpacity: 0.25,
          secondCircleNumber: 2,
          secondCircleNumberSize: 8,
        ));

  void triggerFirstPage() {
    emit(TwoPaginationProgressState(
      currentProgressIndex: 1,
      screenHeight: screenHeight,
      screenWidth: screenWidth,
      halfScreen: screenWidth / 2,
      leftSideLength: (screenWidth / 2) - (screenWidth * 0.0845 / 2),
      firstCircleLength: screenWidth * 0.0845,
      firstCircleOpacity: 1,
      firstCircleNumber: 1,
      firstCircleNumberSize: 16,
      secondCircleLength: screenWidth * 0.0555,
      secondCircleOpacity: 0.25,
      secondCircleNumber: 2,
      secondCircleNumberSize: 8,
    ));
  }

  void triggerSecondPage() {
    emit(TwoPaginationProgressState(
      currentProgressIndex: 2,
      screenHeight: screenHeight,
      screenWidth: screenWidth,
      halfScreen: screenWidth / 2,
      leftSideLength: (screenWidth / 2) -
          10 -
          (screenWidth * 0.0845 / 2) -
          (screenWidth * 0.0555),
      firstCircleLength: screenWidth * 0.0555,
      firstCircleOpacity: 0.25,
      firstCircleNumber: 1,
      firstCircleNumberSize: 8,
      secondCircleLength: screenWidth * 0.0845,
      secondCircleOpacity: 1,
      secondCircleNumber: 2,
      secondCircleNumberSize: 16,
    ));
  }
}

The code works. But it I feel like I'm doing this very inefficiently. Is there a better way to do this?

Thanks in advance!

Edit: Here are some context on the little calculations I did >

Large Circle Length = screenWidth * 0.0845
Small Circle Length = screenWidth * 0.0555

horizontalSpaceTiny = 5 pixels
horizontalSpace10 = 10 pixels
0 Answers
Related