import 'package:flutter/material.dart'; import 'dart:io'; import 'package:image_picker/image_picker.dart'; class EscortPublish extends StatefulWidget { @override State createState() => _EscortPublishState(); } class _EscortPublishState extends State { final _nameController = TextEditingController(); final _phoneController = TextEditingController(); final _addressController = TextEditingController(); final List levels = ['平价', '中端', '高端', '顶级']; final List services = ['工作室', '上门', '空降', '伴游']; final List taboos = ['DU品', '吃YAO', 'WU套', '醉9']; final List tags = [ '学生', '良家', 'OL', '网红', '模特', '明星', '新人', '兼职', '短期', '原装', 'S身材', '高颜值', '三点粉', '微胖', '水嫩', '紧致', '清纯', '轻熟', '气质', '纯欲' ]; final List packages = [ '双飞', '3P', '口爆', '胸推', '足交', '毒龙', '做爱', '69', '无套内射', '舌吻', '水床', '制服', '冰火', '环游', '调教', '喷水', '洗澡', '剧情', 'SM', '三通' ]; String? selectedLevel; String? selectedCity; String? selectedYear; String? selectedWeight; String? selectedHeight; String? selectedCup; List selectedServices = []; List selectedTaboos = []; List selectedTags = []; List selectedPackages = []; List imageFiles = []; XFile? videoFile; bool hideFromPublic = false; final TextEditingController _descController = TextEditingController(); final ImagePicker _picker = ImagePicker(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text.rich( TextSpan( text: '发布外围', children: [ TextSpan( text: '(请保证妹子信息准确)', style: TextStyle(fontSize: 14, color: Colors.grey), ) ], ), ), leading: const BackButton(), ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: [ buildTextField("外围花名", "(必填)", "请输入外围花名,不超过10个字", _nameController), buildDropdownField("所在城市", "(必选)", selectedCity, ['北京', '上海', '广州'], (val) { setState(() => selectedCity = val); }), buildTextField("手机号码", "(必填,不会自动发送给用户)", "请输入妹子联系方式,不超过50个字", _phoneController), buildTextField("详细地址", "(必填,不会自动发送给用户)", "请输入妹子工作室地址,不超过50个字", _addressController), buildDropdownField("出生年份", "(必选)", selectedYear, List.generate(25, (i) => '${2000 - i}'), (val) { setState(() => selectedYear = val); }), buildDropdownField( "体重", "(必选)", selectedWeight, ['40kg', '45kg', '50kg', '55kg'], (val) { setState(() => selectedWeight = val); }), buildDropdownField( "身高", "(必选)", selectedHeight, ['150cm', '160cm', '170cm'], (val) { setState(() => selectedHeight = val); }), buildDropdownField( "罩杯", "(必选)", selectedCup, ['A', 'B', 'C', 'D', 'E'], (val) { setState(() => selectedCup = val); }), const SizedBox(height: 20), Align( alignment: Alignment.centerLeft, child: RichText( text: const TextSpan( style: TextStyle(fontSize: 16, color: Colors.black), children: [ TextSpan(text: '请选择妹子档次'), TextSpan(text: '(单选)', style: TextStyle(color: Colors.red)), ], ), ), ), const SizedBox(height: 10), Wrap( spacing: 12, children: levels.map((level) { final selected = level == selectedLevel; return ChoiceChip( label: Text(level), selected: selected, onSelected: (_) { setState(() => selectedLevel = level); }, selectedColor: Colors.blue, labelStyle: TextStyle(color: selected ? Colors.white : Colors.black), ); }).toList(), ), const SizedBox(height: 20), Align( alignment: Alignment.centerLeft, child: RichText( text: const TextSpan( style: TextStyle(fontSize: 16, color: Colors.black), children: [ TextSpan(text: '服务套餐'), TextSpan( text: '(最少选填1项)', style: TextStyle(color: Colors.red)), ], ), ), ), const SizedBox(height: 10), buildSelectableChips( options: services, selectedList: selectedServices, maxSelect: services.length, isSingle: false, onTap: (_) {}, ), const SizedBox(height: 12), buildPackageInputFields(), const SizedBox(height: 20), Align( alignment: Alignment.centerLeft, child: RichText( text: const TextSpan( style: TextStyle(fontSize: 16, color: Colors.black), children: [ TextSpan(text: '禁忌'), TextSpan(text: '(多选)', style: TextStyle(color: Colors.red)), ], ), ), ), const SizedBox(height: 10), buildSelectableChips( options: taboos, selectedList: selectedTaboos, maxSelect: taboos.length, isSingle: false, onTap: (_) {}, ), const SizedBox(height: 20), Align( alignment: Alignment.centerLeft, child: RichText( text: const TextSpan( style: TextStyle(fontSize: 16, color: Colors.black), children: [ TextSpan(text: '外圈标签'), TextSpan( text: '(必选,最多3个)', style: TextStyle(color: Colors.red)), ], ), ), ), const SizedBox(height: 10), buildSelectableChips( options: tags, selectedList: selectedTags, maxSelect: 3, isSingle: false, onTap: (_) {}, ), Align( alignment: Alignment.centerLeft, child: RichText( text: const TextSpan( style: TextStyle(fontSize: 16, color: Colors.black), children: [ TextSpan(text: '服务项目'), TextSpan( text: '(必选,最多20个)', style: TextStyle(color: Colors.red)), ], ), ), ), const SizedBox(height: 10), buildSelectableChips( options: packages, selectedList: selectedPackages, maxSelect: 20, isSingle: false, onTap: (_) {}, ), const SizedBox(height: 20), Align( alignment: Alignment.centerLeft, child: RichText( text: const TextSpan( style: TextStyle(fontSize: 16, color: Colors.black), children: [ TextSpan(text: '外圈介绍'), TextSpan(text: '(选填)', style: TextStyle(color: Colors.red)), ], ), ), ), const SizedBox(height: 10), TextField( controller: _descController, maxLength: 200, maxLines: 4, decoration: const InputDecoration( hintText: '请输入外圈介绍,不超过200字', border: OutlineInputBorder(), ), ), const SizedBox(height: 20), Align( alignment: Alignment.centerLeft, child: RichText( text: const TextSpan( style: TextStyle(fontSize: 16, color: Colors.black), children: [ TextSpan(text: '上传图片'), TextSpan( text: '(最少1张,最多9张)', style: TextStyle(color: Colors.red)), ], ), ), ), const SizedBox(height: 10), buildImageUploader(), const SizedBox(height: 20), Row( children: [ Align( alignment: Alignment.centerLeft, child: RichText( text: const TextSpan( style: TextStyle(fontSize: 16, color: Colors.black), children: [ TextSpan(text: '官方认证视频'), TextSpan( text: '(选填,不超过100M)', style: TextStyle(color: Colors.red)), ], ), ), ), Spacer(), Row( children: [ Radio( value: true, groupValue: hideFromPublic, onChanged: (_) => setState(() => hideFromPublic = true), ), const Text("不对外展示", style: TextStyle(fontSize: 12)), ], ), ], ), const SizedBox(height: 8), GestureDetector( onTap: pickVideo, child: Container( height: 160, color: Colors.grey[100], child: Center( child: videoFile == null ? const Icon(Icons.videocam, size: 40, color: Colors.grey) : const Icon(Icons.check_circle, color: Colors.green, size: 40), ), ), ), const SizedBox(height: 8), const Text("① 视频需包含本人露脸、当前日期、所在城市/名媛会", style: TextStyle(color: Colors.grey)), const Text("例如:3月3日,名媛会露脸在苏州等你", style: TextStyle(color: Colors.grey)), const Text("② 审核通过率高,通过后可获得官方认证标识", style: TextStyle(color: Colors.grey)), const Text("③ 选择对外展示可额外获得官方流量扶持+佣金优惠", style: TextStyle(color: Colors.grey)), const SizedBox(height: 30), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () {}, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6)), backgroundColor: Colors.blue, ), child: const Text("提交", style: TextStyle(fontSize: 16)), ), ), ], ), ), ); } Widget buildTextField(String label, String hint, String placeholder, TextEditingController controller) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildLabel(label, hint), const SizedBox(height: 6), TextField( controller: controller, decoration: InputDecoration( hintText: placeholder, border: OutlineInputBorder(), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), ), ), ], ), ); } Widget buildDropdownField(String label, String hint, String? value, List options, ValueChanged onChanged) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildLabel(label, hint), const SizedBox(height: 6), DropdownButtonFormField( value: value, items: options .map((e) => DropdownMenuItem(value: e, child: Text(e))) .toList(), onChanged: onChanged, decoration: InputDecoration( border: OutlineInputBorder(), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), ), ), ], ), ); } Widget buildLabel(String title, String hint) { return RichText( text: TextSpan( text: title, style: const TextStyle(fontSize: 16, color: Colors.black), children: [ TextSpan(text: ' $hint', style: const TextStyle(color: Colors.red)), ], ), ); } Widget buildSelectableChips({ required List options, required List selectedList, required int maxSelect, required bool isSingle, required Function(String) onTap, }) { return Wrap( spacing: 8, runSpacing: 8, children: options.map((option) { final bool selected = selectedList.contains(option); return ChoiceChip( label: Text(option), selected: selected, onSelected: (_) { setState(() { if (isSingle) { selectedList ..clear() ..add(option); } else { if (selected) { selectedList.remove(option); } else if (selectedList.length < maxSelect) { selectedList.add(option); } } onTap(option); }); }, ); }).toList(), ); } Widget buildPackageInputFields() { return Column( children: List.generate(4, (i) { return Row( children: [ Expanded( child: TextField( decoration: InputDecoration( hintText: '请输入服务套餐${i + 1}${i == 0 ? ',必填' : ''}', border: OutlineInputBorder(), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), ), ), ), const SizedBox(width: 8), Expanded( child: TextField( decoration: InputDecoration( hintText: '请输入套餐${i + 1}价格${i == 0 ? ',必填' : ''}', border: OutlineInputBorder(), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), ), keyboardType: TextInputType.number, ), ), ], ); }), ); } Widget buildImageUploader() { return Wrap( spacing: 8, runSpacing: 8, children: List.generate(9, (index) { if (index < imageFiles.length) { return Stack( children: [ Image.file( File(imageFiles[index].path), width: 100, height: 100, fit: BoxFit.cover, ), Positioned( top: 0, right: 0, child: GestureDetector( onTap: () { setState(() { imageFiles.removeAt(index); }); }, child: const Icon(Icons.cancel, color: Colors.red, size: 20), ), ), ], ); } else if (index == imageFiles.length) { return GestureDetector( onTap: pickImages, child: Container( width: 100, height: 100, color: Colors.grey[200], child: const Icon(Icons.add), ), ); } else { return Container( width: 100, height: 100, color: Colors.grey[100], ); } }), ); } Future pickVideo() async { final XFile? picked = await _picker.pickVideo( source: ImageSource.gallery, maxDuration: const Duration(minutes: 5), ); if (picked != null) { setState(() { videoFile = picked; }); } } Future pickImages() async { final List? images = await _picker.pickMultiImage(); if (images != null && images.isNotEmpty) { setState(() { final remainingSlots = 9 - imageFiles.length; imageFiles.addAll(images.take(remainingSlots)); }); } } }