measure_page_test.dart 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. import 'dart:typed_data';
  2. import 'package:fis_jsonrpc/rpc.dart';
  3. import 'package:fis_measure/interfaces/enums/annotation.dart';
  4. import 'package:fis_measure/interfaces/process/items/item.dart';
  5. import 'package:fis_measure/interfaces/process/items/item_metas.dart';
  6. import 'package:fis_measure/interfaces/process/items/terms.dart';
  7. import 'package:fis_measure/interfaces/process/items/types.dart';
  8. import 'package:fis_measure/interfaces/process/player/play_controller.dart';
  9. import 'package:fis_measure/interfaces/process/standard_line/calibration.dart';
  10. import 'package:fis_measure/interfaces/process/visuals/visual_area.dart';
  11. import 'package:fis_measure/interfaces/process/workspace/application.dart';
  12. import 'package:fis_measure/interfaces/process/workspace/exam_info.dart';
  13. import 'package:fis_measure/interfaces/process/workspace/measure_controller.dart';
  14. import 'package:fis_measure/process/items/item_meta_convert.dart';
  15. import 'package:fis_measure/process/workspace/measure_3d_view_controller.dart';
  16. import 'package:fis_measure/process/workspace/measure_controller.dart';
  17. import 'package:fis_measure/process/workspace/measure_data_controller.dart';
  18. import 'package:fis_measure/view/measure/measure_main_view.dart';
  19. import 'package:flutter/material.dart';
  20. import 'package:get/get.dart';
  21. import 'package:vid/us/vid_us_mode.dart';
  22. import 'item_create_test.dart';
  23. import 'process/workspace/measure_handler.dart';
  24. class MeasureDataTester {
  25. static Future<List<RemedicalItemList>> getRemedicalList(
  26. String patientCode,
  27. String recordCode,
  28. String token,
  29. ) async {
  30. return [];
  31. }
  32. static Future<RemedicalInfoDTO?> getImageInfo(
  33. String remedicalCode,
  34. String token,
  35. String? remedicalAISelectedInfoCode,
  36. ) async {
  37. return null;
  38. }
  39. static Future<MeasureApplicationDTO?> getMeasureApplication(
  40. dynamic args) async {
  41. return null;
  42. }
  43. static Future<bool> saveUserDefinedMeasureApplicationAsync(
  44. dynamic args) async {
  45. return true;
  46. }
  47. static Future<bool?> saveImage(
  48. Uint8List imageBytes,
  49. String patientCode,
  50. String recordCode,
  51. String remedicalCode,
  52. String measuredData,
  53. ) async {
  54. return null;
  55. }
  56. static Future<void> saveMeasureSystemSettingAsync(
  57. MeasureSystemSettingDTO measureSystemSetting) async {
  58. return;
  59. }
  60. static Future<MeasureSystemSettingDTO?> getMeasureSystemSettingAsync() async {
  61. return null;
  62. }
  63. static Future<String> shareImage(String vid) async {
  64. return '';
  65. }
  66. static Future<CommentItemResultDTO?> getCommentsByApplicationAsync(
  67. String applicationName, String categoryName) async {
  68. return null;
  69. }
  70. static Future<PresetCommentItemResultDTO?> getPresetCommentsAsync() async {
  71. return null;
  72. }
  73. static Future<bool?> resetUserCommentsAsync(
  74. String applicationName, String categoryName) async {
  75. return null;
  76. }
  77. static Future<bool?> saveUserDefinedCommentsAsync(
  78. String applicationName,
  79. String categoryName,
  80. List<CommentItemDTO>? add,
  81. List<CommentItemDTO>? delete,
  82. List<UpdateCommentItemDTO>? update) async {
  83. return null;
  84. }
  85. }
  86. class MeasureTestPage extends StatefulWidget {
  87. const MeasureTestPage({Key? key}) : super(key: key);
  88. // ignore: non_constant_identifier_names
  89. static List<dynamic> MetaDTOList = [];
  90. @override
  91. State<StatefulWidget> createState() => _MeasureTestPageState();
  92. }
  93. class _MeasureTestPageState extends State<MeasureTestPage> {
  94. static const C_LINEAR_TISSUE =
  95. // "http://cdn-bj.fis.plus/9F066341FA874E21B48CDE247C13D495.vid"; //B TVI TD
  96. // "http://cdn-bj.fis.plus/974BABA5113640639FD749E06DD7DA5B.vid"; //B CF CW
  97. "http://cdn-bj.fis.plus/0B344F48BA574ECD82B7FEDB8848421A.vid"; //单幅TM
  98. // "http://cdn-bj.fis.plus/3379F38302884C2991D90FBDFB0DEA7E.dat"; //单幅TM(2)
  99. // "http://cdn-bj.fis.plus/6A99AD2530864616B64355A8EA9AE3EC.vid";
  100. // "http://cdn-bj.fis.plus/F26C6E5D57A7472A97E9EB543DF0D16C.vid"; // 单幅Convex
  101. // "http://cdn-bj.fis.plus/6B6E069659D14E7299EB9F6EFCDE9C8C.vid"; //双幅单TissueConvex
  102. // "http://cdn-bj.fis.plus/062643B82365437DB95F3811580AF3ED.vid"; //四幅单模式
  103. // "http://cdn-bj.fis.plus/EA90D146049D416E8E466B7446E00001.vid"; //四幅Doppler
  104. // "http://cdn-bj.fis.plus/3rd_linearTvTissue2.vid"; //魔盒
  105. // "http://cdn-bj.fis.plus/81FFF8E5E078473FA687FBE81C4869B1.vid"; // 魔盒TV
  106. // "http://cdn-bj.fis.plus/7B450708A2784B1490304C82787349BE.vid";// 胎儿Zoom
  107. static const C_CONVEX_TISSUE =
  108. "http://cdn-bj.fis.plus/FEB1AAE5D9C24839BEE31B16E8CB450A.vid";
  109. final _3dc = Get.put<Measure3DViewController>(Measure3DViewController());
  110. final datac = Get.put<MeasureDataController>(MeasureDataController(
  111. MeasureDataTester.getRemedicalList,
  112. MeasureDataTester.getImageInfo,
  113. MeasureDataTester.getMeasureApplication,
  114. MeasureDataTester.saveUserDefinedMeasureApplicationAsync,
  115. MeasureDataTester.saveImage,
  116. MeasureDataTester.saveMeasureSystemSettingAsync,
  117. MeasureDataTester.getMeasureSystemSettingAsync,
  118. MeasureDataTester.shareImage,
  119. MeasureDataTester.getCommentsByApplicationAsync,
  120. MeasureDataTester.saveUserDefinedCommentsAsync,
  121. MeasureDataTester.resetUserCommentsAsync,
  122. MeasureDataTester.getPresetCommentsAsync,
  123. ));
  124. final measureHandler = Get.put(MeasureHandler());
  125. final controller = Get.put<IMeasureController>(MeasureController(
  126. "12345",
  127. imagesFetchFunc: (code) async {
  128. return <ExamImageInfo>[
  129. ExamImageInfo(C_LINEAR_TISSUE, C_LINEAR_TISSUE),
  130. ExamImageInfo(C_CONVEX_TISSUE, C_CONVEX_TISSUE)
  131. ];
  132. },
  133. ));
  134. bool loaded = false;
  135. int opType = 0;
  136. bool useArrowAnnotation = false;
  137. @override
  138. void initState() {
  139. measureHandler.changeImageLoaded;
  140. controller.load().then((value) {
  141. // 加载指定图像
  142. controller.examInfo.selectedImageIndex = 0;
  143. });
  144. controller.imageLoaded.addListener(onImageLoaded);
  145. super.initState();
  146. }
  147. @override
  148. void dispose() {
  149. controller.imageLoaded.removeListener(onImageLoaded);
  150. controller.dispose();
  151. Get.delete<IMeasureController>();
  152. super.dispose();
  153. }
  154. void onImageLoaded(Object sender, ExamImageInfo? e) {
  155. if (!mounted) return;
  156. if (e != null) {
  157. Future.delayed(const Duration(milliseconds: 100), () {
  158. controller.playerController.play();
  159. });
  160. setState(() {
  161. loaded = true;
  162. });
  163. }
  164. }
  165. @override
  166. Widget build(BuildContext context) {
  167. Widget body;
  168. if (!loaded) {
  169. const loadingWidget = Center(child: CircularProgressIndicator());
  170. body = Row(
  171. children: const [
  172. SizedBox(
  173. width: 300,
  174. child: loadingWidget,
  175. ),
  176. VerticalDivider(),
  177. Expanded(child: loadingWidget),
  178. ],
  179. );
  180. } else {
  181. body = Row(
  182. key: ValueKey(controller.examInfo.selectedImageIndex),
  183. children: [
  184. opType == 1
  185. ? const _MeasureLeftAnnotation()
  186. : const _MeasureLeftBoard(),
  187. const VerticalDivider(),
  188. const Expanded(
  189. child: MeasureRightBoard(),
  190. ),
  191. ],
  192. );
  193. }
  194. return Scaffold(
  195. backgroundColor: const Color.fromARGB(255, 53, 55, 51),
  196. appBar: AppBar(
  197. actions: [
  198. TextButton(
  199. onPressed: () {
  200. Get.find<IApplication>().clearRecords();
  201. },
  202. child: const Text(
  203. "清空",
  204. style: TextStyle(
  205. color: Colors.white,
  206. ),
  207. ),
  208. ),
  209. TextButton(
  210. onPressed: () {
  211. Get.find<IApplication>().undoRecord();
  212. },
  213. child: const Text(
  214. "撤销",
  215. style: TextStyle(
  216. color: Colors.white,
  217. ),
  218. ),
  219. ),
  220. TextButton(
  221. onPressed: () {
  222. final c = Get.find<IStandardLineCalibrationController>();
  223. if (c.isEditing) {
  224. c.cancelEdit();
  225. } else {
  226. c.enterEditMode();
  227. }
  228. },
  229. child: const Text(
  230. "校准线",
  231. style: TextStyle(
  232. color: Colors.white,
  233. ),
  234. ),
  235. ),
  236. TextButton.icon(
  237. onPressed: () {
  238. setState(() {
  239. if (useArrowAnnotation) {
  240. useArrowAnnotation = false;
  241. controller.workingApplication.switchAnnotation();
  242. } else {
  243. useArrowAnnotation = true;
  244. controller.workingApplication
  245. .switchAnnotation(AnnotationType.arrow);
  246. }
  247. });
  248. },
  249. icon: const Icon(Icons.arrow_right_alt_sharp),
  250. label: Text(
  251. "箭头",
  252. style: TextStyle(
  253. color: useArrowAnnotation ? Colors.amber : Colors.white,
  254. ),
  255. ),
  256. ),
  257. TextButton(
  258. onPressed: () {
  259. setState(() {
  260. opType = opType == 1 ? 0 : 1;
  261. });
  262. },
  263. child: Text(
  264. opType == 1 ? "注释" : "测量",
  265. style: const TextStyle(
  266. color: Colors.white,
  267. ),
  268. ),
  269. ),
  270. TextButton(
  271. onPressed: () {
  272. if (controller.examInfo.selectedImageIndex == 0) return;
  273. controller.examInfo.selectedImageIndex = 0;
  274. },
  275. child: Text(
  276. '线阵',
  277. style: TextStyle(
  278. color: controller.examInfo.selectedImageIndex == 0
  279. ? Colors.amber
  280. : Colors.white,
  281. ),
  282. ),
  283. ),
  284. TextButton(
  285. onPressed: () {
  286. if (controller.examInfo.selectedImageIndex == 1) return;
  287. controller.examInfo.selectedImageIndex = 1;
  288. },
  289. child: Text(
  290. '扇阵',
  291. style: TextStyle(
  292. color: controller.examInfo.selectedImageIndex == 1
  293. ? Colors.amber
  294. : Colors.white,
  295. ),
  296. ),
  297. ),
  298. ],
  299. leading: IconButton(
  300. onPressed: () {
  301. Navigator.of(context).pop();
  302. },
  303. icon: const Icon(Icons.arrow_back),
  304. ),
  305. ),
  306. body: body,
  307. floatingActionButton: _ModeTips(),
  308. floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
  309. );
  310. }
  311. }
  312. class MeasureRightBoard extends StatefulWidget {
  313. const MeasureRightBoard({Key? key}) : super(key: key);
  314. @override
  315. State<StatefulWidget> createState() => _MeasureRightBoardState();
  316. }
  317. class _MeasureRightBoardState extends State<MeasureRightBoard> {
  318. final playerController = Get.find<IPlayerController>();
  319. @override
  320. Widget build(BuildContext context) {
  321. return Container(
  322. padding: const EdgeInsets.all(8).copyWith(left: 0),
  323. child: Stack(
  324. children: const [
  325. MeasureMainView(),
  326. _PlayerTips(),
  327. ],
  328. ),
  329. );
  330. }
  331. }
  332. class _PlayerTips extends StatefulWidget {
  333. const _PlayerTips({Key? key}) : super(key: key);
  334. @override
  335. State<StatefulWidget> createState() => _PlayerTipsState();
  336. }
  337. class _PlayerTipsState extends State<_PlayerTips> {
  338. final playerController = Get.find<IPlayerController>();
  339. bool loading = false;
  340. String content = 'xxxxxxxxxxxxxxxxxxxx';
  341. @override
  342. void initState() {
  343. playerController.frameLoadStateChanged.addListener(_onLoadStateChanged);
  344. super.initState();
  345. }
  346. @override
  347. void dispose() {
  348. playerController.frameLoadStateChanged.removeListener(_onLoadStateChanged);
  349. super.dispose();
  350. }
  351. void _onLoadStateChanged(sender, bool e) {
  352. loading = e;
  353. if (loading) {
  354. Future.delayed(
  355. const Duration(milliseconds: 100),
  356. () {
  357. setState(() {
  358. content = loading ? "Loading。。。" : "";
  359. });
  360. },
  361. );
  362. } else {
  363. setState(() {
  364. content = "";
  365. });
  366. }
  367. }
  368. @override
  369. Widget build(BuildContext context) {
  370. return Positioned(
  371. child: Center(
  372. child: Text(
  373. content,
  374. style: const TextStyle(
  375. color: Colors.white,
  376. fontSize: 28,
  377. ),
  378. ),
  379. ),
  380. top: Get.size.height * 0.44,
  381. left: 0,
  382. right: 0,
  383. );
  384. }
  385. }
  386. class _MeasureLeftBoard extends StatefulWidget {
  387. const _MeasureLeftBoard({Key? key}) : super(key: key);
  388. @override
  389. State<StatefulWidget> createState() => _MeasureLeftBoardState();
  390. }
  391. class _MeasureLeftBoardState extends State<_MeasureLeftBoard> {
  392. // ignore: non_constant_identifier_names
  393. static final C_SUPPORTED_ITEMS = <String>[
  394. MeasureTerms.Distance,
  395. MeasureTerms.Perimeter,
  396. MeasureTerms.Area,
  397. MeasureTerms.Angle,
  398. MeasureTerms.Depth,
  399. MeasureTerms.Volume,
  400. MeasureTerms.Stenosis,
  401. MeasureTerms.AbRatio,
  402. MeasureTerms.RUV,
  403. //
  404. MeasureTypes.AreaPerimeterEllipse,
  405. MeasureTypes.AreaPerimeterPolyline,
  406. MeasureTypes.AreaPerimeterSpline,
  407. ];
  408. // ignore: non_constant_identifier_names
  409. static final C_SUPPORTED_M_ITEMS = <String>[
  410. MeasureTerms.VerticalDistance,
  411. MeasureTerms.Timespan,
  412. MeasureTerms.Depth,
  413. MeasureTerms.Stenosis,
  414. MeasureTerms.AbRatio,
  415. MeasureTerms.Slope,
  416. MeasureTerms.TAMAX,
  417. MeasureTerms.Velocity,
  418. MeasureTerms.Acceleration,
  419. MeasureTerms.PSED,
  420. MeasureTerms.RI,
  421. MeasureTerms.MaxPG,
  422. "LV TEI Index",
  423. "AV Ratio",
  424. MeasureTerms.HeartRate,
  425. // MeasureTerms.PHT,
  426. "MV PHT",
  427. "Qp/Qs",
  428. ];
  429. late final List<String> passeItems;
  430. final scrollController = ScrollController();
  431. final application = Get.find<IApplication>();
  432. String modeType = 'Tissue';
  433. int activeIndex = 0;
  434. List<ItemMeta> workingItems = [];
  435. @override
  436. void initState() {
  437. // passeItems = C_SUPPORTED_ITEMS;
  438. // passeItems = TestItems.C_DISTANCE_ITEMS;
  439. passeItems = TestItems.C_TEST_ITEMS;
  440. loadItems();
  441. application.visualAreaChanged.addListener(_visualAreaChanged);
  442. super.initState();
  443. }
  444. @override
  445. dispose() {
  446. application.visualAreaChanged.removeListener(_visualAreaChanged);
  447. super.dispose();
  448. }
  449. void loadItems() {
  450. workingItems = [];
  451. var names = (modeType == "TissueTM" || modeType == "Doppler")
  452. ? C_SUPPORTED_M_ITEMS
  453. : passeItems;
  454. final workingItemDtos =
  455. MeasureTestPage.MetaDTOList.where((e) => names.contains(e['Name']));
  456. for (var map in workingItemDtos) {
  457. final dto = ItemMetaDTO.fromJson(map);
  458. final item = ItemMetaConverter(dto).output();
  459. workingItems.add(item);
  460. }
  461. }
  462. void _visualAreaChanged(sender, IVisualArea e) {
  463. if (mounted) {
  464. _setAvailableModes(e.mode.modeType.toString().split('.')[1]);
  465. // changeItemByMeta(0); //暂时不要自动切测量项
  466. setState(() {});
  467. }
  468. }
  469. void _setAvailableModes(String name) {
  470. modeType = name;
  471. loadItems();
  472. }
  473. @override
  474. Widget build(BuildContext context) {
  475. return Container(
  476. width: 300,
  477. padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
  478. child: Scrollbar(
  479. controller: scrollController,
  480. isAlwaysShown: true,
  481. child: ListView.separated(
  482. controller: scrollController,
  483. itemCount: workingItems.length,
  484. itemBuilder: (BuildContext context, int index) {
  485. final meta = workingItems[index];
  486. final active = index == activeIndex;
  487. return active
  488. ? ElevatedButton(
  489. onPressed: () {
  490. changeItemByMeta(index);
  491. },
  492. child: Text(meta.name),
  493. style: ElevatedButton.styleFrom(
  494. fixedSize: const Size.fromHeight(50),
  495. ),
  496. )
  497. : OutlinedButton(
  498. onPressed: () => changeItemByMeta(index),
  499. child: Text(meta.name),
  500. style: OutlinedButton.styleFrom(
  501. fixedSize: const Size.fromHeight(50),
  502. ),
  503. );
  504. },
  505. separatorBuilder: (BuildContext context, int index) {
  506. return const SizedBox(height: 8);
  507. },
  508. ),
  509. ),
  510. );
  511. }
  512. void changeItemByMeta(int index) {
  513. setState(() {
  514. activeIndex = index;
  515. });
  516. final meta = workingItems[index];
  517. application.switchItem(meta);
  518. print(application.activeMeasureItem?.meta.name);
  519. // handle combo item
  520. if (application.activeMeasureItem != null) {
  521. final item = application.activeMeasureItem!;
  522. if (item is ITopMeasureItem) {
  523. item.switchChild(0);
  524. }
  525. }
  526. }
  527. }
  528. class _MeasureLeftAnnotation extends StatefulWidget {
  529. const _MeasureLeftAnnotation({Key? key}) : super(key: key);
  530. @override
  531. State<StatefulWidget> createState() => _MeasureLeftAnnotationState();
  532. }
  533. class _MeasureLeftAnnotationState extends State<_MeasureLeftAnnotation> {
  534. // ignore: non_constant_identifier_names
  535. static final C_SUPPORTED_TEXTS = <String>[
  536. "肝左叶",
  537. "胆囊",
  538. "脾脏",
  539. "结石",
  540. "积液",
  541. ];
  542. final scrollController = ScrollController();
  543. final application = Get.find<IApplication>();
  544. @override
  545. void initState() {
  546. // application.switchAnnotation(AnnotationType.label, C_SUPPORTED_TEXTS[0]);
  547. // application.switchAnnotation(AnnotationType.arrow);
  548. application.switchAnnotation(AnnotationType.input);
  549. super.initState();
  550. }
  551. @override
  552. Widget build(BuildContext context) {
  553. return Container(
  554. width: 300,
  555. padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
  556. child: Scrollbar(
  557. controller: scrollController,
  558. isAlwaysShown: true,
  559. child: ListView.separated(
  560. controller: scrollController,
  561. itemCount: C_SUPPORTED_TEXTS.length,
  562. itemBuilder: (BuildContext context, int index) {
  563. final name = C_SUPPORTED_TEXTS[index];
  564. const style = TextStyle(color: Colors.white, fontSize: 16);
  565. const dragStyle = TextStyle(color: Colors.amber, fontSize: 18);
  566. // TODO: melon - set drag cursor after version updated up then 3.0
  567. // https://github.com/flutter/flutter/pull/100475
  568. return Draggable<String>(
  569. data: name,
  570. dragAnchorStrategy: (data, context, offset) {
  571. // return offset - Offset(120, 14);
  572. return Offset.zero;
  573. },
  574. child: OutlinedButton(
  575. child: Text(name, style: style),
  576. onPressed: () {
  577. application.switchAnnotation(AnnotationType.label, name);
  578. },
  579. style: OutlinedButton.styleFrom(
  580. shape: RoundedRectangleBorder(
  581. borderRadius: BorderRadius.circular(4),
  582. ),
  583. side: BorderSide(color: Colors.grey.shade100),
  584. fixedSize: const Size.fromHeight(44),
  585. ),
  586. ),
  587. feedback: Material(
  588. color: Colors.transparent,
  589. child: Text(name, style: dragStyle),
  590. ),
  591. onDragStarted: () {
  592. application.switchAnnotation(AnnotationType.label, name);
  593. },
  594. );
  595. },
  596. separatorBuilder: (BuildContext context, int index) {
  597. return const SizedBox(height: 8);
  598. },
  599. ),
  600. ),
  601. );
  602. }
  603. }
  604. class _ModeTips extends StatefulWidget {
  605. @override
  606. State<StatefulWidget> createState() => _ModeTipsState();
  607. }
  608. class _ModeTipsState extends State<_ModeTips> {
  609. IApplication? application;
  610. @override
  611. void initState() {
  612. Future.delayed(const Duration(milliseconds: 100), () {
  613. application = Get.find<IApplication>();
  614. application!.visualsLoaded.addListener(_onVisualsLoaded);
  615. });
  616. super.initState();
  617. }
  618. @override
  619. void dispose() {
  620. application!.visualsLoaded.removeListener(_onVisualsLoaded);
  621. super.dispose();
  622. }
  623. void _onVisualsLoaded(Object sender, void e) {
  624. setState(() {});
  625. }
  626. @override
  627. Widget build(BuildContext context) {
  628. if (application == null) return const Text("Wait");
  629. return Material(
  630. child: Text(application!.avaliableModes.map((e) => e.name).join('/')),
  631. );
  632. }
  633. }