Flutter how to draw a curved path between widgets keeping it responsive

I want to make a curved path between widgets on my website, like a story timeline.
I have managed to draw the curved paths, however, the problem arises when resizing the page as they move and start overlapping with the widgets.

What I am trying to achieve

What I have so far

The issue when resizing

Currently, I am using the following code:

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:sales_pitch/views/1.dart';
import 'package:sales_pitch/views/2.dart';
import 'package:sales_pitch/views/4.dart';
import 'package:sales_pitch/views/5.dart';
import 'package:sales_pitch/views/6.dart';
import 'package:sales_pitch/views/landing.dart';
import 'package:sales_pitch/widgets/curved_painter.dart';

import '../constants/assets.dart';
import '3.dart';

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

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {
  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;

    List<Widget> sections = [
      const LandingView(),
      const FirstView(),
      const SecondView(),
      const ThirdView(),
      const FourthView(),
      const FifthView(),
      const SixthView(),
      const SizedBox(height: 250),
    ];

    //* I have also tried using positioned images however these also moved and screwed up the layout
    List<Widget> lines = [
      Positioned(
        top: size.height * 0.95,
        left: 0,
        right: 0,
        child: Align(
          alignment: Alignment.bottomCenter,
          child: SizedBox(
            width: size.width * 0.18,
            child: Image.asset(
              AppAssets.getAppPath(1),
              fit: BoxFit.fitWidth,
            ),
          ),
        ),
      ),
      Positioned(
        top: size.height * 1.42,
        left: size.width * 0.2,
        child: Align(
          alignment: Alignment.centerLeft,
          child: SizedBox(
            width: size.width * 0.3,
            child: Image.asset(
              AppAssets.getAppPath(2),
              fit: BoxFit.fitWidth,
            ),
          ),
        ),
      ),
      Positioned(
        top: size.height * 1.58,
        right: size.width * 0.2,
        child: Align(
          alignment: Alignment.centerRight,
          child: SizedBox(
            width: size.width * 0.35,
            child: Image.asset(
              AppAssets.getAppPath(3),
              fit: BoxFit.fitWidth,
            ),
          ),
        ),
      ),
      Positioned(
        top: size.height * 1.75,
        left: size.width * 0.15,
        right: size.width * 0.1,
        child: Align(
          alignment: Alignment.bottomCenter,
          child: SizedBox(
            height: size.height * 0.2,
            child: Image.asset(
              AppAssets.getAppPath(4),
              fit: BoxFit.fitHeight,
            ),
          ),
        ),
      ),
      Positioned(
        top: size.height * 2.5,
        left: 0,
        right: 0,
        child: Align(
          alignment: Alignment.bottomCenter,
          child: SizedBox(
            height: size.height * 0.26,
            child: Image.asset(
              AppAssets.getAppPath(5),
              fit: BoxFit.fitHeight,
            ),
          ),
        ),
      ),
      Positioned(
        top: size.height * 3.2,
        left: size.width * 0.4,
        child: Align(
          alignment: Alignment.topCenter,
          child: SizedBox(
            height: size.height * 0.3,
            child: Image.asset(
              AppAssets.getAppPath(6),
              fit: BoxFit.fitHeight,
            ),
          ),
        ),
      ),
      Positioned(
        top: size.height * 4,
        right: size.width * 0.1,
        child: Align(
          alignment: Alignment.centerRight,
          child: SizedBox(
            height: size.height * 0.45,
            child: Image.asset(
              AppAssets.getAppPath(7),
              fit: BoxFit.fitHeight,
            ),
          ),
        ),
      ),
      Positioned(
        top: size.height * 5.4,
        left: 0,
        right: 0,
        child: Align(
          alignment: Alignment.bottomCenter,
          child: SizedBox(
            height: size.height * 0.2,
            child: Image.asset(
              AppAssets.getAppPath(8),
              fit: BoxFit.fitHeight,
            ),
          ),
        ),
      ),
    ];

    return Scaffold(
      body: SingleChildScrollView(
        child: Stack(
          clipBehavior: Clip.none,
          children: [
            SizedBox(height: size.height * (sections.length / 2)),
            //...lines,
            //TODO: add the new lines to a list to cleanup code
            //* First Line
            Positioned(
              top: size.height * 0.95,
              left: size.width * 0.35,
              height: size.height * 0.23,
              width: size.width * 0.15,
              child: CustomPaint(
                painter: CurvePainter(),
                willChange: true,
                child: Container(),
              ),
            ),
            //* Second Line
            Positioned(
              top: size.height * 1.35,
              left: size.width * 0.15,
              height: size.height * 0.12,
              width: size.width * 0.35,
              child: Transform(
                transform: Matrix4.rotationY(pi),
                alignment: Alignment.center,
                child: CustomPaint(
                  painter: CurvePainter(),
                  willChange: true,
                  child: Container(),
                ),
              ),
            ),
            Column(
                mainAxisAlignment: MainAxisAlignment.start, children: sections),
          ],
        ),
      ),
    );
  }
}

I have also tried using images exported from Figma and positioning them as needed, however, the same issue arises. I would also prefer the lines in code so they can easily be changed in the future.

Thank you in advance.

Leave a Comment