flutterdrawingresponsive

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.


Solution

  • AFAIK

    1. Second line with text at the right can be done with Row widget.
    2. Third line -> same
    3. Fouth line -> Column + Row below line with text and lorem block.

    It's easier to do it with rows, rather than calculate it with positioned. Is see overlapping only at 1st line, but it can also be done with Row\Column