// lib/app/widgets/hi_collapsible_list.dart import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:kaer_with_panels/app/widgets/kr_local_image.dart'; import 'dart:math' as math; // 导入 math 库以使用 pi import 'package:flutter/gestures.dart'; import 'package:get/get.dart'; import '../routes/app_pages.dart'; /// 可折叠列表项的数据模型 class HICollapsibleItem { final String title; // 👇 核心改动 1: 将 content 类型从 String 改为 List final List content; const HICollapsibleItem({required this.title, required this.content}); } /// 一个独立的、自定义样式的可折叠面板组件 (HI 前缀) /// /// 它接收一个标题和一个内容字符串,并渲染成一个带边框、可展开/收起的面板。 class HICollapsibleItemWidget extends StatefulWidget { const HICollapsibleItemWidget({ super.key, required this.item, this.initiallyExpanded = false, }); /// 要显示的数据项 final HICollapsibleItem item; /// 初始是否展开 final bool initiallyExpanded; @override State createState() => _HICollapsibleItemWidgetState(); } class _HICollapsibleItemWidgetState extends State { bool _isExpanded = false; @override void initState() { super.initState(); _isExpanded = widget.initiallyExpanded; } List _buildTextSpans(String text) { final List spans = []; final RegExp linkRegExp = RegExp(r'\[link\](.*?)\[/link\]'); // 正则表达式匹配 [link]...[/link] text.splitMapJoin( linkRegExp, onMatch: (Match match) { final linkText = match.group(1)!; // 获取链接文本,例如 "点击这里" spans.add( TextSpan( text: linkText, style: const TextStyle( color: const Color(0xFFADFF5B), // 链接颜色 ), recognizer: TapGestureRecognizer() ..onTap = () { // 在这里处理点击事件,例如打开一个网页 // 注意:您需要添加 url_launcher 依赖 Get.toNamed( Routes.KR_WEBVIEW, arguments: { 'url': 'https://www.baidu.com', }, ); }, ), ); return ''; }, onNonMatch: (String nonMatch) { spans.add(TextSpan(text: nonMatch)); // 普通文本部分 return ''; }, ); return spans; } @override Widget build(BuildContext context) { final BorderRadius borderRadius = BorderRadius.circular(40.w); return AnimatedContainer( duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, decoration: BoxDecoration( border: Border.all(color: Colors.white, width: 2.0), borderRadius: borderRadius, ), child: ClipRRect( borderRadius: borderRadius, child: Column( mainAxisSize: MainAxisSize.min, children: [ // ===== 标题部分 ===== GestureDetector( onTap: () { setState(() { _isExpanded = !_isExpanded; }); }, behavior: HitTestBehavior.translucent, child: Padding( padding: EdgeInsets.fromLTRB(24.w, 16.w, 24.w, 16.w), child: Row( children: [ Expanded( child: Text( widget.item.title, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: FontWeight.w600, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), AnimatedRotation( turns: _isExpanded ? 0.75 : 0.25, duration: const Duration(milliseconds: 200), child: KrLocalImage( imageName: 'arrow-right-icon', imageType: ImageType.svg, color: Colors.white, ), ), ], ), ), ), // ===== 内容部分 ===== AnimatedSize( duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, child: _isExpanded ? Container( padding: EdgeInsets.fromLTRB(24.w, 0, 24.w, 16.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: widget.item.content.map((itemText) { return Padding( padding: EdgeInsets.only(bottom: 8.h), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.only(top: 11.w, right: 8.w), child: Container( width: 5.w, height: 5.w, decoration: const BoxDecoration( color: Colors.white, shape: BoxShape.circle, ), ), ), Expanded( child: Text.rich( TextSpan( style: TextStyle( color: Colors.white, fontSize: 13.sp, height: 1.8, fontWeight: FontWeight.w300, ), children: _buildTextSpans(itemText), ), ), ), ], ), ); }).toList(), ), ) : const SizedBox.shrink(), ), ], ), ), ); } }