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, | ||
|  |     ); | ||
|  |   } | ||
|  | } |