You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter技术问题:如何实现组件间响应式曲线路径避免缩放重叠

Flutter 故事时间线曲线路径适配问题

问题概述

我需要在Flutter项目中实现组件间的故事时间线曲线路径,目前已成功绘制曲线路径,但页面缩放(尺寸变化)时,路径会移位并与组件重叠。

效果说明

  • 目标效果:曲线路径始终精准连接对应组件,页面缩放/尺寸变化时保持相对位置,不会出现错位或重叠
  • 当前效果:曲线路径已绘制完成,但页面缩放时会偏离预设位置,与相邻组件重叠
  • 缩放问题:调整页面尺寸后,路径的位置和比例无法适配新的页面布局,导致与组件错位

已尝试方案

  • 使用Figma导出的图片做定位,同样出现缩放移位问题
  • 更倾向于代码实现曲线路径,方便后续修改维护

当前实现代码

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),
    ];

    //* 曾尝试用定位图片,但同样会移位打乱布局
    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: 将新路径整理到列表中优化代码
            //* 第一条路径
            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(),
              ),
            ),
            //* 第二条路径
            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),
          ],
        ),
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Luca Rughani Jeevanjee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 14:12:06