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

Flutter中如何从中心拉伸图片匹配内容高度?技术求助

Flutter实现图片中心拉伸适配ListView高度方案

需求说明

实现图片从中心区域拉伸的效果,使图片高度与ListView内容高度完全匹配。当ListView高度超过原图高度时,仅额外拉伸图片的中心区域(类似9Patch图片的拉伸逻辑),计划通过centerSlice属性完成。

示例效果

图片顶部和底部区域保持原始尺寸与样式不变,仅中间指定区域随ListView内容高度增加而拉伸,最终图片整体高度与ListView完全一致。

问题分析

当前代码通过GlobalKey获取ListView高度,但centerSlice参数未基于图片原始尺寸设置,无法实现预期的中心拉伸效果;同时使用固定延迟获取高度的方式存在渲染时机不准确的问题。

解决方案代码

import 'dart:ui' as ui;
import 'package:flutter/material.dart';

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

  @override
  State<StretchedBackgroundPage> createState() => _StretchedBackgroundPageState();
}

class _StretchedBackgroundPageState extends State<StretchedBackgroundPage> {
  double listViewHeight = 0.0;
  double originalImageHeight = 0.0;
  final GlobalKey listKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    // 预加载获取图片原始尺寸
    _getOriginalImageSize();
    // 渲染完成后获取ListView准确高度
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _updateListHeight();
    });
  }

  void _getOriginalImageSize() async {
    final imageProvider = AssetImage('images/homeBG.png');
    final imageStream = imageProvider.resolve(const ImageConfiguration());
    final ui.Image image = await imageStream.first;
    setState(() {
      originalImageHeight = image.height.toDouble();
    });
  }

  void _updateListHeight() {
    final renderBox = listKey.currentContext?.findRenderObject() as RenderBox?;
    if (renderBox != null) {
      setState(() {
        listViewHeight = renderBox.size.height;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Stack(
        children: [
          if (originalImageHeight > 0 && listViewHeight > 0)
            Container(
              width: MediaQuery.of(context).size.width,
              height: listViewHeight,
              decoration: BoxDecoration(
                image: DecorationImage(
                  alignment: Alignment.topCenter,
                  fit: BoxFit.fill,
                  // 定义垂直方向中心拉伸区域:水平全宽,垂直仅中间1px(可根据图片实际调整)
                  centerSlice: Rect.fromLTRB(
                    0,
                    originalImageHeight / 2,
                    MediaQuery.of(context).size.width,
                    originalImageHeight / 2 + 1,
                  ),
                  image: const AssetImage('images/homeBG.png'),
                ),
              ),
            ),
          _buildListView(),
        ],
      ),
    );
  }

  Widget _buildListView() {
    return ListView(
      key: listKey,
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      children: List.generate(12, (index) => _buildCell()),
    );
  }

  Widget _buildCell() {
    return const Padding(
      padding: EdgeInsets.all(20),
      child: SizedBox(height: 150),
    );
  }
}

关键实现说明

  • 图片原始尺寸获取:通过AssetImage解析图片流,获取图片真实高度,这是准确设置centerSlice的核心基础。
  • centerSlice参数设置:将垂直方向的拉伸区域限定在图片中心1px高度范围(可根据图片实际可拉伸区域调整),确保仅中间区域随ListView高度拉伸,上下部分保留原图样式。
  • 高度获取优化:使用WidgetsBinding.instance.addPostFrameCallback替代固定延迟,在Widget渲染完成后获取ListView高度,避免因渲染时机问题导致的高度偏差。

内容的提问来源于stack exchange,提问作者Anis Mansuri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 22:34:54