效果图如下,就一个仿抖音个人主页的一串代码,如果大家有什么好的写法希望能分享一下,效果图如下。

dart
import 'package:flutter/material.dart';
import 'package:theme/app_theme.dart';
class UserDetailPage extends StatefulWidget {
const UserDetailPage({super.key});
@override
State<UserDetailPage> createState() => _UserDetailPageState();
}
class _UserDetailPageState extends State<UserDetailPage>
with SingleTickerProviderStateMixin {
//滑动监听
final ScrollController _scrollController = ScrollController();
// TabController (修改为3个标签页,可根据需要调整)
late TabController _tabController;
// 封面高度
static const double coverHeight = 140.0;
// appbar 透明度
double appBarOpacity = 0.0;
@override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
// 初始化TabController,length对应标签数量
_tabController = TabController(length: 3, vsync: this);
}
void _onScroll() {
setState(() {
double offset = _scrollController.offset;
if (offset < 0) {
appBarOpacity = 0.0;
} else if (offset < coverHeight) {
appBarOpacity = offset / coverHeight;
} else {
appBarOpacity = 1.0;
}
});
}
@override
void dispose() {
_scrollController.removeListener(_onScroll);
_scrollController.dispose();
_tabController.dispose(); // 释放TabController
super.dispose();
}
// 构建每个Tab对应的内容页面
Widget _buildTabViewContent(String tabName) {
return Container(
decoration: BoxDecoration(color: Colors.grey[200]),
padding: const EdgeInsets.all(20.0),
child: Text(
tabName,
style: const TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.white.withOpacity(appBarOpacity),
elevation: appBarOpacity > 0.1 ? 4.0 : 0.0,
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: appBarOpacity > 0.5 ? Colors.black : Colors.white,
),
onPressed: () => Navigator.pop(context),
),
),
body: CustomScrollView(
controller: _scrollController,
physics: const BouncingScrollPhysics(),
slivers: [
SliverAppBar(
expandedHeight: coverHeight,
snap: false,
// 滚动时固定在顶部
pinned: false,
floating: false,
// 下滑超过范围时拉伸(实现放大效果)
stretch: true,
// 拉伸时的回调(可选,用于自定义放大逻辑)
onStretchTrigger: () async {},
collapsedHeight: coverHeight,
// 背景透明度和阴影随滚动变化
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.none, // 关键:关闭渐隐
// 拉伸模式:向下滑动时图片从中心放大
stretchModes: const [StretchMode.zoomBackground],
// 背景图片
background: Stack(
fit: StackFit.expand,
children: [
// 背景图片(自动跟随StretchMode放大)
Image.network(
// 替换成你的图片地址
'https://picsum.photos/800/400',
fit: BoxFit.cover,
width: MediaQuery.of(context).size.width,
),
// 可选:添加渐变遮罩,让文字更清晰
const DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black],
),
),
),
// 头像 白色边框
Positioned(
left: 20,
bottom: 45,
child: SafeArea(
child: Row(
children: [
//头像增加白色边框2
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 2),
shape: BoxShape.circle,
),
child: CircleAvatar(
radius: 40,
backgroundColor: Colors.white,
backgroundImage: NetworkImage(
"https://picsum.photos/200/200",
),
),
),
const SizedBox(width: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"用户昵称",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
"用户简介",
style: TextStyle(
color: Colors.grey[400],
fontSize: 13,
),
),
],
),
],
),
),
),
],
),
),
backgroundColor: const Color.fromARGB(255, 255, 255, 255),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Container(
height: 20,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
),
),
),
SliverToBoxAdapter(
child: Container(
width: MediaQuery.of(context).size.width,
color: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 获赞、关注、粉丝数据
const Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'205.6万',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
Text(
' 获赞',
style: TextStyle(fontSize: 14, color: Colors.black),
),
SizedBox(width: 24),
Text(
'443',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
Text(
' 关注',
style: TextStyle(fontSize: 14, color: Colors.black),
),
SizedBox(width: 24),
Text(
'42.0万',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
Text(
' 粉丝',
style: TextStyle(fontSize: 14, color: Colors.black),
),
],
),
const SizedBox(height: 16), // IP地址和性别标签
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(4),
),
child: Text(
'IP: 河北',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
),
const SizedBox(width: 12),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(4),
),
child: Text(
'男',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
),
],
),
const SizedBox(height: 20),
// 关注按钮和分享按钮
Row(
children: [
Expanded(
child: Container(
height: 48,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text(
'+ 关注',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
),
const SizedBox(width: 12),
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey[300]!,
width: 1,
),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.share_outlined,
size: 20,
color: Colors.grey[600],
),
),
],
),
const SizedBox(height: 20), // 增加间距
// ========== 新增:TabBar ==========
TabBar(
controller: _tabController,
isScrollable: true,
tabAlignment: TabAlignment.start,
dividerColor: Colors.transparent,
// 怎么设置第一个标签左边的间距
labelPadding: const EdgeInsets.only(left: 0.0, right: 20.0),
tabs: const [
Tab(text: '作品'),
Tab(text: '动态'),
Tab(text: '收藏'),
],
),
],
),
),
),
// ========== 新增:TabBarView 内容区域 ==========
SliverFillRemaining(
child: TabBarView(
controller: _tabController,
// 三个Tab对应的内容
children: [
_buildTabViewContent('作品'),
_buildTabViewContent('动态'),
_buildTabViewContent('收藏'),
],
),
),
],
),
);
}
}
还有就是这种头像溢出效果怎么实现啊,sliverAppbar感觉就没有办法实现,他必须要固定高度,而且背景图还是铺开的.
