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

Flutter如何为商品卡片实现骨架加载器替代圆形进度指示器

替换FutureBuilder加载状态为商品卡片骨架加载器

方法一:自定义骨架加载组件

你可以手动实现模拟商品卡片结构的骨架组件,搭配渐变动画模拟加载效果:

  1. 先定义基础骨架结构(匹配你的商品卡片布局):
class ProductSkeleton extends StatelessWidget {
  const ProductSkeleton({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Colors.grey[200],
        borderRadius: BorderRadius.circular(8),
      ),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 商品图片占位
          Container(
            width: 80,
            height: 80,
            decoration: BoxDecoration(
              color: Colors.grey[300],
              borderRadius: BorderRadius.circular(8),
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // 商品名称占位
                Container(
                  height: 16,
                  width: double.infinity,
                  color: Colors.grey[300],
                  margin: const EdgeInsets.only(bottom: 8),
                ),
                // 商品价格占位
                Container(
                  height: 14,
                  width: 60,
                  color: Colors.grey[300],
                  margin: const EdgeInsets.only(bottom: 8),
                ),
                // 操作按钮占位
                Container(
                  height: 32,
                  width: 80,
                  color: Colors.grey[300],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
  1. 给骨架添加渐变动画(让加载效果更自然):
class AnimatedProductSkeleton extends StatefulWidget {
  const AnimatedProductSkeleton({super.key});

  @override
  State<AnimatedProductSkeleton> createState() => _AnimatedProductSkeletonState();
}

class _AnimatedProductSkeletonState extends State<AnimatedProductSkeleton> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    )..repeat(reverse: true);
    _animation = Tween<double>(begin: -1.0, end: 2.0).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      shaderCallback: (bounds) {
        return LinearGradient(
          begin: Alignment.centerLeft,
          end: Alignment.centerRight,
          colors: [Colors.grey[200]!, Colors.grey[300]!, Colors.grey[200]!],
          stops: const [0.0, 0.5, 1.0],
          transform: SlideTransform(_animation.value),
        ).createShader(bounds);
      },
      blendMode: BlendMode.srcATop,
      child: const ProductSkeleton(),
    );
  }
}

class SlideTransform extends GradientTransform {
  final double offset;

  SlideTransform(this.offset);

  @override
  Matrix4? transform(Rect bounds, {TextDirection? textDirection}) {
    return Matrix4.translationValues(offset * bounds.width, 0.0, 0.0);
  }
}
  1. 修改你的FutureBuilder代码,替换等待状态的组件:
FutureBuilder<List<Product>>(
  future: productFuture,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      // 返回多个骨架,模拟商品列表加载状态
      return ListView.builder(
        itemCount: 5,
        itemBuilder: (context, index) {
          return const AnimatedProductSkeleton();
        },
      );
    } else if (snapshot.hasData) {
      final catalog = snapshot.data;
      return buildCatalog(catalog!);
    } else {
      return const Text("No widget to build");
    }
  },
),

方法二:使用第三方库快速实现

用shimmer库可以快速实现骨架加载效果:

  1. 在pubspec.yaml添加依赖:
dependencies:
  shimmer: ^3.0.0
  1. 修改FutureBuilder代码:
import 'package:shimmer/shimmer.dart';

FutureBuilder<List<Product>>(
  future: productFuture,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return Shimmer.fromColors(
        baseColor: Colors.grey[300]!,
        highlightColor: Colors.grey[100]!,
        child: ListView.builder(
          itemCount: 5,
          itemBuilder: (context, index) {
            return const ProductSkeleton(); // 复用自定义的骨架结构
          },
        ),
      );
    } else if (snapshot.hasData) {
      final catalog = snapshot.data;
      return buildCatalog(catalog!);
    } else {
      return const Text("No widget to build");
    }
  },
),

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 03:15:31