keyboard_manage_page.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. import 'package:dotted_border/dotted_border.dart';
  2. import 'package:dropdown_button2/dropdown_button2.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter_screenutil/flutter_screenutil.dart';
  5. import 'package:get/get.dart';
  6. import 'package:keyboard/base/base_page.dart';
  7. import 'package:keyboard/data/bean/keyboard_info.dart';
  8. import 'package:keyboard/module/keyboard_manage/keyboard_manage_controller.dart';
  9. import 'package:keyboard/resource/string.gen.dart';
  10. import 'package:reorderables/reorderables.dart';
  11. import '../../data/bean/character_info.dart';
  12. import '../../resource/assets.gen.dart';
  13. import '../../router/app_pages.dart';
  14. import '../../widget/gradient_rect_slider_track_shape.dart';
  15. import '../../widget/tab_custom_gradient_indicator.dart';
  16. class KeyboardManagePage extends BasePage<KeyboardManageController> {
  17. const KeyboardManagePage({super.key});
  18. @override
  19. immersive() {
  20. return true;
  21. }
  22. static Future start({KeyboardInfo? customKeyboardInfo}) async {
  23. return Get.toNamed(
  24. RoutePath.keyboardManage,
  25. arguments: {"customKeyboardInfo": customKeyboardInfo},
  26. );
  27. }
  28. @override
  29. Widget buildBody(BuildContext context) {
  30. return Container(
  31. decoration: BoxDecoration(
  32. image: DecorationImage(
  33. image: Assets.images.bgKeyboardManage.provider(),
  34. fit: BoxFit.fill,
  35. ),
  36. ),
  37. child: SafeArea(
  38. child: Container(
  39. alignment: Alignment.topCenter,
  40. child: Column(
  41. children: [
  42. // TabBar
  43. _buildTitle(),
  44. SizedBox(height: 10.h),
  45. Expanded(
  46. child: Container(
  47. decoration: ShapeDecoration(
  48. color: Colors.white,
  49. shape: RoundedRectangleBorder(
  50. borderRadius: BorderRadius.only(
  51. topLeft: Radius.circular(20.r),
  52. topRight: Radius.circular(20.r),
  53. ),
  54. ),
  55. ),
  56. child: PageView(
  57. controller: controller.pageController,
  58. onPageChanged: (index) {
  59. controller.switchPageKeyboardType(index);
  60. },
  61. children: [
  62. _buildKeyboardSettings(isCustom: true),
  63. _buildKeyboardSettings(isCustom: false),
  64. ],
  65. ),
  66. ),
  67. ),
  68. ],
  69. ),
  70. ),
  71. ),
  72. );
  73. }
  74. Widget _buildTitle() {
  75. return Padding(
  76. padding: EdgeInsets.symmetric(horizontal: 16.w),
  77. child: Row(
  78. children: [
  79. GestureDetector(
  80. onTap: controller.clickBack,
  81. child: Assets.images.iconMineBackArrow.image(
  82. width: 24.w,
  83. height: 24.w,
  84. ),
  85. ),
  86. Expanded(
  87. child: TabBar(
  88. // onTap: controller.switchTabKeyboardType,
  89. controller: controller.tabController,
  90. tabs:
  91. controller.keyboardManageType
  92. .map((e) => Tab(text: e))
  93. .toList(),
  94. dividerHeight: 0,
  95. indicator: TabCustomGradientIndicator(),
  96. labelStyle: TextStyle(
  97. color: Colors.black.withAlpha(204),
  98. fontSize: 17.sp,
  99. fontWeight: FontWeight.w500,
  100. ),
  101. unselectedLabelStyle: TextStyle(
  102. color: Colors.black.withAlpha(102),
  103. fontSize: 17.sp,
  104. fontWeight: FontWeight.w500,
  105. ),
  106. ),
  107. ),
  108. SizedBox(width: 24.w),
  109. ],
  110. ),
  111. );
  112. }
  113. Widget _buildKeyboardSettings({required bool isCustom}) {
  114. return Column(
  115. children: [
  116. Expanded(
  117. child: SingleChildScrollView(
  118. child: Column(
  119. crossAxisAlignment: CrossAxisAlignment.start,
  120. children: [
  121. if (isCustom) _buildDropdownButton(), // 只有自定义显示下拉
  122. _buildIntimacySlider(isCustom: isCustom),
  123. _buildKeyboardCharacter(isCustom: isCustom),
  124. ],
  125. ),
  126. ),
  127. ),
  128. _buildSaveButton(isCustom: isCustom),
  129. ],
  130. );
  131. }
  132. Widget _buildDropdownButton() {
  133. return Obx(() {
  134. return Padding(
  135. padding: EdgeInsets.only(left: 16.w, top: 24.h, right: 16.w),
  136. child: Row(
  137. children: [
  138. Text(
  139. "自己&",
  140. textAlign: TextAlign.center,
  141. style: TextStyle(
  142. color: Colors.black.withAlpha(204),
  143. fontSize: 16.sp,
  144. fontWeight: FontWeight.w500,
  145. ),
  146. ),
  147. DropdownButton2<String>(
  148. isDense: true,
  149. underline: Container(height: 0),
  150. customButton: Row(
  151. children: [
  152. Text(
  153. controller.currentCustomKeyboardInfo.name ?? "",
  154. style: TextStyle(
  155. color: Colors.black.withAlpha(204),
  156. fontSize: 16.sp,
  157. fontWeight: FontWeight.w500,
  158. ),
  159. ),
  160. Assets.images.iconModeSwitchArrow.image(
  161. width: 20.r,
  162. height: 20.r,
  163. ),
  164. ],
  165. ),
  166. dropdownSeparator: DropdownSeparator<String>(
  167. height: 1,
  168. child: Padding(
  169. padding: EdgeInsets.symmetric(horizontal: 15.w),
  170. child: Divider(height: 1, color: Color(0xFFF6F6F6)),
  171. ),
  172. ),
  173. // 选项改变回调
  174. onChanged: (String? newValue) {
  175. controller.switchCustomKeyboard(newValue);
  176. },
  177. // 生成下拉菜单项
  178. items: List.generate(controller.customKeyboardInfoList.length, (
  179. index,
  180. ) {
  181. String? value = controller.customKeyboardInfoList[index].name;
  182. return DropdownItem<String>(
  183. value: value,
  184. child: Column(
  185. crossAxisAlignment: CrossAxisAlignment.start,
  186. mainAxisSize: MainAxisSize.min,
  187. children: [
  188. Container(
  189. child: Text(
  190. value ?? "",
  191. style: TextStyle(
  192. color: Colors.black.withAlpha(204),
  193. fontSize: 14.sp,
  194. fontWeight: FontWeight.w400,
  195. ),
  196. ),
  197. ),
  198. ],
  199. ),
  200. );
  201. }),
  202. dropdownStyleData: DropdownStyleData(
  203. // 下拉菜单最大高度
  204. direction: DropdownDirection.left,
  205. maxHeight: 250.w,
  206. width: 102.w,
  207. decoration: BoxDecoration(
  208. color: Colors.white,
  209. borderRadius: BorderRadius.circular(8.w),
  210. ),
  211. ),
  212. ),
  213. ],
  214. ),
  215. );
  216. });
  217. }
  218. Widget _buildIntimacySlider({required bool isCustom}) {
  219. return // 亲密度模块
  220. Container(
  221. margin: EdgeInsets.only(left: 16.w, top: 24.h, right: 16.w),
  222. padding: EdgeInsets.only(
  223. left: 16.w,
  224. top: 23.h,
  225. right: 16.w,
  226. bottom: 26.h,
  227. ),
  228. decoration: BoxDecoration(
  229. image: DecorationImage(
  230. image: Assets.images.bgKeyboardManageIntimacy.provider(),
  231. fit: BoxFit.fill,
  232. ),
  233. borderRadius: BorderRadius.circular(10.r),
  234. ),
  235. child: Column(
  236. children: [
  237. // 亲密度
  238. Row(
  239. crossAxisAlignment: CrossAxisAlignment.center,
  240. children: [
  241. Assets.images.iconKeyboardManageFavorite.image(
  242. width: 20.w,
  243. height: 20.w,
  244. ),
  245. Assets.images.iconKeyboardManageIntimacyText.image(
  246. width: 48.w,
  247. height: 19.h,
  248. ),
  249. const Spacer(),
  250. Container(
  251. alignment: Alignment.center,
  252. width: 81.w,
  253. height: 28.h,
  254. decoration: ShapeDecoration(
  255. color: const Color(0xFFE1E0E7),
  256. shape: RoundedRectangleBorder(
  257. borderRadius: BorderRadius.circular(16.r),
  258. ),
  259. ),
  260. child: Obx(() {
  261. return Text(
  262. isCustom
  263. ? '${StringName.intimacy}${controller.currentCustomIntimacy}%'
  264. : '${StringName.intimacy}${controller.currentGeneralIntimacy}%',
  265. textAlign: TextAlign.right,
  266. style: TextStyle(
  267. color: Colors.black.withAlpha(204),
  268. fontSize: 12.sp,
  269. fontWeight: FontWeight.w400,
  270. ),
  271. );
  272. }),
  273. ),
  274. ],
  275. ),
  276. SizedBox(height: 19.h),
  277. Builder(
  278. builder: (context) {
  279. return SliderTheme(
  280. data: SliderTheme.of(context).copyWith(
  281. trackShape: const GradientRectSliderTrackShape(),
  282. trackHeight: 8.h,
  283. thumbColor: Colors.white,
  284. thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7.r),
  285. overlayShape: const RoundSliderOverlayShape(
  286. overlayRadius: 16,
  287. ),
  288. ),
  289. child: Obx(() {
  290. return Slider(
  291. value:
  292. isCustom
  293. ? controller.currentCustomIntimacy.toDouble()
  294. : controller.currentGeneralIntimacy.toDouble(),
  295. divisions: 100,
  296. min: 0,
  297. max: 100,
  298. onChanged: (value) {
  299. controller.updateIntimacy(value.toInt(), isCustom);
  300. },
  301. );
  302. }),
  303. );
  304. },
  305. ),
  306. ],
  307. ),
  308. );
  309. }
  310. // 键盘人设列表
  311. Widget _buildKeyboardCharacter({required bool isCustom}) {
  312. return Column(
  313. mainAxisAlignment: MainAxisAlignment.start,
  314. crossAxisAlignment: CrossAxisAlignment.start,
  315. children: [
  316. Padding(
  317. padding: EdgeInsets.only(left: 16.w, top: 24.h, bottom: 16.h),
  318. child: Text(
  319. StringName.keyboardCharacter,
  320. style: TextStyle(
  321. color: Colors.black.withAlpha(204),
  322. fontSize: 14.sp,
  323. fontWeight: FontWeight.w500,
  324. ),
  325. ),
  326. ),
  327. Obx(() {
  328. return ReorderableWrap(
  329. runSpacing: 16.w,
  330. spacing: 4.w,
  331. runAlignment: WrapAlignment.start,
  332. alignment: WrapAlignment.start,
  333. needsLongPressDraggable: true,
  334. padding: EdgeInsets.symmetric(horizontal: 16.w),
  335. onReorder: (oldIndex, newIndex) {
  336. controller.onReorder(oldIndex, newIndex, isCustom);
  337. },
  338. onNoReorder: (int index) {},
  339. onReorderStarted: (int index) {},
  340. scrollPhysics: const BouncingScrollPhysics(),
  341. footer: [
  342. _buildFooterItem(
  343. image: Assets.images.iconKeyboardManagePlus.image(
  344. width: 18.w,
  345. height: 18.w,
  346. ),
  347. name: StringName.addCharacter,
  348. onTap: () {
  349. controller.clickAddCharacter(isCustom: isCustom);
  350. },
  351. ),
  352. _buildFooterItem(
  353. image: Assets.images.iconKeyboardManageCustom.image(
  354. width: 18.w,
  355. height: 18.w,
  356. ),
  357. name: StringName.customCharacter,
  358. onTap: () {
  359. controller.clickCustomCharacter();
  360. },
  361. ),
  362. ],
  363. children:
  364. isCustom
  365. ? controller.currentCustomKeyboardCharacterList
  366. .map(
  367. (e) => _buildKeyboardCharacterItem(
  368. e,
  369. isCustom: isCustom,
  370. ),
  371. )
  372. .toList()
  373. : controller.currentGeneralKeyboardCharacterList
  374. .map(
  375. (e) => _buildKeyboardCharacterItem(
  376. e,
  377. isCustom: isCustom,
  378. ),
  379. )
  380. .toList(),
  381. );
  382. }),
  383. ],
  384. );
  385. }
  386. Widget _buildKeyboardCharacterItem(
  387. CharacterInfo characterInfo, {
  388. required bool isCustom,
  389. }) {
  390. return SizedBox(
  391. width: (Get.width - 50.w) / 3,
  392. child: Stack(
  393. children: [
  394. Container(
  395. height: 44.h,
  396. margin: EdgeInsets.only(top: 4.h),
  397. decoration: ShapeDecoration(
  398. color: const Color(0xFFF5F4F9),
  399. shape: RoundedRectangleBorder(
  400. borderRadius: BorderRadius.circular(8.r),
  401. ),
  402. ),
  403. alignment: Alignment.center,
  404. child: Row(
  405. mainAxisAlignment: MainAxisAlignment.center,
  406. crossAxisAlignment: CrossAxisAlignment.center,
  407. children: [
  408. Expanded(
  409. child: Text(
  410. '${characterInfo.emoji}${characterInfo.name}',
  411. style: TextStyle(
  412. color: Colors.black.withAlpha(204),
  413. fontSize: 14.sp,
  414. fontWeight: FontWeight.w400,
  415. ),
  416. textAlign: TextAlign.center,
  417. maxLines: 1,
  418. overflow: TextOverflow.ellipsis,
  419. ),
  420. ),
  421. ],
  422. ),
  423. ),
  424. Positioned(
  425. right: 0,
  426. top: 0,
  427. child: GestureDetector(
  428. onTap: () {
  429. controller.clickRemoveCharacter(characterInfo, isCustom);
  430. },
  431. child: Assets.images.iconKeyboardManageX.image(
  432. width: 18.w,
  433. height: 18.w,
  434. ),
  435. ),
  436. ),
  437. ],
  438. ),
  439. );
  440. }
  441. // 添加人设按钮
  442. Widget _buildFooterItem({
  443. required Widget image,
  444. required String name,
  445. void Function()? onTap,
  446. }) {
  447. return GestureDetector(
  448. onTap: onTap,
  449. child: Container(
  450. margin: EdgeInsets.only(top: 4.h),
  451. child: DottedBorder(
  452. color: const Color(0xFFC9C2DB),
  453. // 虚线颜色
  454. strokeWidth: 1.0.w,
  455. dashPattern: [5, 5],
  456. // 线条宽度
  457. borderType: BorderType.RRect,
  458. // 圆角矩形
  459. radius: Radius.circular(8.r),
  460. // 圆角半径
  461. child: Container(
  462. width: 102.w,
  463. height: 38.h,
  464. alignment: Alignment.center,
  465. child: Row(
  466. mainAxisAlignment: MainAxisAlignment.center,
  467. crossAxisAlignment: CrossAxisAlignment.center,
  468. children: [
  469. image,
  470. Text(
  471. name,
  472. style: TextStyle(
  473. color: const Color(0xFFC9C2DB),
  474. fontSize: 14.sp,
  475. fontWeight: FontWeight.w500,
  476. ),
  477. ),
  478. ],
  479. ),
  480. ),
  481. ),
  482. ),
  483. );
  484. }
  485. // 保存按钮
  486. Widget _buildSaveButton({required bool isCustom}) {
  487. return Obx(() {
  488. bool hasChanges =
  489. isCustom
  490. ? (controller.customKeyboardCharacterListChanged ||
  491. controller.customIntimacyChanged)
  492. : (controller.generalKeyboardCharacterListChanged ||
  493. controller.generalIntimacyChanged.value);
  494. return GestureDetector(
  495. onTap: () {
  496. controller.clickSave(isCustom);
  497. },
  498. child: Container(
  499. width: double.infinity,
  500. margin: EdgeInsets.only(left: 16.w, right: 16.w, bottom: 16.h),
  501. height: 48.h,
  502. alignment: Alignment.center,
  503. decoration:
  504. hasChanges
  505. ? ShapeDecoration(
  506. gradient: LinearGradient(
  507. begin: Alignment(0.04, 0.21),
  508. end: Alignment(0.98, 0.76),
  509. colors: [
  510. const Color(0xFF7D46FC),
  511. const Color(0xFFBC87FF),
  512. ],
  513. ),
  514. shape: RoundedRectangleBorder(
  515. borderRadius: BorderRadius.circular(50.r),
  516. ),
  517. shadows: [
  518. BoxShadow(
  519. color: Color(0x66BDA8C9),
  520. blurRadius: 10,
  521. offset: Offset(0, 4),
  522. spreadRadius: 0,
  523. ),
  524. ],
  525. )
  526. : ShapeDecoration(
  527. color: const Color(0x33121212),
  528. shape: RoundedRectangleBorder(
  529. borderRadius: BorderRadius.circular(29.50),
  530. ),
  531. ),
  532. child: Text(
  533. StringName.keyboardSave,
  534. textAlign: TextAlign.center,
  535. style: TextStyle(
  536. color: Colors.white,
  537. fontSize: 16.sp,
  538. fontWeight: FontWeight.w500,
  539. ),
  540. ),
  541. ),
  542. );
  543. });
  544. }
  545. }