2026年1月

这个典故出自《三国演义》,不是正史,我们就按照演义的思路来看下这个问题。


这件事的源头是大将军何进想诛杀十常侍,就和司隶校尉袁绍密谋召边兵入京来干这件事。


没想到事情泄露了,十常侍把何进骗进宫杀掉,袁绍和曹操则率兵闯进宫把十常侍杀了个干净。


按理说,大将军何进统帅在京全部兵马,袁绍这个司隶校尉手下也有几千精兵,杀几个宦官应该不成问题,实在弄不明白俩人为啥号召边兵入京。


何进和袁绍召的边兵就是西凉董卓,洛阳兵变时董卓还在进京的路上,并不知情。


进到洛阳后,才发现大将军何进已死,京中诸将群龙无首,形同一盘散沙。不知道是因为袁绍资历过浅还是因为啥,他没有替代何进的位置。


董卓率大军进京后,填补了权力真空,兼并了何进手下诸军,成了洛阳的实权人物。


董卓无谋,掌握大权后,第一件事不是安抚士民,反而是妄议废长立幼。


在董卓主持的朝会上,董卓宣布废少帝刘辩为弘农王,改立陈留王刘协为帝。


董卓宣布完后,满朝文武重臣无人敢反对,此时高光时刻来了,袁绍愤然起身,大骂董卓是个篡逆之辈。(在正史之中是卢植站起来反对董卓。)


董卓被怒喷一顿,气的七窍生烟,跳起来拔剑说道:“尔要试试我宝剑是否锋利吗?”


袁绍也不甘示弱,拔剑回道:“我剑也未尝不利!”

背景

最近有个小需求需要点击标题展示/折叠文字,并且标题后面还跟着一个svg的下拉图标。大概的效果如下:

我记得之前是有人在掘金上分享如何实现这个下拉图标动画的(貌似不是基于svg?),但我找不到了。。。


一开始,我先去element-plus和ant-design上粗略地看了看有没有类似的下拉图标效果。但我发现貌似就element-plus的select组件上用到了下拉图标旋转的过度动画,和上面的gif预览还有点差异。


因此,我就自己稍微研究了下svg的动画,供自己学习和大家参考~

绘制svg

画一个静态下拉图标

首先,先把默认的图标用svg画出来,代码如下





import 'dart:ui';

import 'package:flutter/material.dart';

import 'package:flutter/services.dart';

import 'package:get/get.dart';

import 'package:url_launcher/url_launcher.dart';

import '../controllers/userprofile_controller.dart';

import '../../../../utility/theme_controller.dart';

import '../../../../utility/custom_fonts.dart';

import '../../../../utility/screen_util_config.dart';


class UserprofileView extends GetView<UserprofileController> {

const UserprofileView({super.key});


@override

Widget build(BuildContext context) {

final themeController = Get.find<ThemeController>();

final customFonts = Get.find<CustomFonts>();

// 🔥 初始设置状态栏为透明(显示背景图)

SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(

statusBarColor: Colors.transparent, // 🔥 初始透明

statusBarIconBrightness: Brightness.light, // 🔥 透明背景用浅色图标

statusBarBrightness: Brightness.dark,

));

// 🔥 关键:使用 ScrollController 和 ValueNotifier 来精确控制

final ScrollController scrollController = ScrollController();

final ValueNotifier<bool> isCollapsed = ValueNotifier<bool>(false);


// 🔥 监听滚动位置 - 动态切换状态栏颜色

scrollController.addListener(() {

final bool shouldCollapse = scrollController.hasClients &&

scrollController.offset > ScreenUtilConfig.setHeight(280);

if (isCollapsed.value != shouldCollapse) {

isCollapsed.value = shouldCollapse;

// 🔥 根据吸顶状态动态设置状态栏颜色

SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(

statusBarColor: shouldCollapse

? themeController.backgroundColor // 🔥 吸顶时用主题色

: Colors.transparent, // 🔥 未吸顶时透明

statusBarIconBrightness: shouldCollapse

? (themeController.isDarkMode.value ? Brightness.light : Brightness.dark) // 🔥 主题色时根据主题调整

: Brightness.light, // 🔥 透明背景时用浅色图标

statusBarBrightness: shouldCollapse

? (themeController.isDarkMode.value ? Brightness.dark : Brightness.light)

: Brightness.dark,

));

}

});


// 🔥 动态 AnnotatedRegion - 根据状态切换

return ValueListenableBuilder<bool>(

valueListenable: isCollapsed,

builder: (context, collapsed, child) {

return AnnotatedRegion<SystemUiOverlayStyle>(

value: SystemUiOverlayStyle(

statusBarColor: collapsed

? themeController.backgroundColor // 🔥 吸顶时主题色

: Colors.transparent, // 🔥 未吸顶时透明

statusBarIconBrightness: collapsed

? (themeController.isDarkMode.value ? Brightness.light : Brightness.dark)

: Brightness.light, // 🔥 透明时用浅色图标

statusBarBrightness: collapsed

? (themeController.isDarkMode.value ? Brightness.dark : Brightness.light)

: Brightness.dark,

),

child: DefaultTabController(

length: 3,

child: Scaffold(

backgroundColor: themeController.backgroundColor,

body: Obx(() {

// 🔥 每次重建时也根据状态设置状态栏

WidgetsBinding.instance.addPostFrameCallback((_) {

SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(

statusBarColor: collapsed

? themeController.backgroundColor

: Colors.transparent,

statusBarIconBrightness: collapsed

? (themeController.isDarkMode.value ? Brightness.light : Brightness.dark)

: Brightness.light,

statusBarBrightness: collapsed

? (themeController.isDarkMode.value ? Brightness.dark : Brightness.light)

: Brightness.dark,

));

});


if (controller.isLoading.value) {

return const Center(child: CircularProgressIndicator());

}


if (controller.hasError.value) {

return Center(

child: Column(

mainAxisAlignment: MainAxisAlignment.center,

children: [

Text(

controller.errorMessage.value,

style: customFonts.bodyMedium,

),

SizedBox(height: ScreenUtilConfig.setHeight(16)),

ElevatedButton(

onPressed: () => controller.refreshAllData(),

child: Text(

'重试',

style: customFonts.buttonTextStyle,

),

),

],

),

);

}


return CustomScrollView(

controller: scrollController,

slivers: [

ValueListenableBuilder<bool>(

如果您看到这篇文章,表示您的 blog 已经安装成功.

如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.
如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {
class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {如果您看到这篇文章,表示您的 blog 已经安装成功.

class UserprofileView extends GetView {

const UserprofileView({super.key});

@override

Widget build(BuildContext context) {
class UserprofileView extends GetView {
const UserprofileView({super.key});

@override
Widget build(BuildContext context) {

final themeController = Get.find<ThemeController>();
final customFonts = Get.find<CustomFonts>();

// 🔥 初始设置状态栏为透明(显示背景图)
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
  statusBarColor: Colors.transparent, // 🔥 初始透明
  statusBarIconBrightness: Brightness.light, // 🔥 透明背景用浅色图标
  statusBarBrightness: Brightness.dark,
));

// 🔥 关键:使用 ScrollController 和 ValueNotifier 来精确控制
final ScrollController scrollController = ScrollController();
final ValueNotifier<bool> isCollapsed = ValueNotifier<bool>(false);

// 🔥 监听滚动位置 - 动态切换状态栏颜色
scrollController.addListener(() {
  final bool shouldCollapse = scrollController.hasClients &&
      scrollController.offset > ScreenUtilConfig.setHeight(280);
  if (isCollapsed.value != shouldCollapse) {
    isCollapsed.value = shouldCollapse;

    // 🔥 根据吸顶状态动态设置状态栏颜色
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
      statusBarColor: shouldCollapse
          ? themeController.backgroundColor  // 🔥 吸顶时用主题色
          : Colors.transparent,              // 🔥 未吸顶时透明
      statusBarIconBrightness: shouldCollapse
          ? (themeController.isDarkMode.value ? Brightness.light : Brightness.dark) // 🔥 主题色时根据主题调整
          : Brightness.light, // 🔥 透明背景时用浅色图标
      statusBarBrightness: shouldCollapse
          ? (themeController.isDarkMode.value ? Brightness.dark : Brightness.light)
          : Brightness.dark,
    ));
  }
});

// 🔥 动态 AnnotatedRegion - 根据状态切换
return ValueListenableBuilder<bool>(
  valueListenable: isCollapsed,
  builder: (context, collapsed, child) {
    return AnnotatedRegion<SystemUiOverlayStyle>(
      value: SystemUiOverlayStyle(
        statusBarColor: collapsed
            ? themeController.backgroundColor  // 🔥 吸顶时主题色
            : Colors.transparent,              // 🔥 未吸顶时透明
        statusBarIconBrightness: collapsed
            ? (themeController.isDarkMode.value ? Brightness.light : Brightness.dark)
            : Brightness.light, // 🔥 透明时用浅色图标
        statusBarBrightness: collapsed
            ? (themeController.isDarkMode.value ? Brightness.dark : Brightness.light)
            : Brightness.dark,
      ),
      child: DefaultTabController(
        length: 3,
        child: Scaffold(
          backgroundColor: themeController.backgroundColor,
          body: Obx(() {
            // 🔥 每次重建时也根据状态设置状态栏
            WidgetsBinding.instance.addPostFrameCallback((_) {
              SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
                statusBarColor: collapsed
                    ? themeController.backgroundColor
                    : Colors.transparent,
                statusBarIconBrightness: collapsed
                    ? (themeController.isDarkMode.value ? Brightness.light : Brightness.dark)
                    : Brightness.light,
                statusBarBrightness: collapsed
                    ? (themeController.isDarkMode.value ? Brightness.dark : Brightness.light)
                    : Brightness.dark,
              ));
            });

            if (controller.isLoading.value) {
              return const Center(child: CircularProgressIndicator());
            }

            if (controller.hasError.value) {
              return Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(
                      controller.errorMessage.value,
                      style: customFonts.bodyMedium,
                    ),
                    SizedBox(height: ScreenUtilConfig.setHeight(16)),
                    ElevatedButton(
                      onPressed: () => controller.refreshAllData(),
                      child: Text(
                        '重试',
                        style: customFonts.buttonTextStyle,
                      ),
                    ),
                  ],
                ),
              );
            }

            return CustomScrollView(
              controller: scrollController,
              slivers: [
                ValueListenableBuilder<bool>(
                  valueListenable: isCollapsed,
                  builder: (context, collapsed, child) {
                    return SliverAppBar(
                      expandedHeight: ScreenUtilConfig.setHeight(380),
                      pinned: true,
                      floating: false,
                      snap: false,
                      backgroundColor: collapsed
                          ? themeController.backgroundColor  // 🔥 吸顶时主题色
                          : Colors.transparent,             // 🔥 未吸顶时透明
                      elevation: 0,
                      // 🔥 动态设置 SliverAppBar 的状态栏样式
                      systemOverlayStyle: SystemUiOverlayStyle(
                        statusBarColor: collapsed
                            ? themeController.backgroundColor
                            : Colors.transparent,
                        statusBarIconBrightness: collapsed
                            ? (themeController.isDarkMode.value ? Brightness.light : Brightness.dark)
                            : Brightness.light,
                        statusBarBrightness: collapsed
                            ? (themeController.isDarkMode.value ? Brightness.dark : Brightness.light)
                            : Brightness.dark,
                      ),

                      title: collapsed ? Row(
                        children: [
                          Container(
                            width: 28,
                            height: 28,
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(14),
                              border: Border.all(
                                  color: themeController.primaryTextColor.withOpacity(0.2),
                                  width: 1
                              ),
                              image: DecorationImage(
                                image: NetworkImage(
                                  controller.userAvatar.isNotEmpty
                                      ? controller.userAvatar
                                      : 'https://via.placeholder.com/28',
                                ),
                                fit: BoxFit.cover,
                              ),
                            ),
                          ),
                          SizedBox(width: ScreenUtilConfig.setWidth(8)),
                          Expanded(
                            child: Text(
                              controller.userName,
                              style: customFonts.bodyLarge.copyWith(
                                color: themeController.primaryTextColor,
                                fontWeight: FontWeight.w600,
                                fontSize: 16,
                              ),
                              overflow: TextOverflow.ellipsis,
                              maxLines: 1,
                            ),
                          ),
                        ],
                      ) : null,
                      leading: IconButton(
                        icon: Icon(
                          Icons.arrow_back_ios,
                          color: collapsed
                              ? themeController.primaryTextColor  // 🔥 吸顶时主题色图标
                              : Colors.white,                     // 🔥 透明时白色图标
                          size: ScreenUtilConfig.setWidth(20),
                        ),
                        onPressed: () => Get.back(),
                      ),
                      actions: [
                        IconButton(
                          icon: Icon(
                            controller.isOwnProfile ? Icons.settings : Icons.more_horiz,
                            color: collapsed
                                ? themeController.primaryTextColor  // 🔥 吸顶时主题色图标
                                : Colors.white,                     // 🔥 透明时白色图标
                            size: ScreenUtilConfig.setWidth(24),
                          ),
                          onPressed: () {
                            if (controller.isOwnProfile) {
                              Get.toNamed('/profile-settings');
                            } else {
                              _showMoreOptions(themeController, customFonts);
                            }
                          },
                        ),
                      ],
                      flexibleSpace: FlexibleSpaceBar(
                        background: _buildUserProfileBackground(themeController, customFonts),
                      ),
                    );
                  },
                ),

                SliverPersistentHeader(
                  pinned: true,
                  delegate: _TabBarDelegate(
                    child: _buildTabBar(themeController, customFonts),
                  ),
                ),

                _buildTabContent(themeController, customFonts),
              ],
            );
          }),
        ),
      ),
    );
  },
);

}

/// 🔥 构建用户资料背景
Widget _buildUserProfileBackground(ThemeController themeController, CustomFonts customFonts) {

return Stack(
  children: [
    _buildBackgroundImage(),
    Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: [
            Colors.black.withOpacity(0.1),
            Colors.black.withOpacity(0.4),
          ],
        ),
      ),
    ),
    Positioned(
      bottom: ScreenUtilConfig.setHeight(0),
      left: 0,
      right: 0,
      child: Container(
        decoration: BoxDecoration(
          color: themeController.backgroundColor,
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(ScreenUtilConfig.setRadius(20)),
            topRight: Radius.circular(ScreenUtilConfig.setRadius(20)),
          ),
        ),
        child: _buildUserProfileContent(themeController, customFonts),
      ),
    ),
  ],
);

}

Widget _buildBackgroundImage() {

final backgroundImage = _getUserBackgroundImage();
final avatarImage = controller.userAvatar;

if (backgroundImage.isNotEmpty) {
  return Positioned.fill(
    child: Image.network(
      backgroundImage,
      fit: BoxFit.cover,
      errorBuilder: (context, error, stackTrace) {
        return _buildBlurredAvatarBackground(avatarImage);
      },
    ),
  );
} else {
  return _buildBlurredAvatarBackground(avatarImage);
}

}

Widget _buildBlurredAvatarBackground(String avatarUrl) {

if (avatarUrl.isEmpty) {
  return Container(
    decoration: BoxDecoration(
      gradient: LinearGradient(
        begin: Alignment.topCenter,
        end: Alignment.bottomCenter,
        colors: [
          Color(0xFF6B9BD1),
          Color(0xFF4A7BA7),
        ],
      ),
    ),
  );
}

return Stack(
  children: [
    Positioned.fill(
      child: Image.network(
        avatarUrl,
        fit: BoxFit.cover,
        errorBuilder: (context, error, stackTrace) {
          return Container(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment.topCenter,
                end: Alignment.bottomCenter,
                colors: [
                  Color(0xFF6B9BD1),
                  Color(0xFF4A7BA7),
                ],
              ),
            ),
          );
        },
      ),
    ),
    Positioned.fill(
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
        child: Container(
          color: Colors.black.withOpacity(0.1),
        ),
      ),
    ),
  ],
);

}

Widget _buildUserProfileContent(ThemeController themeController, CustomFonts customFonts) {

return Container(
  padding: EdgeInsets.fromLTRB(
    ScreenUtilConfig.setWidth(20),
    ScreenUtilConfig.setHeight(20),
    ScreenUtilConfig.setWidth(20),
    ScreenUtilConfig.setHeight(30),
  ),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            width: ScreenUtilConfig.setWidth(80),
            height: ScreenUtilConfig.setWidth(80),
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(ScreenUtilConfig.setRadius(16)),
              border: Border.all(color: Colors.white, width: 3),
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withOpacity(0.1),
                  blurRadius: 8,
                  offset: const Offset(0, 2),
                ),
              ],
              image: DecorationImage(
                image: NetworkImage(
                  controller.userAvatar.isNotEmpty
                      ? controller.userAvatar
                      : 'https://via.placeholder.com/80',
                ),
                fit: BoxFit.cover,
              ),
            ),
          ),
          SizedBox(width: ScreenUtilConfig.setWidth(16)),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  children: [
                    Expanded(
                      child: Text(
                        controller.userName,
                        style: customFonts.largeTitleStyle.copyWith(
                          color: themeController.primaryTextColor,
                          fontWeight: FontWeight.bold,
                          fontSize: 20,
                        ),
                        overflow: TextOverflow.ellipsis,
                        maxLines: 1,
                      ),
                    ),
                    _buildUserGroupBadge(themeController, customFonts),
                    _buildVipBadge(themeController, customFonts),
                  ],
                ),
                SizedBox(height: ScreenUtilConfig.setHeight(8)),
                Row(
                  children: [
                    Obx(() => _buildActionButton(themeController, customFonts)),
                    SizedBox(width: ScreenUtilConfig.setWidth(8)),
                    if (!controller.isOwnProfile)
                      _buildMessageButton(themeController, customFonts),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
      SizedBox(height: ScreenUtilConfig.setHeight(20)),
      Row(
        children: [
          _buildStatItemTapTap(
            _formatNumberWithK(_getFollowersCount()),
            '粉丝',
            customFonts,
            themeController,
          ),
          SizedBox(width: ScreenUtilConfig.setWidth(40)),
          _buildStatItemTapTap(
            _formatNumberWithK(_getArticlesCount()),
            '文章',
            customFonts,
            themeController,
          ),
          SizedBox(width: ScreenUtilConfig.setWidth(40)),
          _buildStatItemTapTap(
            _formatNumberWithK(_getCommentsCount()),
            '评论',
            customFonts,
            themeController,
          ),
        ],
      ),
      SizedBox(height: ScreenUtilConfig.setHeight(16)),
      _buildUserDetailInfo(themeController, customFonts),
      if (controller.userIntroduce.isNotEmpty) ...[
        SizedBox(height: ScreenUtilConfig.setHeight(16)),
        Text(
          controller.userIntroduce,
          style: customFonts.bodyMedium.copyWith(
            color: themeController.primaryTextColor,
            height: 1.4,
          ),
          maxLines: 3,
          overflow: TextOverflow.ellipsis,
        ),
      ],
    ],
  ),
);

}

Widget _buildUserDetailInfo(ThemeController themeController, CustomFonts customFonts) {

final joinDays = _calculateJoinDays();
final location = _getUserLocation();
final region = _getUserRegion();

return Text(
  '加入 TapTap $joinDays 天${location.isNotEmpty ? ' | IP: ${region.isNotEmpty ? region : location}' : ''}${controller.userId.isNotEmpty ? ' | ID: ${controller.userId}' : ''}',
  style: customFonts.bodySmall.copyWith(
    color: themeController.secondaryTextColor,
    fontSize: 12,
  ),
);

}

Widget _buildStatItemTapTap(String value, String label, CustomFonts customFonts, ThemeController themeController) {

return Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text(
      value,
      style: customFonts.largeTitleStyle.copyWith(
        color: themeController.primaryTextColor,
        fontWeight: FontWeight.bold,
        fontSize: 24,
      ),
    ),
    SizedBox(height: ScreenUtilConfig.setHeight(2)),
    Text(
      label,
      style: customFonts.bodySmall.copyWith(
        color: themeController.secondaryTextColor,
        fontSize: 12,
      ),
    ),
  ],
);

}

Widget _buildActionButton(ThemeController themeController, CustomFonts customFonts) {

if (controller.isOwnProfile) {
  return GestureDetector(
    onTap: () => Get.toNamed('/profile-edit'),
    child: Container(
      padding: EdgeInsets.symmetric(
        horizontal: ScreenUtilConfig.setWidth(16),
        vertical: ScreenUtilConfig.setHeight(8),
      ),
      decoration: BoxDecoration(
        color: Colors.grey[200],
        borderRadius: BorderRadius.circular(ScreenUtilConfig.setRadius(20)),
      ),
      child: Text(
        '编辑资料',
        style: customFonts.bodyMedium.copyWith(
          color: Colors.black87,
          fontWeight: FontWeight.w500,
          fontSize: 14,
        ),
      ),
    ),
  );
} else {
  return GestureDetector(
    onTap: controller.toggleFollow,
    child: Container(
      padding: EdgeInsets.symmetric(
        horizontal: ScreenUtilConfig.setWidth(16),
        vertical: ScreenUtilConfig.setHeight(8),
      ),
      decoration: BoxDecoration(
        color: controller.isFollowing.value ? Colors.grey[200] : Colors.grey[200],
        borderRadius: BorderRadius.circular(ScreenUtilConfig.setRadius(20)),
      ),
      child: Text(
        controller.isFollowing.value ? '已关注' : '关注',
        style: customFonts.bodyMedium.copyWith(
          color: Colors.black87,
          fontWeight: FontWeight.w500,
          fontSize: 14,
        ),
      ),
    ),
  );
}

}

Widget _buildMessageButton(ThemeController themeController, CustomFonts customFonts) {

return GestureDetector(
  onTap: () {
    Get.toNamed('/chat', arguments: {
      'userId': controller.userId,
      'userName': controller.userName,
    });
  },
  child: Container(
    padding: EdgeInsets.all(ScreenUtilConfig.setWidth(8)),
    decoration: BoxDecoration(
      color: Colors.grey[200],
      borderRadius: BorderRadius.circular(ScreenUtilConfig.setRadius(20)),
    ),
    child: Icon(
      Icons.message_outlined,
      size: ScreenUtilConfig.setWidth(18),
      color: Colors.black87,
    ),
  ),
);

}

void _showMoreOptions(ThemeController themeController, CustomFonts customFonts) {

Get.bottomSheet(
  Container(
    padding: EdgeInsets.all(ScreenUtilConfig.setWidth(20)),
    decoration: BoxDecoration(
      color: themeController.cardBackgroundColor,
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(ScreenUtilConfig.setRadius(20)),
        topRight: Radius.circular(ScreenUtilConfig.setRadius(20)),
      ),
    ),
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        ListTile(
          leading: Icon(Icons.report, color: Colors.orange),
          title: Text('举报用户', style: customFonts.bodyLarge),
          onTap: () {
            Get.back();
            _reportUser();
          },
        ),
        ListTile(
          leading: Icon(Icons.block, color: Colors.red),
          title: Text('拉黑用户', style: customFonts.bodyLarge),
          onTap: () {
            Get.back();
            _blockUser();
          },
        ),
        ListTile(
          leading: Icon(Icons.share, color: themeController.primaryTextColor),
          title: Text('分享主页', style: customFonts.bodyLarge),
          onTap: () {
            Get.back();
            _shareProfile();
          },
        ),
      ],
    ),
  ),
);

}

Widget _buildUserGroupBadge(ThemeController themeController, CustomFonts customFonts) {

final userGroup = _getUserGroup();
if (userGroup.isEmpty) return const SizedBox.shrink();

Color badgeColor;
String displayText;

switch (userGroup.toLowerCase()) {
  case 'administrator':
    badgeColor = Colors.red;
    displayText = '管理员';
    break;
  case 'editor':
    badgeColor = Colors.orange;
    displayText = '编辑';
    break;
  case 'contributor':
    badgeColor = Colors.green;
    displayText = '贡献者';
    break;
  default:
    badgeColor = Colors.grey;
    displayText = '用户';
}

return Container(
  margin: EdgeInsets.only(left: ScreenUtilConfig.setWidth(4)),
  padding: EdgeInsets.symmetric(
    horizontal: ScreenUtilConfig.setWidth(6),
    vertical: ScreenUtilConfig.setHeight(2),
  ),
  decoration: BoxDecoration(
    color: badgeColor,
    borderRadius: BorderRadius.circular(ScreenUtilConfig.setRadius(4)),
  ),
  child: Text(
    displayText,
    style: customFonts.bodySmall.copyWith(
      color: Colors.white,
      fontSize: 10,
    ),
  ),
);

}

Widget _buildVipBadge(ThemeController themeController, CustomFonts customFonts) {

if (!_isVip()) return const SizedBox.shrink();

return Container(
  margin: EdgeInsets.only(left: ScreenUtilConfig.setWidth(4)),
  padding: EdgeInsets.symmetric(
    horizontal: ScreenUtilConfig.setWidth(6),
    vertical: ScreenUtilConfig.setHeight(2),
  ),
  decoration: BoxDecoration(
    gradient: const LinearGradient(
      colors: [Colors.amber, Colors.orange],
    ),
    borderRadius: BorderRadius.circular(ScreenUtilConfig.setRadius(4)),
  ),
  child: Text(
    'VIP',
    style: customFonts.bodySmall.copyWith(
      color: Colors.white,
      fontSize: 10,
      fontWeight: FontWeight.bold,
    ),
  ),
);

}

Widget _buildTabBar(ThemeController themeController, CustomFonts customFonts) {

return Container(
  color: themeController.backgroundColor,
  child: TabBar(
    labelColor: themeController.primaryTextColor,
    unselectedLabelColor: themeController.primaryTextColor.withOpacity(0.6),
    labelStyle: customFonts.tabSelectedTitleStyle,
    unselectedLabelStyle: customFonts.tabUnselectedTitleStyle,
    indicatorColor: const Color(0xFF4A90E2),
    indicatorWeight: 3,
    tabs: const [
      Tab(text: '动态'),
      Tab(text: '点评'),
      Tab(text: '收藏'),
    ],
  ),
);

}

Widget _buildTabContent(ThemeController themeController, CustomFonts customFonts) {

return SliverFillRemaining(
  child: TabBarView(
    children: [
      _buildDynamicsTab(themeController, customFonts),
      _buildArticlesTab(themeController, customFonts),
      _buildCollectionsTab(themeController, customFonts),
    ],
  ),
);

}

Widget _buildDynamicsTab(ThemeController themeController, CustomFonts customFonts) {

return Obx(() {
  if (controller.dynamics.isEmpty) {
    return _buildEmptyState('暂无动态', themeController, customFonts);
  }

  return ListView.builder(
    padding: customFonts.getPadding(all: 16),
    itemCount: controller.dynamics.length + (controller.hasMoreDynamics.value ? 1 : 0),
    itemBuilder: (context, index) {
      if (index == controller.dynamics.length) {
        controller.loadMoreDynamics();
        return _buildLoadMoreIndicator();
      }

      final dynamic = controller.dynamics[index];
      return _buildDynamicItem(dynamic, themeController, customFonts);
    },
  );
});

}

Widget _buildArticlesTab(ThemeController themeController, CustomFonts customFonts) {

return Obx(() {
  if (controller.articles.isEmpty) {
    return _buildEmptyState('暂无文章', themeController, customFonts);
  }

  return ListView.builder(
    padding: customFonts.getPadding(all: 16),
    itemCount: controller.articles.length + (controller.hasMoreArticles.value ? 1 : 0),
    itemBuilder: (context, index) {
      if (index == controller.articles.length) {
        controller.loadMoreArticles();
        return _buildLoadMoreIndicator();
      }

      final article = controller.articles[index];
      return _buildArticleItem(article, themeController, customFonts);
    },
  );
});

}

Widget _buildCollectionsTab(ThemeController themeController, CustomFonts customFonts) {

return Obx(() {
  if (controller.comments.isEmpty) {
    return _buildEmptyState('暂无评论', themeController, customFonts);
  }

  return ListView.builder(
    padding: customFonts.getPadding(all: 16),
    itemCount: controller.comments.length + (controller.hasMoreComments.value ? 1 : 0),
    itemBuilder: (context, index) {
      if (index == controller.comments.length) {
        controller.loadMoreComments();
        return _buildLoadMoreIndicator();
      }

      final comment = controller.comments[index];
      return _buildCommentItem(comment, themeController, customFonts);
    },
  );
});

}

Widget _buildEmptyState(String message, ThemeController themeController, CustomFonts customFonts) {

return Center(
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      Icon(
        Icons.inbox_outlined,
        size: ScreenUtilConfig.setWidth(80),
        color: themeController.secondaryTextColor.withOpacity(0.5),
      ),
      SizedBox(height: ScreenUtilConfig.setHeight(16)),
      Text(
        message,
        style: customFonts.bodyLarge.copyWith(
          color: themeController.secondaryTextColor,
        ),
      ),
    ],
  ),
);

}

Widget _buildLoadMoreIndicator() {

return Container(
  padding: EdgeInsets.all(ScreenUtilConfig.setWidth(16)),
  alignment: Alignment.center,
  child: const CircularProgressIndicator(),
);

}

Widget _buildDynamicItem(Map<String, dynamic> dynamic, ThemeController themeController, CustomFonts customFonts) {

return Container(
  margin: EdgeInsets.only(bottom: ScreenUtilConfig.setHeight(16)),
  padding: customFonts.getPadding(all: 16),
  decoration: BoxDecoration(
    color: themeController.cardBackgroundColor,
    borderRadius: customFonts.getBorderRadius(all: 12),
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.05),
        blurRadius: 8,
        offset: const Offset(0, 2),
      ),
    ],
  ),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text(
        dynamic['content'] ?? '动态内容',
        style: customFonts.bodyLarge,
        maxLines: 3,
        overflow: TextOverflow.ellipsis,
      ),
      SizedBox(height: ScreenUtilConfig.setHeight(12)),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(
            _formatTime(dynamic['created'] ?? 0),
            style: customFonts.bodySmall.copyWith(
              color: themeController.secondaryTextColor,
            ),
          ),
          Row(
            children: [
              Icon(
                Icons.favorite_border,
                size: ScreenUtilConfig.setWidth(16),
                color: themeController.secondaryTextColor,
              ),
              SizedBox(width: ScreenUtilConfig.setWidth(4)),
              Text(
                '${dynamic['likes'] ?? 0}',
                style: customFonts.bodySmall.copyWith(
                  color: themeController.secondaryTextColor,
                ),
              ),
            ],
          ),
        ],
      ),
    ],
  ),
);

}

Widget _buildArticleItem(Map<String, dynamic> article, ThemeController themeController, CustomFonts customFonts) {

return Container(
  margin: EdgeInsets.only(bottom: ScreenUtilConfig.setHeight(16)),
  padding: customFonts.getPadding(all: 16),
  decoration: BoxDecoration(
    color: themeController.cardBackgroundColor,
    borderRadius: customFonts.getBorderRadius(all: 12),
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.05),
        blurRadius: 8,
        offset: const Offset(0, 2),
      ),
    ],
  ),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text(
        article['title'] ?? '文章标题',
        style: customFonts.articleTitleStyle,
        maxLines: 2,
        overflow: TextOverflow.ellipsis,
      ),
      SizedBox(height: ScreenUtilConfig.setHeight(8)),
      Text(
        article['text'] ?? '文章摘要内容...',
        style: customFonts.articleContentStyle.copyWith(
          color: themeController.secondaryTextColor,
        ),
        maxLines: 3,
        overflow: TextOverflow.ellipsis,
      ),
      SizedBox(height: ScreenUtilConfig.setHeight(12)),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(
            _formatTime(article['created'] ?? 0),
            style: customFonts.bodySmall.copyWith(
              color: themeController.secondaryTextColor,
            ),
          ),
          Row(
            children: [
              Icon(
                Icons.visibility,
                size: ScreenUtilConfig.setWidth(16),
                color: themeController.secondaryTextColor,
              ),
              SizedBox(width: ScreenUtilConfig.setWidth(4)),
              Text(
                '${article['views'] ?? 0}',
                style: customFonts.bodySmall.copyWith(
                  color: themeController.secondaryTextColor,
                ),
              ),
            ],
          ),
        ],
      ),
    ],
  ),
);

}

Widget _buildCommentItem(Map<String, dynamic> comment, ThemeController themeController, CustomFonts customFonts) {

return Container(
  margin: EdgeInsets.only(bottom: ScreenUtilConfig.setHeight(16)),
  padding: customFonts.getPadding(all: 16),
  decoration: BoxDecoration(
    color: themeController.cardBackgroundColor,
    borderRadius: customFonts.getBorderRadius(all: 12),
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.05),
        blurRadius: 8,
        offset: const Offset(0, 2),
      ),
    ],
  ),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text(
        comment['text'] ?? '评论内容',
        style: customFonts.bodyLarge,
        maxLines: 4,
        overflow: TextOverflow.ellipsis,
      ),
      SizedBox(height: ScreenUtilConfig.setHeight(12)),
      Text(
        _formatTime(comment['created'] ?? 0),
        style: customFonts.bodySmall.copyWith(
          color: themeController.secondaryTextColor,
        ),
      ),
    ],
  ),
);

}

// 辅助方法
String _getUserWebsite() => controller.userInfo['url'] ?? '';
String _getUserIp() => controller.userInfo['ip'] ?? '';
String _getUserLocation() {

final local = controller.userInfo['local'] ?? '';
if (local.isEmpty) return '';
final parts = local.split('|');
if (parts.length >= 4) {
  return '${parts[2]}${parts[3]}';
}
return local;

}
String _getUserRegion() => controller.userInfo['region'] ?? '';
String _getUserPhone() => controller.userInfo['phone'] ?? '';
String _getUserEmail() => controller.userInfo['mail'] ?? '';
String _getUserGroup() => controller.userInfo['groupKey'] ?? '';
bool _isVip() => (controller.userInfo['vip'] ?? 0) > 0 || (controller.userInfo['isvip'] ?? 0) > 0;
int _getUserHonor() => controller.userInfo['honor'] ?? 0;
int _getUserExperience() => controller.userInfo['experience'] ?? 0;
int _getUserAssets() => controller.userInfo['assets'] ?? 0;
int _getUserCreated() => controller.userInfo['created'] ?? 0;
int _getUserLogged() => controller.userInfo['logged'] ?? 0;
String _getUserBackgroundImage() => controller.userInfo['userBg'] ?? '';

int _getFollowersCount() {

return controller.userInfo['fanNum'] ??
    controller.userInfo['followersCount'] ??
    controller.userInfo['fans'] ??
    2089;

}

int _getArticlesCount() {

return controller.articlesCount > 0 ?
controller.articlesCount :
(controller.userInfo['postNum'] ??
    controller.userInfo['articlesCount'] ??
    controller.userInfo['posts'] ??
    109);

}

int _getCommentsCount() {

return controller.commentsCount > 0 ?
controller.commentsCount :
(controller.userInfo['commentNum'] ??
    controller.userInfo['commentsCount'] ??
    controller.userInfo['comments'] ??
    756);

}

int _calculateJoinDays() {

final created = _getUserCreated();
if (created == 0) return 970;
final createdDate = DateTime.fromMillisecondsSinceEpoch(created * 1000);
final now = DateTime.now();
return now.difference(createdDate).inDays;

}

String _formatNumberWithK(int number) {

if (number >= 1000000) {
  return '${(number / 1000000).toStringAsFixed(1)}M';
} else if (number >= 1000) {
  return '${(number / 1000).toStringAsFixed(1)}k';
}
return number.toString();

}

void _reportUser() {

Get.snackbar(
  '举报',
  '举报功能开发中...',
  snackPosition: SnackPosition.BOTTOM,
);

}

void _blockUser() {

Get.dialog(
  AlertDialog(
    title: const Text('确认拉黑'),
    content: Text('确定要拉黑用户"${controller.userName}"吗?'),
    actions: [
      TextButton(
        onPressed: () => Get.back(),
        child: const Text('取消'),
      ),
      TextButton(
        onPressed: () {
          Get.back();
          Get.snackbar(
            '拉黑',
            '拉黑功能开发中...',
            snackPosition: SnackPosition.BOTTOM,
          );
        },
        child: const Text('确定'),
      ),
    ],
  ),
);

}

void _shareProfile() {

Get.snackbar(
  '分享',
  '分享功能开发中...',
  snackPosition: SnackPosition.BOTTOM,
);

}

Future _launchUrl(String url) async {

try {
  final uri = Uri.parse(url);
  if (await canLaunchUrl(uri)) {
    await launchUrl(uri, mode: LaunchMode.externalApplication);
  } else {
    Get.snackbar('错误', '无法打开链接');
  }
} catch (e) {
  Get.snackbar('错误', '链接格式错误');
}

}

String _formatNumber(int number) {

if (number >= 10000) {
  return '${(number / 10000).toStringAsFixed(1)}万';
}
return number.toString();

}

String _formatTime(int timestamp) {

if (timestamp == 0) return '刚刚';
final dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
final now = DateTime.now();
final difference = now.difference(dateTime);

if (difference.inDays > 0) {
  return '${difference.inDays}天前';
} else if (difference.inHours > 0) {
  return '${difference.inHours}小时前';
} else if (difference.inMinutes > 0) {
  return '${difference.inMinutes}分钟前';
} else {
  return '刚刚';
}

}
}

class _TabBarDelegate extends SliverPersistentHeaderDelegate {
final Widget child;

_TabBarDelegate({required this.child});

@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {

return child;

}

@override
double get maxExtent => 48;

@override
double get minExtent => 48;

@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {

return false;

}
}