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

Flutter卡片样式调整:实现1/3白背景、1/3模糊、剩余原图效果

Flutter卡片实现1/3原图+1/3模糊+1/3白色信息栏

当前效果

当前效果

当前卡片的模糊效果覆盖了整个图片区域,而非仅中间1/3部分。

预期效果

预期效果

需要实现顶部1/3为原图、中间1/3为模糊效果、底部1/3为白色信息栏的卡片样式。

现有代码

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:trailx_app/const/colors.dart';

class FypHikingCards extends StatefulWidget {
  const FypHikingCards({super.key});
  @override
  State<FypHikingCards> createState() => _FypHikingCards();
}

class _FypHikingCards extends State<FypHikingCards> {
  final List<String> images =
      List<String>.generate(8, (index) => "Item $index");
  final List<String> imagePaths = [
    'assets/images/hiking_0.png',
    'assets/images/hiking_1.png',
    'assets/images/hiking_2.png',
    'assets/images/hiking_3.png',
    'assets/images/hiking_4.png',
    'assets/images/hiking_5.png',
    'assets/images/hiking_6.png',
    'assets/images/hiking_7.png'
  ];
  final List<bool> liked = List<bool>.filled(8, false);

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        crossAxisSpacing: 2.0,
        mainAxisSpacing: 2.0,
        childAspectRatio: 2 / 2.2,
      ),
      itemCount: 8,
      itemBuilder: (context, index) {
        return Center(
          child: Stack(
            children: [
              Card(
                clipBehavior: Clip.antiAliasWithSaveLayer,
                elevation: 2,
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    // This part holds the image with the blur at the bottom 1/3
                    Expanded(
                      flex: 2,
                      child: Stack(
                        children: [
                          // Full image in the background
                          Positioned.fill(
                            child: Image.asset(
                              imagePaths[index],
                              fit: BoxFit.fill,
                              width: double.infinity,
                            ),
                          ),
                          // Blur the bottom part of the image with a fixed height
                          Positioned(
                            bottom: 0,
                            left: 0,
                            right: 0,
                            child: BackdropFilter(
                              filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
                              child: Container(
                                color: Colors.black.withOpacity(
                                    0), // Transparent container for blur
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),
                    // Text information in the white box
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: [
                          SizedBox(
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Text(
                                  "Length",
                                  style: GoogleFonts.getFont('Inter',
                                      color: Colors.black,
                                      fontSize: 8,
                                      fontWeight: FontWeight.w500),
                                ),
                                Text(
                                  "1.69 Mi",
                                  style: GoogleFonts.getFont('Inter',
                                      color: loginGreenColor,
                                      fontSize: 12,
                                      fontWeight: FontWeight.w600),
                                ),
                                Text(
                                  "Duration",
                                  style: GoogleFonts.getFont('Inter',
                                      color: Colors.black,
                                      fontSize: 8,
                                      fontWeight: FontWeight.w500),
                                ),
                                Text(
                                  "1.5 Hr",
                                  style: GoogleFonts.getFont('Inter',
                                      color: loginGreenColor,
                                      fontSize: 12,
                                      fontWeight: FontWeight.w600),
                                ),
                              ],
                            ),
                          ),
                          SizedBox(
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Text(
                                  "Distance",
                                  style: GoogleFonts.getFont('Inter',
                                      color: Colors.black,
                                      fontSize: 8,
                                      fontWeight: FontWeight.w500),
                                ),
                                Text(
                                  "6,321 Ft",
                                  style: GoogleFonts.getFont('Inter',
                                      color: loginGreenColor,
                                      fontSize: 12,
                                      fontWeight: FontWeight.w600),
                                ),
                                Text(
                                  "Difficulty",
                                  style: GoogleFonts.getFont('Inter',
                                      color: Colors.black,
                                      fontSize: 8,
                                      fontWeight: FontWeight.w500),
                                ),
                                Text(
                                  "Medium",
                                  style: GoogleFonts.getFont('Inter',
                                      color: loginGreenColor,
                                      fontSize: 12,
                                      fontWeight: FontWeight.w600),
                                ),
                              ],
                            ),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
              Positioned(
                top: 0,
                right: 0,
                child: IconButton(
                  color: likedColor,
                  icon: liked[index]
                      ? const Icon(Icons.favorite)
                      : const Icon(
                          Icons.favorite_border,
                        ),
                  onPressed: () {
                    setState(() {
                      liked[index] = !liked[index];
                    });
                  },
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

解决方案

问题出在模糊层没有限制高度,默认铺满了整个图片区域。我们需要将模糊层的高度限制为图片区域的1/2(因为图片区域占卡片总高度的2/3,这样模糊层刚好占卡片的1/3),同时调整位置让它覆盖图片的下半部分(即卡片的中间1/3)。

修改后的核心代码片段:

Expanded(
  flex: 2,
  child: LayoutBuilder(
    builder: (context, constraints) {
      final imageHeight = constraints.maxHeight;
      final blurHeight = imageHeight / 2; // 模糊层高度为图片区域的1/2
      return Stack(
        children: [
          Positioned.fill(
            child: Image.asset(
              imagePaths[index],
              fit: BoxFit.fill,
              width: double.infinity,
            ),
          ),
          // 模糊层只覆盖图片下半部分
          Positioned(
            top: blurHeight, // 从图片中间位置开始
            left: 0,
            right: 0,
            height: blurHeight, // 限制模糊层高度
            child: BackdropFilter(
              filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
              child: Container(
                color: Colors.black.withOpacity(0.1), // 可加轻微透明度增强效果
              ),
            ),
          ),
        ],
      );
    },
  ),
),

这样调整后,模糊层就只会覆盖图片区域的下半部分,刚好对应卡片的中间1/3区域,顶部1/3是清晰原图,底部1/3是白色信息栏,完全符合预期效果。

内容的提问来源于stack exchange,提问作者Liu Terry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 00:32:02