my_group.dart 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import 'dart:math';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_hbb/common/hbbs/hbbs.dart';
  4. import 'package:flutter_hbb/common/widgets/login.dart';
  5. import 'package:flutter_hbb/common/widgets/peers_view.dart';
  6. import 'package:flutter_hbb/models/state_model.dart';
  7. import 'package:get/get.dart';
  8. import '../../common.dart';
  9. class MyGroup extends StatefulWidget {
  10. final EdgeInsets? menuPadding;
  11. const MyGroup({Key? key, this.menuPadding}) : super(key: key);
  12. @override
  13. State<StatefulWidget> createState() {
  14. return _MyGroupState();
  15. }
  16. }
  17. class _MyGroupState extends State<MyGroup> {
  18. RxBool get isSelectedDeviceGroup => gFFI.groupModel.isSelectedDeviceGroup;
  19. RxString get selectedAccessibleItemName =>
  20. gFFI.groupModel.selectedAccessibleItemName;
  21. RxString get searchAccessibleItemNameText =>
  22. gFFI.groupModel.searchAccessibleItemNameText;
  23. static TextEditingController searchUserController = TextEditingController();
  24. @override
  25. Widget build(BuildContext context) {
  26. return Obx(() {
  27. if (!gFFI.userModel.isLogin) {
  28. return Center(
  29. child: ElevatedButton(
  30. onPressed: loginDialog, child: Text(translate("Login"))));
  31. } else if (gFFI.userModel.networkError.isNotEmpty) {
  32. return netWorkErrorWidget();
  33. } else if (gFFI.groupModel.groupLoading.value && gFFI.groupModel.emtpy) {
  34. return const Center(
  35. child: CircularProgressIndicator(),
  36. );
  37. }
  38. return Column(
  39. children: [
  40. buildErrorBanner(context,
  41. loading: gFFI.groupModel.groupLoading,
  42. err: gFFI.groupModel.groupLoadError,
  43. retry: null,
  44. close: () => gFFI.groupModel.groupLoadError.value = ''),
  45. Expanded(
  46. child: Obx(() => stateGlobal.isPortrait.isTrue
  47. ? _buildPortrait()
  48. : _buildLandscape())),
  49. ],
  50. );
  51. });
  52. }
  53. Widget _buildLandscape() {
  54. return Row(
  55. children: [
  56. Container(
  57. decoration: BoxDecoration(
  58. borderRadius: BorderRadius.circular(12),
  59. border:
  60. Border.all(color: Theme.of(context).colorScheme.background)),
  61. child: Container(
  62. width: 150,
  63. height: double.infinity,
  64. child: Column(
  65. children: [
  66. _buildLeftHeader(),
  67. Expanded(
  68. child: Container(
  69. width: double.infinity,
  70. height: double.infinity,
  71. child: _buildLeftList(),
  72. ),
  73. )
  74. ],
  75. ),
  76. ),
  77. ).marginOnly(right: 12.0),
  78. Expanded(
  79. child: Align(
  80. alignment: Alignment.topLeft,
  81. child: MyGroupPeerView(
  82. menuPadding: widget.menuPadding,
  83. )),
  84. )
  85. ],
  86. );
  87. }
  88. Widget _buildPortrait() {
  89. return Column(
  90. children: [
  91. Container(
  92. decoration: BoxDecoration(
  93. borderRadius: BorderRadius.circular(6),
  94. border:
  95. Border.all(color: Theme.of(context).colorScheme.background)),
  96. child: Container(
  97. child: Column(
  98. mainAxisSize: MainAxisSize.min,
  99. children: [
  100. _buildLeftHeader(),
  101. Container(
  102. width: double.infinity,
  103. child: _buildLeftList(),
  104. )
  105. ],
  106. ),
  107. ),
  108. ).marginOnly(bottom: 12.0),
  109. Expanded(
  110. child: Align(
  111. alignment: Alignment.topLeft,
  112. child: MyGroupPeerView(
  113. menuPadding: widget.menuPadding,
  114. )),
  115. )
  116. ],
  117. );
  118. }
  119. Widget _buildLeftHeader() {
  120. final fontSize = 14.0;
  121. return Row(
  122. children: [
  123. Expanded(
  124. child: TextField(
  125. controller: searchUserController,
  126. onChanged: (value) {
  127. searchAccessibleItemNameText.value = value;
  128. },
  129. textAlignVertical: TextAlignVertical.center,
  130. style: TextStyle(fontSize: fontSize),
  131. decoration: InputDecoration(
  132. filled: false,
  133. prefixIcon: Icon(
  134. Icons.search_rounded,
  135. color: Theme.of(context).hintColor,
  136. ).paddingOnly(top: 2),
  137. hintText: translate("Search"),
  138. hintStyle: TextStyle(fontSize: fontSize),
  139. border: InputBorder.none,
  140. isDense: true,
  141. ),
  142. ).workaroundFreezeLinuxMint()),
  143. ],
  144. );
  145. }
  146. Widget _buildLeftList() {
  147. return Obx(() {
  148. final userItems = gFFI.groupModel.users.where((p0) {
  149. if (searchAccessibleItemNameText.isNotEmpty) {
  150. return p0.name
  151. .toLowerCase()
  152. .contains(searchAccessibleItemNameText.value.toLowerCase());
  153. }
  154. return true;
  155. }).toList();
  156. final deviceGroupItems = gFFI.groupModel.deviceGroups.where((p0) {
  157. if (searchAccessibleItemNameText.isNotEmpty) {
  158. return p0.name
  159. .toLowerCase()
  160. .contains(searchAccessibleItemNameText.value.toLowerCase());
  161. }
  162. return true;
  163. }).toList();
  164. listView(bool isPortrait) => ListView.builder(
  165. shrinkWrap: isPortrait,
  166. itemCount: deviceGroupItems.length + userItems.length,
  167. itemBuilder: (context, index) => index < deviceGroupItems.length
  168. ? _buildDeviceGroupItem(deviceGroupItems[index])
  169. : _buildUserItem(userItems[index - deviceGroupItems.length]));
  170. var maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
  171. return Obx(() => stateGlobal.isPortrait.isFalse
  172. ? listView(false)
  173. : LimitedBox(maxHeight: maxHeight, child: listView(true)));
  174. });
  175. }
  176. Widget _buildUserItem(UserPayload user) {
  177. final username = user.name;
  178. return InkWell(onTap: () {
  179. isSelectedDeviceGroup.value = false;
  180. if (selectedAccessibleItemName.value != username) {
  181. selectedAccessibleItemName.value = username;
  182. } else {
  183. selectedAccessibleItemName.value = '';
  184. }
  185. }, child: Obx(
  186. () {
  187. bool selected = !isSelectedDeviceGroup.value &&
  188. selectedAccessibleItemName.value == username;
  189. final isMe = username == gFFI.userModel.userName.value;
  190. final colorMe = MyTheme.color(context).me!;
  191. return Container(
  192. decoration: BoxDecoration(
  193. color: selected ? MyTheme.color(context).highlight : null,
  194. border: Border(
  195. bottom: BorderSide(
  196. width: 0.7,
  197. color: Theme.of(context).dividerColor.withOpacity(0.1))),
  198. ),
  199. child: Container(
  200. child: Row(
  201. children: [
  202. Container(
  203. width: 20,
  204. height: 20,
  205. decoration: BoxDecoration(
  206. color: str2color(username, 0xAF),
  207. shape: BoxShape.circle,
  208. ),
  209. child: Align(
  210. alignment: Alignment.center,
  211. child: Center(
  212. child: Text(
  213. username.characters.first.toUpperCase(),
  214. style: TextStyle(color: Colors.white),
  215. textAlign: TextAlign.center,
  216. ),
  217. ),
  218. ),
  219. ).marginOnly(right: 4),
  220. if (isMe) Flexible(child: Text(username)),
  221. if (isMe)
  222. Flexible(
  223. child: Container(
  224. margin: EdgeInsets.only(left: 5),
  225. padding: EdgeInsets.symmetric(horizontal: 3, vertical: 1),
  226. decoration: BoxDecoration(
  227. color: colorMe.withAlpha(20),
  228. borderRadius: BorderRadius.all(Radius.circular(2)),
  229. border: Border.all(color: colorMe.withAlpha(100))),
  230. child: Text(
  231. translate('Me'),
  232. style: TextStyle(
  233. color: colorMe.withAlpha(200), fontSize: 12),
  234. ),
  235. ),
  236. ),
  237. if (!isMe) Expanded(child: Text(username)),
  238. ],
  239. ).paddingSymmetric(vertical: 4),
  240. ),
  241. );
  242. },
  243. )).marginSymmetric(horizontal: 12).marginOnly(bottom: 6);
  244. }
  245. Widget _buildDeviceGroupItem(DeviceGroupPayload deviceGroup) {
  246. final name = deviceGroup.name;
  247. return InkWell(onTap: () {
  248. isSelectedDeviceGroup.value = true;
  249. if (selectedAccessibleItemName.value != name) {
  250. selectedAccessibleItemName.value = name;
  251. } else {
  252. selectedAccessibleItemName.value = '';
  253. }
  254. }, child: Obx(
  255. () {
  256. bool selected = isSelectedDeviceGroup.value &&
  257. selectedAccessibleItemName.value == name;
  258. return Container(
  259. decoration: BoxDecoration(
  260. color: selected ? MyTheme.color(context).highlight : null,
  261. border: Border(
  262. bottom: BorderSide(
  263. width: 0.7,
  264. color: Theme.of(context).dividerColor.withOpacity(0.1))),
  265. ),
  266. child: Container(
  267. child: Row(
  268. children: [
  269. Container(
  270. width: 20,
  271. height: 20,
  272. child: Icon(IconFont.deviceGroupOutline,
  273. color: MyTheme.accent, size: 19),
  274. ).marginOnly(right: 4),
  275. Expanded(child: Text(name)),
  276. ],
  277. ).paddingSymmetric(vertical: 4),
  278. ),
  279. );
  280. },
  281. )).marginSymmetric(horizontal: 12).marginOnly(bottom: 6);
  282. }
  283. }