import 'dart:convert'; import 'dart:html'; import 'dart:typed_data'; import 'package:dio/dio.dart' as adio; import 'package:dio/dio.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; import 'package:get/get.dart'; import 'package:link/view/preview.dart'; import 'package:web_smooth_scroll/web_smooth_scroll.dart'; import '../constants.dart'; import '../utils/api.dart'; import '../utils/http_utils.dart'; import '../utils/size_fit.dart'; import '../utils/storage.dart'; import 'component/link_btn.dart'; import 'component/link_step.dart'; import 'component/toast.dart'; import 'component/top_container.dart'; import 'component/upload_file.dart'; import 'component/web_image.dart'; class EditLink extends StatefulWidget { const EditLink({Key? key}) : super(key: key); @override State createState() => _EditLinkState(); } class _EditLinkState extends State { TextEditingController controller = TextEditingController(); TextEditingController titleController = TextEditingController(); TextEditingController descController = TextEditingController(); FileUploadInputElement uploadInput = FileUploadInputElement(); bool isLink = true; bool showError = false; Uint8List? chooseImage; String strLink = ''; String strTitle = ''; String strDesc = ''; String imgUrl = ''; bool isFirstEnter = true; var social; late List types; late ScrollController scrollController; @override void initState() { // TODO: implement initState scrollController = ScrollController(); Map data = Get.parameters; social = jsonDecode(data['social']); types = social['types']; if (types[0] == 'LINK') { isLink = true; } else { isLink = false; } getDetail(); super.initState(); } @override dispose(){ controller.dispose(); titleController.dispose(); descController.dispose(); scrollController.dispose(); super.dispose(); } chooseFile() async { uploadInput.accept = '.png,.jpg,.jpeg'; uploadInput.multiple = false; uploadInput.click(); uploadInput.onChange.listen((e) { final files = uploadInput.files; if (files?.length == 1) { FileReader reader = FileReader(); reader.onLoadEnd.listen((event) { setState(() { chooseImage = reader.result as Uint8List; }); }); reader.readAsArrayBuffer(files![0]); } }); } Future getDetail() async { String id = Get.parameters['id']!; var data = await HttpUtils.get(Api.userSocials + '/$id'); setState(() { if (data['type'] == 'LINK') { isLink = true; controller.text = data['user_url']; strLink = data['user_url']; } else { isLink = false; } strTitle = data['user_nick']; titleController.text = data['user_nick']; strDesc = data['description']; descController.text = data['description']; }); } intro() { Color tapColor = kBtnColor; if (isLink) { if (strLink.isNotEmpty) { tapColor = const Color(0xFFA5A5AD); } } else { if (chooseImage != null) { tapColor = const Color(0xFFA5A5AD); } } return Container( color: showError ? const Color(0xFF002FFF) : Colors.transparent, padding: EdgeInsets.only( left: 20.px, right: 20.px, top: showError ? 12.px : 0, bottom: showError ? 12.px : 0), child: Column( children: [ Row( children: [ Container( width: 12.px, height: 12.px, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6.px), border: Border.all(color: kThemeColor, width: 1.px)), child: Text( '1', style: TextStyle( color: kThemeColor, fontFamily: 'Link1', fontWeight: FontWeight.w800, fontSize: 8.px), ), ), SizedBox( width: 3.px, ), Text( '去『已选平台-我』找到「', style: TextStyle(color: const Color(0xFFA5A5AD), fontSize: 12.px), ), Text( isLink ? '分享-复制链接' : '我的二维码', style: TextStyle( color: const Color(0xFFA5A5AD), fontSize: 12.px, fontWeight: FontWeight.bold), ), Text( isLink ? '」' : '」保存', style: TextStyle(color: const Color(0xFFA5A5AD), fontSize: 12.px), ), ], ), SizedBox( height: 5.px, ), Row( children: [ Container( width: 12.px, height: 12.px, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6.px), border: Border.all(color: kThemeColor, width: 1.px)), child: Text( '2', style: TextStyle( color: kThemeColor, fontFamily: 'Link1', fontWeight: FontWeight.w800, fontSize: 8.px), ), ), SizedBox( width: 3.px, ), Text( '回到此处', style: TextStyle(color: const Color(0xFFA5A5AD), fontSize: 12.px), ), SizedBox( width: 3.px, ), if (!isLink && chooseImage == null) UploadFile( child: Text( '上传二维码', style: TextStyle( color: tapColor, fontSize: 12.px, fontWeight: FontWeight.bold), ), callback: (Uint8List file) { setState(() { chooseImage = file; showError = false; }); }, ), if (isLink || (chooseImage != null)) GestureDetector( onTap: () { if (isLink) { if (strLink.isEmpty) { getClipData(); } } else { if (chooseImage == null) { chooseFile(); } } }, child: Text( isLink ? '粘贴链接' : '上传二维码', style: TextStyle( color: tapColor, fontSize: 12.px, fontWeight: FontWeight.bold), ), ) ], ), ], ), ); } switchWarn() { return Container( alignment: Alignment.center, padding: EdgeInsets.only(top: 32.px, bottom: 32.px), child: Column( children: [ SvgPicture.asset( 'assets/icons/question.svg', width: 48.px, height: 48.px, ), SizedBox( height: 12.px, ), Text( isLink ? '切换选择「二维码」\n将使现有「链接」不被展示\n确定要切换吗?' : '切换选择「链接」\n将使现有「二维码」不被展示\n确定要切换吗?', textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 16.px, fontWeight: FontWeight.bold), ), SizedBox( height: 16.px, ), AlertButton( title: '确定', isCancel: false, width: 220.px, height: 40.px, callback: () { Toast().hideHud(); setState(() { showError = false; if (isLink) { strLink = ''; isLink = false; } else { chooseImage = null; isLink = true; } }); }), SizedBox( height: 24.px, ), AlertButton( title: '再想想', isCancel: true, width: 220.px, height: 40.px, callback: () { Toast().hideHud(); }), ], ), ); } switchType() { if (isLink && strLink.isNotEmpty) { Toast().showCustomHud(switchWarn(), context: context); return; } if (!isLink && chooseImage != null) { Toast().showCustomHud(switchWarn(), context: context); return; } setState(() { showError = false; isLink = !isLink; }); } preview() { Toast().showText('长按图片识别或保存', context: context); showDialog( context: context, barrierDismissible: false, barrierColor: Colors.transparent, useSafeArea: false, builder: (BuildContext context) { return Preview( memory: chooseImage, ); }); } Future getClipData() async { await Clipboard.getData(Clipboard.kTextPlain).then((value) { if (value != null && value.text != null) { setState(() { strLink = value.text!; controller.text = strLink; }); } }); } content() { return Column( children: [ SizedBox( height: 32.px, ), Container( width: 343.px, padding: EdgeInsets.all(20.px), margin: EdgeInsets.only(bottom: 20.px), decoration: BoxDecoration( color: const Color(0xFF2C2C2E), borderRadius: BorderRadius.circular(24.px)), child: GestureDetector( onTap: () { Toast().showInfoText('已添加的链接不能更改平台\n建议重新添加新链接\n并删除原有错误的链接', context: context); }, child: Row( children: [ Expanded( child: Text( '已选平台', style: TextStyle( color: kThemeColor, fontSize: 16.px, fontWeight: FontWeight.bold), )), // ClipRRect( // borderRadius: BorderRadius.circular(6.px), // child: social['logo'].length == 0 // ? SvgPicture.asset( // 'assets/icons/other_link.svg', // width: 24.px, // height: 24.px, // ) // : WebImage( // url: social['logo'], width: 24.px, height: 24.px), // ), social['logo'].length == 0 ? ClipRRect( borderRadius: BorderRadius.circular(6.px), child: SvgPicture.asset( 'assets/icons/other_link.svg', width: 24.px, height: 24.px, )) : WebImage( url: social['logo'], width: 24.px, height: 24.px, borderRadius: 6.px, ), SizedBox( width: 8.px, ), Text( social['name'], style: TextStyle( color: const Color(0xFFA5A5AD), fontSize: 14.px), ), Image.asset( 'assets/images/arrow_right.png', width: 20.px, height: 20.px, ) // SvgPicture.asset( // 'assets/icons/arrow_right.svg', // width: 20.px, // height: 20.px, // color: const Color(0xFFA5A5AD), // ) ], )), ), Container( width: 343.px, padding: EdgeInsets.only(top: 20.px, bottom: 20.px), margin: EdgeInsets.only(bottom: 20.px), decoration: BoxDecoration( color: const Color(0xFF2C2C2E), borderRadius: BorderRadius.circular(24.px)), child: Column( children: [ Row( children: [ SizedBox( width: 20.px, ), Expanded( child: Text( '链接方式', style: TextStyle( color: kThemeColor, fontSize: 16.px, fontWeight: FontWeight.bold), )), Container( height: 28.px, // width: 98.px, decoration: BoxDecoration( color: const Color(0xFF131314), borderRadius: BorderRadius.circular(14.px)), child: Row( children: [ Container( padding: EdgeInsets.only(left: 10.px, right: 10.px), height: 28.px, decoration: BoxDecoration( color: kThemeColor, borderRadius: BorderRadius.circular(14.px)), child: Row( children: [ SvgPicture.asset( isLink ? 'assets/icons/link.svg' : 'assets/icons/qrcode.svg', width: 16.px, height: 16.px, color: const Color(0xFF131314), ), SizedBox( width: 2.px, ), Text( isLink ? '链接' : '二维码', style: TextStyle( color: const Color(0xFF131314), fontSize: 12.px), ) ], ), ), if (types.length > 1) GestureDetector( onTap: () { switchType(); }, child: Container( alignment: Alignment.center, width: 36.px, height: 28.px, child: SvgPicture.asset( isLink ? 'assets/icons/qrcode.svg' : 'assets/icons/link.svg', width: 16.px, height: 16.px, color: const Color(0xFF74747A), ), ), ) ], ), ), SizedBox( width: 20.px, ) ], ), SizedBox( height: 8.px, ), intro(), // SizedBox( // height: 28.px, // ), SizedBox( height: 16.px, ), if (isLink) Row( children: [ SizedBox( width: 20.px, ), Expanded( child: TextField( controller: controller, cursorColor: kBtnColor, onChanged: (value) { setState(() { strLink = value; showError = false; // keyword = value; }); }, style: TextStyle( color: Colors.white, fontSize: 14.px, fontFamily: 'Link1'), decoration: InputDecoration( border: InputBorder.none, hintText: '请输入链接', hintStyle: TextStyle( fontSize: 14.px, color: const Color(0xFF444447)), counterText: "", ), ), ), if (strLink.isEmpty) GestureDetector( onTap: () { getClipData(); }, child: SvgPicture.asset( 'assets/icons/copy_link.svg', width: 24.px, height: 24.px, color: kBtnColor, ), ), SizedBox( width: 20.px, ), ], ), if (!isLink) Stack( children: [ DottedBorder( color: const Color(0xFF444447), borderType: BorderType.RRect, radius: Radius.circular(16.px), child: ClipRRect( borderRadius: BorderRadius.circular(16.px), child: Container( width: 303.px, alignment: Alignment.center, padding: EdgeInsets.only(top: 12.px, bottom: 12.px), child: chooseImage != null ? GestureDetector( onTap: () { preview(); }, child: Image.memory( chooseImage!, height: 120.px, ), ) : UploadFile( // width: 120.px, // height: 120.px, child: SvgPicture.asset( 'assets/icons/upload_qrcode.svg', width: 120.px, height: 120.px, ), callback: (Uint8List file) { setState(() { chooseImage = file; showError = false; }); }, ), ), )), if (chooseImage != null) Positioned( right: 12.px, top: 12.px, child: UploadFile( // width: 68.px, // height: 28.px, child: Container( width: 68.px, height: 28.px, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(14.px), color: const Color(0xBF131314)), child: Text( '重新上传', style: TextStyle( color: kBtnColor, fontSize: 12.px), )), callback: (Uint8List file) { setState(() { chooseImage = file; showError = false; }); }, )) // GestureDetector( // onTap: () { // chooseFile(); // }, // child: Container( // width: 68.px, // height: 28.px, // alignment: Alignment.center, // decoration: BoxDecoration( // borderRadius: BorderRadius.circular(14.px), // color: const Color(0xBF131314)), // child: Text( // '重新上传', // style: TextStyle( // color: kBtnColor, fontSize: 12.px), // )), // )) ], ), ], ), ), if ((isLink && strLink.isNotEmpty) || (!isLink && chooseImage != null)) Container( width: 343.px, padding: EdgeInsets.all(20.px), margin: EdgeInsets.only(bottom: 20.px), decoration: BoxDecoration( color: const Color(0xFF2C2C2E), borderRadius: BorderRadius.circular(24.px)), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '标题', style: TextStyle( color: kThemeColor, fontWeight: FontWeight.bold, fontSize: 16.px, height: 1.75), ), SizedBox( height: 8.px, ), SelectableText( '填写在我该平台上的用户名,不填将展示平台名称,如:', style: TextStyle( color: const Color(0xFFA5A5AD), fontSize: 12.px, height: 1.5), ), SizedBox( height: 16.px, ), TextField( cursorColor: kBtnColor, controller: titleController, onChanged: (value) { setState(() { // keyword = value; strTitle = value; }); }, style: TextStyle(color: Colors.white, fontSize: 14.px), decoration: InputDecoration( border: InputBorder.none, hintText: social['url_example'], hintStyle: TextStyle( fontSize: 14.px, color: const Color(0xFF444447)), counterText: "", ), ), Container( height: 1.px, color: const Color(0xFF444447), margin: EdgeInsets.only(top: 18.px, bottom: 18.px), ), Text( '描述', style: TextStyle( color: kThemeColor, fontWeight: FontWeight.bold, fontSize: 16.px, height: 1.75), ), SizedBox( height: 8.px, ), Text( '对以上内容的补充描述,如:', style: TextStyle( color: const Color(0xFFA5A5AD), fontSize: 12.px, height: 1.5), ), SizedBox( height: 16.px, ), TextField( cursorColor: kBtnColor, controller: descController, onChanged: (value) { setState(() { strDesc = value; // keyword = value; }); }, style: TextStyle(color: Colors.white, fontSize: 14.px), decoration: InputDecoration( border: InputBorder.none, hintText: social['description_example'], hintStyle: TextStyle( fontSize: 14.px, color: const Color(0xFF444447)), counterText: "", ), ), ], )), SizedBox( height: 162.px, ) ], ); } Future del() async { String id = Get.parameters['id']!; await HttpUtils.delete(Api.userSocials + '/$id'); Get.back(); } Future commit() async { String id = Get.parameters['id']!; if (isLink && strLink.isEmpty) { Toast().showInfoText('请粘贴链接地址后再提交\n或选择“稍后添加”', context: context); setState(() { showError = true; }); return; } if (!isLink && chooseImage == null && imgUrl.isEmpty) { Toast().showInfoText('请上传二维码后再提交\n或选择“稍后添加”', context: context); setState(() { showError = true; }); return; } Toast().showHud(context: context); if (isLink) { var data2 = await HttpUtils.put(Api.userSocials + '/$id', data: { 'user_url': strLink, 'description': strDesc, 'social_id': social['id'], 'social_name': social['id'].length == 0 ? social['name'] : '', 'type': 'LINK', 'user_nick': strTitle }); } else { Map data = await HttpUtils.get(Api.ossFormUpload, params: {'type': 'AVATAR', 'file_ext': 'png'}); Dio dio = Dio(); Map map = data['fields']; map['file'] = await adio.MultipartFile.fromBytes(chooseImage!, filename: 'avatar.png'); adio.FormData formData = adio.FormData.fromMap(map); adio.Response response = await dio.post(data['upload_url'], data: formData); var data2 = await HttpUtils.put(Api.userSocials + '/$id', data: { 'user_url': data['view_url'], 'description': strDesc, 'social_id': social['id'], 'social_name': social['id'].length == 0 ? social['name'] : '', 'type': 'QR', 'user_nick': strTitle }); } Toast().hideHud(); Get.back(); } tapDelete() { Toast().showCustomHud( Container( alignment: Alignment.center, padding: EdgeInsets.only(top: 32.px, bottom: 32.px), child: Column( children: [ Text( '删除此链接后,将无法恢复,\n确定要删除吗?', style: TextStyle( color: Colors.white, fontSize: 16.px, fontWeight: FontWeight.bold), ), SizedBox( height: 16.px, ), AlertButton( title: '确定,立即删除', isCancel: false, width: 220.px, height: 40.px, callback: () { Toast().hideHud(); del(); }), SizedBox( height: 24.px, ), AlertButton( title: '取消,再想想', isCancel: true, width: 220.px, height: 40.px, callback: () { Toast().hideHud(); }), ], ), ), context: context); } @override Widget build(BuildContext context) { SizeFit.initialize(context); return Material( color: kBgColor, child: TopContainer( child: Stack(children: [ Positioned( left: 0, top: 0, right: 0, height: MediaQuery.of(context).size.height, // bottom: 0, child: WebSmoothScroll( controller: scrollController, child: SingleChildScrollView( // physics: const NeverScrollableScrollPhysics(), controller: scrollController, child: content(), ))), Positioned( left: 0, right: 0, bottom: 0, child: Container( height: isFirstEnter ? 162.px : 140.px, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0x00131314), Color(0xFF131314), Color(0xFF131314), ])), child: Column( children: [ SizedBox( height: 36.px, ), LinkButton( title: '保存修改', disable: false, isBlack: false, callback: () { commit(); }), SizedBox( height: 30.px, ), GestureDetector( onTap: () { tapDelete(); }, child: Text( '删除已选账号', style: TextStyle( color: const Color(0xFF74747A), fontSize: 14.px), ), ) ], ), )) ]))); } }