device_card.dart 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // ignore_for_file: non_constant_identifier_names, constant_identifier_names
  2. import 'package:flutter/material.dart';
  3. import 'package:vnote_device_plugin/consts/types.dart';
  4. import 'device_entity.dart';
  5. final _typeNameMap = <String, String>{
  6. DeviceTypes.TEMP: "额温枪",
  7. DeviceTypes.WEIGHT: "体重秤",
  8. DeviceTypes.SUGAR: "血糖仪",
  9. DeviceTypes.SPO2: "血氧仪",
  10. DeviceTypes.NIBP: "血压计",
  11. };
  12. /// 设备卡片
  13. class DeviceCard extends StatelessWidget {
  14. static const Color _BOUND_COLOR = Colors.green;
  15. static final Color _UNBOUND_COLOR = Colors.orange.shade800;
  16. /// 设备类型
  17. final String type;
  18. /// 设备数据
  19. final DeviceEntity? data;
  20. /// 点击绑定/解绑事件
  21. final ValueChanged<bool>? onTapBind;
  22. const DeviceCard({
  23. super.key,
  24. required this.type,
  25. this.data,
  26. this.onTapBind,
  27. });
  28. bool get hasData => data != null;
  29. @override
  30. Widget build(BuildContext context) {
  31. EdgeInsets padding =
  32. const EdgeInsets.symmetric(vertical: 12, horizontal: 16);
  33. final children = <Widget>[];
  34. children.add(_buildHeader());
  35. if (data != null) {
  36. children.add(const SizedBox(height: 8));
  37. final divider = Divider(
  38. height: 1,
  39. // indent: 8,
  40. // endIndent: 8,
  41. color: Colors.grey.shade400,
  42. );
  43. final cells = _buildInfoCells();
  44. for (final cell in cells) {
  45. children.add(divider);
  46. children.add(cell);
  47. }
  48. // 缩小下内距
  49. padding = padding.copyWith(bottom: 8);
  50. }
  51. return Container(
  52. padding: padding,
  53. decoration: BoxDecoration(
  54. color: Colors.white,
  55. borderRadius: BorderRadius.circular(8),
  56. ),
  57. child: Column(
  58. children: children,
  59. ),
  60. );
  61. }
  62. List<Widget> _buildInfoCells() {
  63. return [
  64. _InfoCell("名称", data!.productName),
  65. _InfoCell("型号", data!.model),
  66. _InfoCell("Mac", data!.mac),
  67. ];
  68. }
  69. Widget _buildHeader() {
  70. return Row(
  71. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  72. children: [
  73. _ProductImage(type),
  74. Expanded(
  75. child: _buildTypeSummary(),
  76. ),
  77. _buildOperateButton(),
  78. ],
  79. );
  80. }
  81. Widget _buildTypeSummary() {
  82. final typeName = _typeNameMap[type] ?? type;
  83. return Container(
  84. alignment: Alignment.centerLeft,
  85. padding: const EdgeInsets.only(left: 12),
  86. child: Column(
  87. crossAxisAlignment: CrossAxisAlignment.start,
  88. children: [
  89. Text(
  90. typeName,
  91. style: const TextStyle(
  92. color: Colors.black,
  93. fontSize: 16,
  94. ),
  95. ),
  96. const SizedBox(height: 4),
  97. Row(
  98. crossAxisAlignment: CrossAxisAlignment.center,
  99. children: [
  100. SizedBox(
  101. width: 8,
  102. height: 8,
  103. child: CircleAvatar(
  104. backgroundColor: hasData ? _BOUND_COLOR : _UNBOUND_COLOR,
  105. ),
  106. ),
  107. const SizedBox(width: 4),
  108. Text(
  109. hasData ? "已绑定" : "未绑定",
  110. style: TextStyle(
  111. color: Colors.grey.shade600,
  112. fontSize: 14,
  113. ),
  114. ),
  115. ],
  116. ),
  117. ],
  118. ),
  119. );
  120. }
  121. Widget _buildOperateButton() {
  122. final color = hasData ? _UNBOUND_COLOR : _BOUND_COLOR;
  123. return TextButton(
  124. style: ButtonStyle(
  125. overlayColor: MaterialStatePropertyAll(color.withOpacity(.2)),
  126. ),
  127. onPressed: () {
  128. onTapBind?.call(!hasData);
  129. },
  130. child: Row(
  131. children: [
  132. Text(
  133. hasData ? "解除绑定" : "去绑定",
  134. style: TextStyle(
  135. color: color,
  136. fontSize: 14,
  137. ),
  138. ),
  139. Icon(
  140. Icons.arrow_forward_ios,
  141. color: color,
  142. size: 12,
  143. ),
  144. ],
  145. ),
  146. );
  147. }
  148. }
  149. class _ProductImage extends StatelessWidget {
  150. final String type;
  151. const _ProductImage(this.type);
  152. @override
  153. Widget build(BuildContext context) {
  154. final path = "assets/$type.png";
  155. return Container(
  156. width: 76,
  157. height: 76,
  158. decoration: BoxDecoration(
  159. color: Colors.grey.withOpacity(.08),
  160. borderRadius: BorderRadius.circular(8),
  161. ),
  162. child: Image.asset(
  163. path,
  164. fit: BoxFit.contain,
  165. ),
  166. );
  167. }
  168. }
  169. class _InfoCell extends StatelessWidget {
  170. static const double _CELL_HEIGHT = 46.0;
  171. static const double _FONT_SIZE = 16.0;
  172. static const _LABEL_STYLE =
  173. TextStyle(color: Colors.black, fontSize: _FONT_SIZE);
  174. static final _CONTENT_STYLE =
  175. TextStyle(color: Colors.grey.shade600, fontSize: _FONT_SIZE);
  176. final String label;
  177. final String content;
  178. const _InfoCell(
  179. this.label,
  180. this.content,
  181. );
  182. @override
  183. Widget build(BuildContext context) {
  184. return SizedBox(
  185. height: _CELL_HEIGHT,
  186. child: Row(
  187. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  188. children: [
  189. Text(label, style: _LABEL_STYLE),
  190. Text(content, style: _CONTENT_STYLE),
  191. ],
  192. ),
  193. );
  194. }
  195. }