measure_page_test.dart 20 KB

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