148 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Dart
		
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Dart
		
	
	
| import 'package:flutter/material.dart';
 | |
| import '../tools/tools.dart';
 | |
| 
 | |
| /// 通用列表卡片组件:
 | |
| /// - 两两为一组,优先尝试同一行显示左右两列并左/右对齐;
 | |
| /// - 如放不下,则自动拆成两行,上行左对齐,下行右对齐。
 | |
| class DannerRepainItem extends StatelessWidget {
 | |
|   final String title;
 | |
|   final List<String> details;
 | |
|   final bool showBottomTags;
 | |
|   final List<Widget> bottomTags;
 | |
|   final bool showTitleIcon;
 | |
| 
 | |
|   const DannerRepainItem({
 | |
|     Key? key,
 | |
|     required this.title,
 | |
|     required this.details,
 | |
|     this.showBottomTags = false,
 | |
|     this.showTitleIcon = true,
 | |
|     this.bottomTags = const [],
 | |
|   }) : super(key: key);
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return Padding(
 | |
|       padding: const EdgeInsets.only(left: 15, right: 15, bottom: 15),
 | |
|       child: Container(
 | |
|         decoration: const BoxDecoration(
 | |
|           color: Colors.white,
 | |
|           borderRadius: BorderRadius.all(Radius.circular(5)),
 | |
|         ),
 | |
|         child: Column(
 | |
|           crossAxisAlignment: CrossAxisAlignment.stretch,
 | |
|           children: [
 | |
|             // — 标题行 —
 | |
|             Padding(
 | |
|               padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
 | |
|               child: Row(
 | |
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | |
|                 children: [
 | |
|                   Row(children: [
 | |
|                     if (showTitleIcon)
 | |
|                       const Icon(Icons.star_rate_sharp, color: Colors.green, size: 18),
 | |
|                     SizedBox(width: showTitleIcon ? 5 : 0),
 | |
|                     Text(title, style: const TextStyle(fontSize: 14)),
 | |
|                   ]),
 | |
|                   const Icon(Icons.arrow_forward_ios_rounded, color: Colors.grey, size: 15),
 | |
|                 ],
 | |
|               ),
 | |
|             ),
 | |
|             const Divider(height: 1),
 | |
| 
 | |
|             // — 详情区:动态两列/换行 —
 | |
|             Padding(
 | |
|               padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
 | |
|               child: LayoutBuilder(builder: (context, constraints) {
 | |
|                 // 间距:你可以根据设计随意调整
 | |
|                 const double horizontalGap = 20;
 | |
|                 const double verticalGap = 5;
 | |
| 
 | |
|                 List<Widget> rows = [];
 | |
|                 for (int i = 0; i < details.length; i += 2) {
 | |
|                   final left = details[i];
 | |
|                   final right = (i + 1 < details.length) ? details[i + 1] : '';
 | |
| 
 | |
|                   // 测量文字宽度
 | |
|                   final leftPainter = TextPainter(
 | |
|                     text: TextSpan(text: left, style: HhTextStyleUtils.secondaryTitleStyle),
 | |
|                     maxLines: 1,
 | |
|                     textDirection: TextDirection.ltr,
 | |
|                   )..layout();
 | |
|                   final rightPainter = TextPainter(
 | |
|                     text: TextSpan(text: right, style: HhTextStyleUtils.secondaryTitleStyle),
 | |
|                     maxLines: 1,
 | |
|                     textDirection: TextDirection.ltr,
 | |
|                   )..layout();
 | |
| 
 | |
|                   final canFitOneLine = right.isNotEmpty &&
 | |
|                       (leftPainter.width + horizontalGap + rightPainter.width)
 | |
|                           <= constraints.maxWidth;
 | |
| 
 | |
|                   if (right.isNotEmpty && canFitOneLine) {
 | |
|                     // 同行显示,左右对齐
 | |
|                     rows.add(Padding(
 | |
|                       padding: EdgeInsets.only(bottom: verticalGap),
 | |
|                       child: Row(
 | |
|                         mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | |
|                         children: [
 | |
|                           _DetailText(left),
 | |
|                           _DetailText(right),
 | |
|                         ],
 | |
|                       ),
 | |
|                     ));
 | |
|                   } else {
 | |
|                     // 拆为两行
 | |
|                     rows.add(Padding(
 | |
|                       padding: EdgeInsets.only(bottom: verticalGap),
 | |
|                       child: Column(
 | |
|                         crossAxisAlignment: CrossAxisAlignment.stretch,
 | |
|                         children: [
 | |
|                           // 上行:左对齐
 | |
|                           _DetailText(left),
 | |
|                           if (right.isNotEmpty)
 | |
|                           // 下行:右对齐
 | |
|                             Align(
 | |
|                               alignment: Alignment.centerRight,
 | |
|                               child: _DetailText(right),
 | |
|                             ),
 | |
|                         ],
 | |
|                       ),
 | |
|                     ));
 | |
|                   }
 | |
|                 }
 | |
| 
 | |
|                 return Column(children: rows);
 | |
|               }),
 | |
|             ),
 | |
| 
 | |
|             // — 底部标签区 —
 | |
|             if (showBottomTags && bottomTags.isNotEmpty)
 | |
|               Padding(
 | |
|                 padding: const EdgeInsets.only(left: 15, right: 15, bottom: 15),
 | |
|                 child: Wrap(spacing: 5, runSpacing: 5, children: bottomTags),
 | |
|               ),
 | |
|           ],
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// Detail 文本封装:
 | |
| /// 默认一行不换行;若超出则让整个组件撑宽,由外层判断拆行。
 | |
| class _DetailText extends StatelessWidget {
 | |
|   final String text;
 | |
|   const _DetailText(this.text, {Key? key}) : super(key: key);
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return Text(
 | |
|       text,
 | |
|       style: HhTextStyleUtils.secondaryTitleStyle,
 | |
|       softWrap: false,
 | |
|       overflow: TextOverflow.visible,
 | |
|     );
 | |
|   }
 | |
| }
 |