421 lines
14 KiB
Dart
421 lines
14 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../../core/storage/local_storage.dart';
|
|
import '../../../models/watermark.dart';
|
|
|
|
/// 编辑这一张照片的水印内容(时间 / 地址 / 天气 / 温度 / 备注)
|
|
///
|
|
/// 返回 null 表示用户取消了。
|
|
Future<WatermarkData?> showWatermarkContentSheet(
|
|
BuildContext context, {
|
|
required WatermarkData initial,
|
|
}) {
|
|
return showModalBottomSheet<WatermarkData>(
|
|
context: context,
|
|
backgroundColor: Colors.white,
|
|
isScrollControlled: true,
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
|
),
|
|
builder: (_) => _WatermarkContentSheet(initial: initial),
|
|
);
|
|
}
|
|
|
|
class _WatermarkContentSheet extends StatefulWidget {
|
|
const _WatermarkContentSheet({required this.initial});
|
|
|
|
final WatermarkData initial;
|
|
|
|
@override
|
|
State<_WatermarkContentSheet> createState() => _WatermarkContentSheetState();
|
|
}
|
|
|
|
class _WatermarkContentSheetState extends State<_WatermarkContentSheet> {
|
|
late final TextEditingController _address;
|
|
late final TextEditingController _weather;
|
|
late final TextEditingController _temperature;
|
|
late final TextEditingController _remark;
|
|
|
|
/// 时间可以改 —— 补加水印、或者当时手机时间不对时要靠它
|
|
late DateTime _time;
|
|
|
|
/// 常用地址,从 LocalStorage 读(同步可读)
|
|
late List<String> _addresses;
|
|
bool _managing = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_time = widget.initial.time;
|
|
_addresses = List<String>.from(LocalStorage.instance.getWatermarkAddresses());
|
|
_address = TextEditingController(text: widget.initial.address);
|
|
_weather = TextEditingController(text: widget.initial.weather);
|
|
_temperature = TextEditingController(text: widget.initial.temperature);
|
|
_remark = TextEditingController(text: widget.initial.remark);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_address.dispose();
|
|
_weather.dispose();
|
|
_temperature.dispose();
|
|
_remark.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _submit() {
|
|
Navigator.of(context).pop(
|
|
widget.initial.copyWith(
|
|
time: _time,
|
|
address: _address.text.trim(),
|
|
weather: _weather.text.trim(),
|
|
temperature: _temperature.text.trim(),
|
|
remark: _remark.text.trim(),
|
|
),
|
|
);
|
|
}
|
|
|
|
// ---------------- 时间 ----------------
|
|
|
|
Future<void> _pickTime() async {
|
|
final date = await showDatePicker(
|
|
context: context,
|
|
initialDate: _time,
|
|
firstDate: DateTime(2000),
|
|
lastDate: DateTime(2100),
|
|
);
|
|
if (date == null || !mounted) return;
|
|
|
|
final time = await showTimePicker(
|
|
context: context,
|
|
initialTime: TimeOfDay.fromDateTime(_time),
|
|
);
|
|
if (time == null || !mounted) return;
|
|
|
|
setState(() {
|
|
_time = DateTime(date.year, date.month, date.day, time.hour, time.minute);
|
|
});
|
|
}
|
|
|
|
// ---------------- 常用地址 ----------------
|
|
|
|
Future<void> _addAddress() async {
|
|
if (_addresses.length >= LocalStorage.watermarkAddressLimit) return;
|
|
// 预填当前地址:常见的用法是「改一改然后存成常用」,省得再敲一遍
|
|
final text = await _promptAddress(initial: _address.text.trim());
|
|
if (text == null || !mounted) return;
|
|
|
|
final next = [..._addresses, text];
|
|
await LocalStorage.instance.saveWatermarkAddresses(next);
|
|
if (!mounted) return;
|
|
setState(() => _addresses = next);
|
|
}
|
|
|
|
Future<void> _removeAddress(int index) async {
|
|
final next = [..._addresses]..removeAt(index);
|
|
await LocalStorage.instance.saveWatermarkAddresses(next);
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_addresses = next;
|
|
if (_addresses.isEmpty) _managing = false;
|
|
});
|
|
}
|
|
|
|
Future<String?> _promptAddress({String initial = ''}) {
|
|
final controller = TextEditingController(text: initial);
|
|
return showDialog<String>(
|
|
context: context,
|
|
builder: (dialogContext) => AlertDialog(
|
|
title: const Text('添加常用地址', style: TextStyle(fontSize: 16)),
|
|
content: TextField(
|
|
controller: controller,
|
|
autofocus: true,
|
|
maxLines: 2,
|
|
minLines: 1,
|
|
decoration: const InputDecoration(
|
|
hintText: '例如:云南省昆明市五华区…',
|
|
hintStyle: TextStyle(fontSize: 13, color: Color(0xFFD7CCC8)),
|
|
),
|
|
style: const TextStyle(fontSize: 14),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(dialogContext).pop(),
|
|
child: const Text('取消', style: TextStyle(color: Color(0xFF8D6E63))),
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
final text = controller.text.trim();
|
|
Navigator.of(dialogContext).pop(text.isEmpty ? null : text);
|
|
},
|
|
child: const Text('添加', style: TextStyle(color: Color(0xFFC8956E))),
|
|
),
|
|
],
|
|
),
|
|
).whenComplete(controller.dispose);
|
|
}
|
|
|
|
// ---------------- 构建 ----------------
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Padding(
|
|
// 键盘弹出时把内容顶上去,否则输入框会被挡住
|
|
padding: EdgeInsets.only(bottom: MediaQuery.viewInsetsOf(context).bottom),
|
|
child: SafeArea(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.fromLTRB(20, 16, 20, 20),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Center(
|
|
child: Text(
|
|
'编辑水印内容',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF5D4037),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 18),
|
|
|
|
_timeRow(),
|
|
const SizedBox(height: 16),
|
|
_quickAddressSection(),
|
|
const SizedBox(height: 16),
|
|
|
|
_field(label: '地址', controller: _address, hint: '自动定位,也可手改'),
|
|
_field(label: '天气', controller: _weather, hint: '如:晴'),
|
|
_field(label: '温度', controller: _temperature, hint: '如:25℃'),
|
|
_field(label: '备注', controller: _remark, hint: '想写点什么'),
|
|
|
|
const SizedBox(height: 12),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: SizedBox(
|
|
height: 46,
|
|
child: OutlinedButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: const Color(0xFFC8956E),
|
|
side: const BorderSide(color: Color(0xFFC8956E)),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(23),
|
|
),
|
|
),
|
|
child: const Text('取消'),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: SizedBox(
|
|
height: 46,
|
|
child: FilledButton(
|
|
onPressed: _submit,
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: const Color(0xFFC8956E),
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(23),
|
|
),
|
|
),
|
|
child: const Text('确定'),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 时间行:点一下弹日期 + 时间选择器
|
|
Widget _timeRow() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text('时间', style: TextStyle(fontSize: 13, color: Color(0xFF8D6E63))),
|
|
const SizedBox(height: 6),
|
|
InkWell(
|
|
onTap: _pickTime,
|
|
borderRadius: BorderRadius.circular(10),
|
|
child: Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 13),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF7F4F1),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
formatWatermarkTime(_time),
|
|
style: const TextStyle(fontSize: 14, color: Color(0xFF5D4037)),
|
|
),
|
|
),
|
|
const Icon(Icons.event, size: 17, color: Color(0xFFBCAAA4)),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _quickAddressSection() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
const Text(
|
|
'常用地址',
|
|
style: TextStyle(fontSize: 13, color: Color(0xFF8D6E63)),
|
|
),
|
|
const Spacer(),
|
|
if (_addresses.isNotEmpty)
|
|
GestureDetector(
|
|
onTap: () => setState(() => _managing = !_managing),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
|
child: Text(
|
|
_managing ? '完成' : '管理',
|
|
style: const TextStyle(fontSize: 12.5, color: Color(0xFFC8956E)),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Wrap(
|
|
spacing: 8,
|
|
runSpacing: 8,
|
|
children: [
|
|
for (var i = 0; i < _addresses.length; i++)
|
|
_addressChip(i, _addresses[i]),
|
|
if (!_managing && _addresses.length < LocalStorage.watermarkAddressLimit)
|
|
_addChip(),
|
|
],
|
|
),
|
|
if (_addresses.isEmpty && !_managing)
|
|
const Padding(
|
|
padding: EdgeInsets.only(top: 2),
|
|
child: Text(
|
|
'把常去的地点存下来,以后一键替换',
|
|
style: TextStyle(fontSize: 11.5, color: Color(0xFFBCAAA4)),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _addressChip(int index, String address) {
|
|
return GestureDetector(
|
|
// 管理模式下点整体是删除,正常模式点一下填进地址框
|
|
onTap: _managing
|
|
? () => _removeAddress(index)
|
|
: () => setState(() => _address.text = address),
|
|
child: Container(
|
|
constraints: const BoxConstraints(maxWidth: 210),
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
|
|
decoration: BoxDecoration(
|
|
color: _managing ? const Color(0xFFFDECEA) : const Color(0xFFFFF3E6),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Flexible(
|
|
child: Text(
|
|
address,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: TextStyle(
|
|
fontSize: 12.5,
|
|
color: _managing
|
|
? const Color(0xFFE53935)
|
|
: const Color(0xFF8D6E63),
|
|
),
|
|
),
|
|
),
|
|
if (_managing) ...[
|
|
const SizedBox(width: 6),
|
|
const Icon(Icons.close, size: 14, color: Color(0xFFE53935)),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _addChip() {
|
|
return GestureDetector(
|
|
onTap: _addAddress,
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: const Color(0xFFE0C9A6)),
|
|
),
|
|
child: const Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.add, size: 14, color: Color(0xFFC8956E)),
|
|
SizedBox(width: 4),
|
|
Text(
|
|
'添加',
|
|
style: TextStyle(fontSize: 12.5, color: Color(0xFFC8956E)),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _field({
|
|
required String label,
|
|
required TextEditingController controller,
|
|
required String hint,
|
|
}) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 14),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
label,
|
|
style: const TextStyle(fontSize: 13, color: Color(0xFF8D6E63)),
|
|
),
|
|
const SizedBox(height: 6),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 14),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF7F4F1),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: TextField(
|
|
controller: controller,
|
|
style: const TextStyle(fontSize: 14, color: Color(0xFF5D4037)),
|
|
decoration: InputDecoration(
|
|
border: InputBorder.none,
|
|
isDense: true,
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 13),
|
|
hintText: hint,
|
|
hintStyle: const TextStyle(fontSize: 14, color: Color(0xFFD7CCC8)),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|