my_group.dart 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. selectedAccessibleItemName.value = '';
  129. },
  130. textAlignVertical: TextAlignVertical.center,
  131. style: TextStyle(fontSize: fontSize),
  132. decoration: InputDecoration(
  133. filled: false,
  134. prefixIcon: Icon(
  135. Icons.search_rounded,
  136. color: Theme.of(context).hintColor,
  137. ).paddingOnly(top: 2),
  138. hintText: translate("Search"),
  139. hintStyle: TextStyle(fontSize: fontSize),
  140. border: InputBorder.none,
  141. isDense: true,
  142. ),
  143. ).workaroundFreezeLinuxMint()),
  144. ],
  145. );
  146. }
  147. Widget _buildLeftList() {
  148. return Obx(() {
  149. final userItems = gFFI.groupModel.users.where((p0) {
  150. if (searchAccessibleItemNameText.isNotEmpty) {
  151. return p0.name
  152. .toLowerCase()
  153. .contains(searchAccessibleItemNameText.value.toLowerCase());
  154. }
  155. return true;
  156. }).toList();
  157. final deviceGroupItems = gFFI.groupModel.deviceGroups.where((p0) {
  158. if (searchAccessibleItemNameText.isNotEmpty) {
  159. return p0.name
  160. .toLowerCase()
  161. .contains(searchAccessibleItemNameText.value.toLowerCase());
  162. }
  163. return true;
  164. }).toList();
  165. listView(bool isPortrait) => ListView.builder(
  166. shrinkWrap: isPortrait,
  167. itemCount: deviceGroupItems.length + userItems.length,
  168. itemBuilder: (context, index) => index < deviceGroupItems.length
  169. ? _buildDeviceGroupItem(deviceGroupItems[index])
  170. : _buildUserItem(userItems[index - deviceGroupItems.length]));
  171. var maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
  172. return Obx(() => stateGlobal.isPortrait.isFalse
  173. ? listView(false)
  174. : LimitedBox(maxHeight: maxHeight, child: listView(true)));
  175. });
  176. }
  177. Widget _buildUserItem(UserPayload user) {
  178. final username = user.name;
  179. return InkWell(onTap: () {
  180. isSelectedDeviceGroup.value = false;
  181. if (selectedAccessibleItemName.value != username) {
  182. selectedAccessibleItemName.value = username;
  183. } else {
  184. selectedAccessibleItemName.value = '';
  185. }
  186. }, child: Obx(
  187. () {
  188. bool selected = !isSelectedDeviceGroup.value &&
  189. selectedAccessibleItemName.value == username;
  190. final isMe = username == gFFI.userModel.userName.value;
  191. final colorMe = MyTheme.color(context).me!;
  192. return Container(
  193. decoration: BoxDecoration(
  194. color: selected ? MyTheme.color(context).highlight : null,
  195. border: Border(
  196. bottom: BorderSide(
  197. width: 0.7,
  198. color: Theme.of(context).dividerColor.withOpacity(0.1))),
  199. ),
  200. child: Container(
  201. child: Row(
  202. children: [
  203. Container(
  204. width: 20,
  205. height: 20,
  206. decoration: BoxDecoration(
  207. color: str2color(username, 0xAF),
  208. shape: BoxShape.circle,
  209. ),
  210. child: Align(
  211. alignment: Alignment.center,
  212. child: Center(
  213. child: Text(
  214. username.characters.first.toUpperCase(),
  215. style: TextStyle(color: Colors.white),
  216. textAlign: TextAlign.center,
  217. ),
  218. ),
  219. ),
  220. ).marginOnly(right: 4),
  221. if (isMe) Flexible(child: Text(username)),
  222. if (isMe)
  223. Flexible(
  224. child: Container(
  225. margin: EdgeInsets.only(left: 5),
  226. padding: EdgeInsets.symmetric(horizontal: 3, vertical: 1),
  227. decoration: BoxDecoration(
  228. color: colorMe.withAlpha(20),
  229. borderRadius: BorderRadius.all(Radius.circular(2)),
  230. border: Border.all(color: colorMe.withAlpha(100))),
  231. child: Text(
  232. translate('Me'),
  233. style: TextStyle(
  234. color: colorMe.withAlpha(200), fontSize: 12),
  235. ),
  236. ),
  237. ),
  238. if (!isMe) Expanded(child: Text(username)),
  239. ],
  240. ).paddingSymmetric(vertical: 4),
  241. ),
  242. );
  243. },
  244. )).marginSymmetric(horizontal: 12).marginOnly(bottom: 6);
  245. }
  246. Widget _buildDeviceGroupItem(DeviceGroupPayload deviceGroup) {
  247. final name = deviceGroup.name;
  248. return InkWell(onTap: () {
  249. isSelectedDeviceGroup.value = true;
  250. if (selectedAccessibleItemName.value != name) {
  251. selectedAccessibleItemName.value = name;
  252. } else {
  253. selectedAccessibleItemName.value = '';
  254. }
  255. }, child: Obx(
  256. () {
  257. bool selected = isSelectedDeviceGroup.value &&
  258. selectedAccessibleItemName.value == name;
  259. return Container(
  260. decoration: BoxDecoration(
  261. color: selected ? MyTheme.color(context).highlight : null,
  262. border: Border(
  263. bottom: BorderSide(
  264. width: 0.7,
  265. color: Theme.of(context).dividerColor.withOpacity(0.1))),
  266. ),
  267. child: Container(
  268. child: Row(
  269. children: [
  270. Container(
  271. width: 20,
  272. height: 20,
  273. child: Icon(IconFont.deviceGroupOutline,
  274. color: MyTheme.accent, size: 19),
  275. ).marginOnly(right: 4),
  276. Expanded(child: Text(name)),
  277. ],
  278. ).paddingSymmetric(vertical: 4),
  279. ),
  280. );
  281. },
  282. )).marginSymmetric(horizontal: 12).marginOnly(bottom: 6);
  283. }
  284. }