choose_social.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. import 'dart:convert';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_svg/svg.dart';
  4. import 'package:get/get.dart';
  5. import '../constants.dart';
  6. import '../utils/api.dart';
  7. import '../utils/http_utils.dart';
  8. import '../utils/size_fit.dart';
  9. import '../utils/storage.dart';
  10. import 'component/top_container.dart';
  11. class ChooseSocial extends StatelessWidget {
  12. const ChooseSocial({Key? key}) : super(key: key);
  13. @override
  14. Widget build(BuildContext context) {
  15. return Material(
  16. color: kBgColor, child: TopContainer(child: ChooseSocialContent()));
  17. }
  18. }
  19. class ChooseSocialContent extends StatefulWidget {
  20. const ChooseSocialContent({Key? key}) : super(key: key);
  21. @override
  22. State<ChooseSocialContent> createState() => _ChooseSocialContentState();
  23. }
  24. class _ChooseSocialContentState extends State<ChooseSocialContent> {
  25. String keyword = '';
  26. List socials = [];
  27. List searchs = [];
  28. bool isFirstEnter = true;
  29. @override
  30. void initState() {
  31. Map<String, dynamic> data = Get.parameters;
  32. if (data.isNotEmpty) {
  33. isFirstEnter = data['add_more'] != '1';
  34. }
  35. getLinks();
  36. // TODO: implement initState
  37. super.initState();
  38. }
  39. Future getLinks() async {
  40. var data = await HttpUtils.get(Api.socials);
  41. // print(data.toString());
  42. if (data['data'] is List) {
  43. print(data['data'].toString());
  44. setState(() {
  45. socials = data['data'];
  46. });
  47. }
  48. }
  49. Widget item(var social) {
  50. return SocialItem(
  51. keyword: keyword, social: social, isFirstEnter: isFirstEnter);
  52. }
  53. @override
  54. Widget build(BuildContext context) {
  55. SizeFit.initialize(context);
  56. return Stack(children: [
  57. SingleChildScrollView(
  58. child: Column(
  59. children: [
  60. SizedBox(
  61. height: 72.px,
  62. ),
  63. if (keyword.isEmpty)
  64. Container(
  65. width: 375.px,
  66. alignment: Alignment.topLeft,
  67. padding: EdgeInsets.only(left: 28.px, bottom: 5.px),
  68. child: Text(
  69. '热门平台',
  70. textAlign: TextAlign.left,
  71. style: TextStyle(
  72. color: const Color(0xFF74747A),
  73. fontSize: 12.px,
  74. fontWeight: FontWeight.bold),
  75. ),
  76. ),
  77. ...List<Widget>.generate(socials.length, (i) {
  78. return item(socials[i]);
  79. }),
  80. if (keyword.isNotEmpty)
  81. GestureDetector(
  82. onTap: () {
  83. var social = {
  84. "id": "",
  85. "name": keyword,
  86. "logo": "",
  87. "types": ["LINK", "QR"],
  88. "description_example": "",
  89. "url_example": ""
  90. };
  91. if (Get.parameters['update'] == '1') {
  92. Get.back(result: social);
  93. return;
  94. }
  95. Get.toNamed('/add_link', parameters: {
  96. 'social': jsonEncode(social),
  97. 'add_more': isFirstEnter ? '0' : '1'
  98. });
  99. StorageUtil().remove('tempGuideLink0');
  100. // Get.toNamed('/add_link');
  101. },
  102. child: Container(
  103. // height: 50.px,
  104. alignment: Alignment.centerLeft,
  105. padding: EdgeInsets.fromLTRB(30.px, 8.px, 30.px, 8.px),
  106. child: Row(
  107. children: [
  108. SvgPicture.asset(
  109. 'assets/icons/other_link.svg',
  110. width: 32.px,
  111. height: 32.px,
  112. ),
  113. SizedBox(
  114. width: 13.px,
  115. ),
  116. Text(
  117. '用“',
  118. style:
  119. TextStyle(color: Colors.white, fontSize: 14.px),
  120. ),
  121. Text(
  122. keyword,
  123. style: TextStyle(color: kBtnColor, fontSize: 14.px),
  124. ),
  125. Text(
  126. '”做平台名称',
  127. style:
  128. TextStyle(color: Colors.white, fontSize: 14.px),
  129. )
  130. ],
  131. ),
  132. ),
  133. ),
  134. SizedBox(
  135. height: 50.px,
  136. )
  137. ],
  138. ),
  139. ),
  140. Positioned(
  141. left: 0,
  142. right: 0,
  143. bottom: 0,
  144. height: 48.px,
  145. child: Container(
  146. decoration: const BoxDecoration(
  147. gradient: LinearGradient(
  148. begin: Alignment.topCenter,
  149. end: Alignment.bottomCenter,
  150. colors: [
  151. Color(0x00131314),
  152. Color(0xFF131314),
  153. Color(0xFF131314),
  154. ])),
  155. )),
  156. Column(
  157. children: [
  158. Container(
  159. height: 72.px,
  160. padding: EdgeInsets.only(left: 16.px),
  161. decoration: const BoxDecoration(
  162. gradient: LinearGradient(
  163. begin: Alignment.topCenter,
  164. end: Alignment.bottomCenter,
  165. colors: [
  166. Color(0xFF131314),
  167. Color(0xFF131314),
  168. Color(0xFF131314),
  169. Color(0x00131314)
  170. ])),
  171. child: Row(
  172. children: [
  173. Expanded(
  174. child: Container(
  175. height: 40.px,
  176. padding: EdgeInsets.only(left: 10.px, right: 10.px),
  177. decoration: BoxDecoration(
  178. borderRadius: BorderRadius.circular(20.px),
  179. color: const Color(0xFF2C2C2E)),
  180. child: Row(
  181. children: [
  182. SvgPicture.asset(
  183. 'assets/icons/search.svg',
  184. width: 16.px,
  185. height: 16.px,
  186. ),
  187. SizedBox(
  188. width: 12.px,
  189. ),
  190. Expanded(
  191. child: Container(
  192. height: 40.px,
  193. color: Colors.transparent,
  194. alignment: Alignment.centerLeft,
  195. child: TextField(
  196. autofocus: true,
  197. cursorColor: kBtnColor,
  198. onChanged: (value) {
  199. setState(() {
  200. keyword = value;
  201. });
  202. },
  203. style:
  204. TextStyle(color: Colors.white, fontSize: 14.px),
  205. decoration: InputDecoration(
  206. border: InputBorder.none,
  207. hintText: '请输入平台名称',
  208. hintStyle: TextStyle(
  209. fontSize: 14.px,
  210. color: const Color(0xFF74747A)),
  211. counterText: "",
  212. ),
  213. ),
  214. ))
  215. ],
  216. ),
  217. )),
  218. GestureDetector(
  219. onTap: () {
  220. Get.back();
  221. },
  222. child: Container(
  223. width: 72.px,
  224. alignment: Alignment.center,
  225. child: Text(
  226. '取消',
  227. style: TextStyle(color: kThemeColor, fontSize: 14.px),
  228. ),
  229. ),
  230. )
  231. ],
  232. ),
  233. )
  234. ],
  235. ),
  236. ]);
  237. }
  238. }
  239. class SocialItem extends StatefulWidget {
  240. String keyword;
  241. bool isFirstEnter;
  242. var social;
  243. SocialItem(
  244. {Key? key,
  245. required this.keyword,
  246. required this.social,
  247. required this.isFirstEnter})
  248. : super(key: key);
  249. @override
  250. State<SocialItem> createState() => _SocialItemState();
  251. }
  252. class _SocialItemState extends State<SocialItem> {
  253. bool isSeleted = false;
  254. @override
  255. Widget build(BuildContext context) {
  256. if (widget.keyword.isEmpty) {
  257. return GestureDetector(
  258. onTapDown: (TapDownDetails details) {
  259. setState(() {
  260. isSeleted = true;
  261. });
  262. },
  263. onTapUp: (TapUpDetails details) {
  264. setState(() {
  265. isSeleted = false;
  266. });
  267. },
  268. onTapCancel: () {
  269. setState(() {
  270. isSeleted = false;
  271. });
  272. },
  273. onTap: () {
  274. isSeleted = true;
  275. if (Get.parameters['update'] == '1') {
  276. Get.back(result: widget.social);
  277. return;
  278. }
  279. Get.toNamed('/add_link', parameters: {
  280. 'social': jsonEncode(widget.social),
  281. 'add_more': widget.isFirstEnter ? '0' : '1'
  282. });
  283. StorageUtil().remove('tempGuideLink0');
  284. },
  285. child: Container(
  286. // height: 50.px,
  287. alignment: Alignment.centerLeft,
  288. padding: EdgeInsets.fromLTRB(12.px, 8.px, 12.px, 8.px),
  289. margin: EdgeInsets.only(left: 16.px, right: 16.px),
  290. decoration: BoxDecoration(
  291. color: isSeleted ? const Color(0xFF2C2C2E) : Colors.transparent,
  292. borderRadius: BorderRadius.circular(12.px)),
  293. child: Row(
  294. children: [
  295. ClipRRect(
  296. borderRadius: BorderRadius.circular(8.rpx),
  297. child: Container(
  298. width: 32.px,
  299. height: 32.px,
  300. decoration: BoxDecoration(
  301. image: DecorationImage(
  302. image: NetworkImage(widget.social['logo']),
  303. fit: BoxFit.cover)),
  304. ),
  305. ),
  306. SizedBox(
  307. width: 13.px,
  308. ),
  309. Text(
  310. widget.social['name'],
  311. style: TextStyle(color: Colors.white, fontSize: 14.px),
  312. )
  313. ],
  314. ),
  315. ),
  316. );
  317. }
  318. if (widget.social['name'].indexOf(widget.keyword) == -1) {
  319. return const SizedBox(
  320. width: 0,
  321. height: 0,
  322. );
  323. }
  324. List<TextSpan> spans = [];
  325. int start = 0; // 当前要截取字符串的起始位置
  326. int end; // end 表示要高亮显示的文本出现在当前字符串中的索引
  327. String text = widget.social['name']; //'中国中 86';
  328. String lightText = widget.keyword; //'中';
  329. // 如果有符合的高亮文字
  330. while ((end = text.indexOf(lightText, start)) != -1) {
  331. // 第一步:添加正常显示的文本
  332. spans.add(TextSpan(
  333. text: text.substring(start, end),
  334. style: TextStyle(color: Colors.white, fontSize: 14.px)));
  335. // 第二步:添加高亮显示的文本
  336. spans.add(TextSpan(
  337. text: lightText,
  338. style: TextStyle(color: const Color(0xFFFF9900), fontSize: 14.px)));
  339. // 设置下一段要截取的开始位置
  340. start = end + lightText.length;
  341. }
  342. // 下面这行代码的意思是
  343. // 如果没有要高亮显示的,则start=0,也就是返回了传进来的text
  344. // 如果有要高亮显示的,则start=最后一个高亮显示文本的索引,然后截取到text的末尾
  345. spans.add(
  346. TextSpan(
  347. text: text.substring(start, text.length),
  348. style: TextStyle(color: Colors.white, fontSize: 14.px)),
  349. );
  350. return GestureDetector(
  351. onTapDown: (TapDownDetails details) {
  352. setState(() {
  353. isSeleted = true;
  354. });
  355. },
  356. onTapUp: (TapUpDetails details) {
  357. setState(() {
  358. isSeleted = false;
  359. });
  360. },
  361. onTapCancel: () {
  362. setState(() {
  363. isSeleted = false;
  364. });
  365. },
  366. onTap: () {
  367. isSeleted = true;
  368. print(Get.parameters['update'].toString());
  369. if (Get.parameters['update'] == '1') {
  370. Get.back(result: widget.social);
  371. return;
  372. }
  373. Get.toNamed('/add_link', parameters: {
  374. 'social': jsonEncode(widget.social),
  375. 'add_more': widget.isFirstEnter ? '0' : '1'
  376. });
  377. StorageUtil().remove('tempGuideLink0');
  378. },
  379. child: Container(
  380. // height: 50.px,
  381. alignment: Alignment.centerLeft,
  382. padding: EdgeInsets.fromLTRB(12.px, 8.px, 12.px, 8.px),
  383. margin: EdgeInsets.only(left: 16.px, right: 16.px),
  384. decoration: BoxDecoration(
  385. color: isSeleted ? const Color(0xFF2C2C2E) : Colors.transparent,
  386. borderRadius: BorderRadius.circular(12.px)),
  387. child: Row(
  388. children: [
  389. ClipRRect(
  390. borderRadius: BorderRadius.circular(6.rpx),
  391. child: Container(
  392. width: 32.px,
  393. height: 32.px,
  394. decoration: BoxDecoration(
  395. image: DecorationImage(
  396. image: NetworkImage(widget.social['logo']),
  397. fit: BoxFit.cover)),
  398. ),
  399. ),
  400. SizedBox(
  401. width: 13.px,
  402. ),
  403. RichText(text: TextSpan(children: spans))
  404. ],
  405. ),
  406. ),
  407. );
  408. }
  409. }