Flutter 实现多个头像堆叠

由 Kerrinz 发布
  | 8 次浏览

本文起草稿于 2025-01-31

实现效果是这样的:

https://www.jianshu.com/p/79698e47f1e6

实现方法1 —— FittedBox

原本使用小尺寸的 SizeBox 包裹住大尺寸的元素时,元素的宽高会变成跟随小尺寸 SizeBox 的宽高,
在中间夹一层 FittedBox(fit: BoxFit.none) 来避免元素宽高变化。

示例代码(可直接运行):

import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;
  const MyHomePage({super.key, required this.title});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      padding: const EdgeInsets.all(16.0),
      // reverse: true, // 开启 reverse 可以使用相反的堆叠层级,但同时排列顺序也会反转
      scrollDirection: Axis.horizontal,
      itemBuilder: (context, index) {
        return SizedBox(
          width: 32 - 8,
          height: 32 - 8,
          child: FittedBox(
            fit: BoxFit.none,
            child: SizedBox(
              width: 32,
              height: 32,
              child: itemBuilder(context, index),
            ),
          ),
        );
      },
      itemCount: 4,
    );
  }

  Widget itemBuilder(BuildContext context, int index) {
    // 用纯色模拟图片
    return Container(
      width: 32,
      height: 32,
      decoration: BoxDecoration(
        color: [Colors.red, Colors.blue, Colors.green, Colors.yellow][index],
        borderRadius: const BorderRadius.all(Radius.circular(50.0)),
      ),
    );
  }
}

运行结果:

2025-12-28T14:57:12.png

缺点:
重叠部分触发点击事件的层级是错的,例如点击红色和蓝色元素重叠部分,理应触发蓝色元素的点击事件,实际触发的确是红色元素;不过,在使用了 reverse: true 进行反转时,触发点击事件的层级就是正确的了。

实现方法2——Stack + Position

很普通的绝对定位,通过设置 clipBehavior: Clip.none 即可允许元素溢出。

示例代码,可直接运行:

import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.blue),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 120,
      height: 60,
      child: Stack(
        clipBehavior: Clip.none, // 允许子部件溢出
        children: List.generate(colors.length, (index) {
          return Positioned(
            left: index * 30.0, // 在这调节重叠区域大小
            child: CircleAvatar(
              radius: 30,
              backgroundColor: [Colors.blue,Colors.green,Colors.orange,Colors.purple][index],
            ),
          );
        }),
      ),
    );
  }
}

效果:
2025-12-31T15:59:14.png

想反转堆叠的话 Positioned 内的 left 改成 right 即可;

实现方法3——OverflowBox

示例代码,可直接运行:

import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.blue),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        for (int i = 0; i < 4; i++)
          Align(
            alignment: Alignment.centerLeft,
            child: Container(
              margin: EdgeInsets.only(left: i * 50.0), // 调整重叠部分大小
              child: OverflowBox(
                maxWidth: 100, // 限制最大宽度
                child: CircleAvatar(
                  radius: 25,
                  backgroundColor: [Colors.blue,Colors.green,Colors.orange,Colors.purple][i],
                ),
              ),
            ),
          ),
      ],
    );
  }
}

参考:
https://www.jianshu.com/p/79698e47f1e6
https://blog.csdn.net/m0_55635384/article/details/128735969


版权属于: Kerrinz
本文链接:https://kerrinz.com/archives/679.html
作品采用《知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议》进行许可,转载请务必注明出处!

暂无评论

发表评论