diff --git a/lib/main.dart b/lib/main.dart index d041f39..a2f75a0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,7 +34,7 @@ void main() async{ // todosBox.clear(); final flispBox = await Hive.openBox('flisps'); - flispBox.clear(); + // flispBox.clear(); // notifyService.cancelAllNotifications(); diff --git a/lib/widgets/common.dart b/lib/widgets/common.dart index d8e2969..d696bb9 100644 --- a/lib/widgets/common.dart +++ b/lib/widgets/common.dart @@ -20,7 +20,7 @@ Container buildBody({Widget? child}) { Container buildCard({Widget? child}) { return Container( decoration: buildBoxDecoration(), - padding: EdgeInsets.all(10), + padding: EdgeInsets.all(16), child: child, ); } diff --git a/lib/widgets/flisp_form.dart b/lib/widgets/flisp_form.dart index 355e9a5..04393fc 100644 --- a/lib/widgets/flisp_form.dart +++ b/lib/widgets/flisp_form.dart @@ -1,5 +1,6 @@ import 'package:flisp_app/models/flisp.dart'; import 'package:flisp_app/provider/flisp_provider.dart'; +import 'package:flisp_app/widgets/flisp_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:provider/provider.dart'; @@ -23,17 +24,33 @@ class FlispForm extends StatefulWidget { } class _FlispFormState extends State { + bool _showTagOptions = false; + @override void initState() { super.initState(); } + void _toggleTagOptions() { + setState(() { + _showTagOptions = !_showTagOptions; + }); + } + + void _selectTag(FlispTag tag) { + final provider = Provider.of(context, listen: false); + setState(() { + provider.formItem.tag = tag; + _showTagOptions = false; + }); + } + @override Widget build(BuildContext context) { final int maxContentCount = 100; final provider = Provider.of(context); - FormBuilderTextField buildContentField() { + Widget buildContentField() { return FormBuilderTextField( name: 'content', initialValue: provider.formItem.content, @@ -78,23 +95,129 @@ class _FlispFormState extends State { ); } - // 图标按钮组件 - Widget buildIconButton({ - required IconData icon, - required Color color, - required VoidCallback onTap, - }) { - return GestureDetector( - onTap: onTap, - child: Container( - width: 32, - height: 32, - decoration: BoxDecoration( - color: color.withAlpha(50), - borderRadius: BorderRadius.circular(8), - border: Border.all(color: color.withAlpha(80), width: 1), + Widget buildFormBody() { + return Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildContentField(), + SizedBox(height: 8), + buildFlispTag(provider.formItem), + SizedBox(height: 12), + ], + ), + ), + ); + } + + Widget buildSelectImageButton() { + return GestureDetector( + onTap: () {}, + child: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: Colors.blue.withAlpha(50), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.blue.withAlpha(80), width: 1), + ), + child: Icon( + Icons.photo_library_rounded, + size: 20, + color: Colors.blue, + ), + ), + ); + } + + Widget buildSelectTagButton() { + return GestureDetector( + onTap: _toggleTagOptions, + child: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: Colors.orange.withAlpha(50), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.orange.withAlpha(80), width: 1), + ), + child: Icon( + Icons.local_offer_rounded, + size: 20, + color: Colors.orange, + ), + ), + ); + } + + Widget buildConfirmButton() { + return Container( + width: 48, + height: 48, + decoration: BoxDecoration( + color: Theme.of(context).primaryColor, + borderRadius: BorderRadius.circular(24), + ), + child: IconButton( + icon: Icon(Icons.arrow_forward_rounded, size: 24), + color: Colors.white, + onPressed: () => widget.onOk(), + ), + ); + } + + Widget buildShowTagOptions() { + // 计算弹窗位置(基于标签按钮位置) + return Positioned( + bottom: 50, // 调整垂直位置 + left: 60, // 调整水平位置 + child: Container( + width: 90, + padding: EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 8, + offset: Offset(0, 2), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: + FlispTag.values.map((tag) { + return GestureDetector( + onTap: () => _selectTag(tag), + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8), + child: Row( + children: [ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: tag.color, + shape: BoxShape.circle, + ), + child: Icon( + tag.icon, + size: 12, + color: Colors.white, + ), + ), + SizedBox(width: 8), + Text(tag.label, style: TextStyle(fontSize: 12)), + ], + ), + ), + ); + }).toList(), ), - child: Icon(icon, size: 18, color: color), ), ); } @@ -105,182 +228,45 @@ class _FlispFormState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // 功能图标区域 Row( children: [ - // 上传图片按钮 - buildIconButton( - icon: Icons.photo_library_rounded, - color: Colors.blue, - onTap: () { - // 上传图片功能 - }, - ), - + buildSelectImageButton(), SizedBox(width: 12), - - // 选择标签按钮 - buildIconButton( - icon: Icons.local_offer_rounded, - color: Colors.orange, - onTap: () { - // 选择标签功能 - }, - ), + buildSelectTagButton(), ], ), - - // 确认按钮(箭头形状) - Container( - width: 48, - height: 48, - decoration: BoxDecoration( - color: Theme.of(context).primaryColor, - borderRadius: BorderRadius.circular(24), - border: Border.all(color: Theme.of(context).primaryColor, width: 1), - ), - child: IconButton( - icon: Icon(Icons.arrow_forward_rounded, size: 24), - color: Colors.white, - onPressed: () => widget.onOk(), - ), - ), + buildConfirmButton(), ], ), ); } - // 选择的标签显示 - Widget buildSelectedTags() { - // 模拟选择的标签 - final selectedTags = ['旅行', '美食']; + // 整体使用Stack布局分离事件层级 + return Stack( + children: [ + // 外层点击区域(用于关闭弹窗) + GestureDetector( + onTap: () { + if (_showTagOptions) { + setState(() => _showTagOptions = false); + } + }, + // 弹窗显示时才拦截事件,隐藏时不影响其他点击 + behavior: + _showTagOptions + ? HitTestBehavior.opaque + : HitTestBehavior.translucent, + ), - if (selectedTags.isEmpty) return SizedBox(); + // 主表单内容 + FormBuilder( + key: widget.formKey, + child: Column(children: [buildFormBody(), buildBottomButton()]), + ), - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 8), - Wrap( - spacing: 8, - runSpacing: 8, - children: - selectedTags.map((tag) { - return Container( - padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - color: Colors.orange.withOpacity(0.1), - borderRadius: BorderRadius.circular(16), - border: Border.all(color: Colors.orange.withOpacity(0.3)), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - tag, - style: TextStyle( - fontSize: 12, - color: Colors.orange, - fontWeight: FontWeight.w500, - ), - ), - SizedBox(width: 4), - GestureDetector( - onTap: () { - // 删除标签 - }, - child: Icon( - Icons.close, - size: 14, - color: Colors.orange, - ), - ), - ], - ), - ); - }).toList(), - ), - SizedBox(height: 12), - ], - ); - } - - // 图片预览 - Widget buildImagePreview() { - // 模拟上传的图片 - final imageItems = [ - {'name': 'photo1.jpg'}, - ]; - - if (imageItems.isEmpty) return SizedBox(); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Wrap( - spacing: 8, - runSpacing: 8, - children: - imageItems.map((image) { - return Container( - padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: BoxDecoration( - color: Colors.blue.withOpacity(0.1), - borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.blue.withOpacity(0.3)), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.photo, size: 16, color: Colors.blue), - SizedBox(width: 6), - Text( - image['name']!, - style: TextStyle(fontSize: 12, color: Colors.blue), - ), - SizedBox(width: 4), - GestureDetector( - onTap: () { - // 删除图片 - }, - child: Icon( - Icons.close, - size: 14, - color: Colors.blue, - ), - ), - ], - ), - ); - }).toList(), - ), - SizedBox(height: 12), - ], - ); - } - - return FormBuilder( - key: widget.formKey, - child: Column( - children: [ - // 文本输入区域 - Expanded( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - buildContentField(), - SizedBox(height: 4), - buildSelectedTags(), - buildImagePreview(), - ], - ), - ), - ), - // 底部功能按钮行 - buildBottomButton(), - ], - ), + // 标签弹窗(放在最上层,确保事件优先响应) + if (_showTagOptions) buildShowTagOptions(), + ], ); } } diff --git a/lib/widgets/flisp_widget.dart b/lib/widgets/flisp_widget.dart index 8a2593b..b72ae82 100644 --- a/lib/widgets/flisp_widget.dart +++ b/lib/widgets/flisp_widget.dart @@ -49,11 +49,7 @@ Widget _buildFlispContent(Flisp flisp) { return RichText( text: TextSpan( text: flisp.content, - style: const TextStyle( - color: Colors.black87, - fontWeight: FontWeight.bold, - fontSize: 18, - ), + style: const TextStyle(color: Colors.black87, fontSize: 16), ), ); } @@ -136,28 +132,50 @@ Widget _buildFlispVideo(Flisp flisp) { ); } -Widget _buildFlispTag(Flisp flisp) { - return Row( - children: [ - // 只有一个标签,直接显示 - Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), - decoration: BoxDecoration( - color: Colors.orange, - borderRadius: BorderRadius.circular(12), +Widget buildFlispTag(Flisp flisp) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: flisp.tag.color.withAlpha(30), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: flisp.tag.color.withAlpha(60)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 14, + height: 14, + decoration: BoxDecoration( + color: flisp.tag.color, + shape: BoxShape.circle, + ), + child: Icon(flisp.tag.icon, size: 8, color: Colors.white), ), - child: Text( + SizedBox(width: 6), + Text( flisp.tag.label, - style: const TextStyle(fontSize: 12, color: Colors.white), + style: TextStyle( + fontSize: 12, + color: flisp.tag.color, + fontWeight: FontWeight.w500, + ), ), - ), - const Spacer(), - // 时间在后 - Text( - DateFormat('yyyy-MM-dd HH:mm').format(flisp.createTime), - style: TextStyle(fontSize: 12, color: Colors.grey[500]), - ), - ], + ], + ), + ); +} + +Widget buildFlispTime(Flisp flisp) { + return Text( + DateFormat('yyyy-MM-dd HH:mm:ss').format(flisp.createTime), + style: TextStyle(fontSize: 12, color: Colors.grey), + ); +} + +Widget _buildFlispSuffix(Flisp flisp) { + return Row( + children: [buildFlispTag(flisp), const Spacer(), buildFlispTime(flisp)], ); } @@ -180,7 +198,7 @@ Widget _buildFlispItem({ const SizedBox(height: 4), if (hasVideo) _buildFlispVideo(flisp), const SizedBox(height: 4), - _buildFlispTag(flisp), + _buildFlispSuffix(flisp), ], ), ), @@ -193,9 +211,9 @@ Widget buildEmptyState() { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(Icons.checklist, size: 80, color: Colors.orange.shade600), + Icon(Icons.flash_on, size: 80, color: Colors.orange.shade600), Text( - '📝 还没有闪灵\n点击➕号添加第一个任务吧~', + '📝 还没有闪灵\n点击➕号添加第一个灵感吧~', textAlign: TextAlign.center, style: TextStyle(color: Colors.orange.shade600), ),