diff --git a/assets/image/kbn.png b/assets/image/kbn.png new file mode 100644 index 0000000..52ccdc5 Binary files /dev/null and b/assets/image/kbn.png differ diff --git a/assets/image/xiaozhi.png b/assets/image/xiaozhi.png deleted file mode 100644 index f169ad7..0000000 Binary files a/assets/image/xiaozhi.png and /dev/null differ diff --git a/lib/api/dto/daily_tip_dto.dart b/lib/api/dto/daily_tip_dto.dart new file mode 100644 index 0000000..d53eb57 --- /dev/null +++ b/lib/api/dto/daily_tip_dto.dart @@ -0,0 +1,21 @@ +class DailyTipDto { + int? id; + String? title; + String? content; + + DailyTipDto({this.id, this.title, this.content}); + + Map toJson() { + final map = {}; + map["id"] = id; + map["title"] = title; + map["content"] = content; + return map; + } + + DailyTipDto.fromJson(dynamic json) { + id = json["id"] ?? 0; + title = json["title"] ?? ""; + content = json["content"] ?? ""; + } +} diff --git a/lib/api/dto/plan_detail_dto.dart b/lib/api/dto/plan_detail_dto.dart index cfa74b8..31d1172 100644 --- a/lib/api/dto/plan_detail_dto.dart +++ b/lib/api/dto/plan_detail_dto.dart @@ -24,6 +24,21 @@ class PlanStepDto { stepExplain = json["step_explain"]; stepStatus = json["step_status"]; } + + /// 浅拷贝(只复制字段,不生成新 id) + PlanStepDto copyWith({ + int? id, + String? stepIcon, + String? stepContent, + String? stepExplain, + int? stepStatus, + }) => PlanStepDto( + id: id ?? this.id, + stepIcon: stepIcon ?? this.stepIcon, + stepContent: stepContent ?? this.stepContent, + stepExplain: stepExplain ?? this.stepExplain, + stepStatus: stepStatus ?? this.stepStatus, + ); } class PlanDetailDto { diff --git a/lib/api/endpoints/other.dart b/lib/api/endpoints/other.dart new file mode 100644 index 0000000..b43c8bd --- /dev/null +++ b/lib/api/endpoints/other.dart @@ -0,0 +1,8 @@ +import 'package:plan/api/dto/daily_tip_dto.dart'; +import 'package:plan/api/network/request.dart'; + +///获取每日一句列表 +Future> getDailyTipList() async { + var res = await Request().get("/daily_tips"); + return (res as List).map((e) => DailyTipDto.fromJson(e)).toList(); +} diff --git a/lib/api/endpoints/plan_api.dart b/lib/api/endpoints/plan_api.dart index 8bf2c96..a02b46a 100644 --- a/lib/api/endpoints/plan_api.dart +++ b/lib/api/endpoints/plan_api.dart @@ -15,7 +15,7 @@ Future initPlanApi(String need, int agentId) async { ///保存用户计划 Future savePlanApi({ - required String planId, + required int planId, required String summary, required String dialog, required List steps, @@ -45,7 +45,7 @@ Future editPlanSummaryApi(int planId, String summary) async { } ///获取计划详情 -Future getPlanDetailApi(String planId) async { +Future getPlanDetailApi(int planId) async { var res = await Request().get("/plan/plan_detail", { "plan_id": planId, }); diff --git a/lib/page/home/widget/plan_form_card.dart b/lib/page/home/widget/plan_form_card.dart index 8bd1969..adfd5c1 100644 --- a/lib/page/home/widget/plan_form_card.dart +++ b/lib/page/home/widget/plan_form_card.dart @@ -12,7 +12,7 @@ class PlanFormCard extends StatefulWidget { } class _PlanFormCardState extends State { - final TextEditingController _inputController = TextEditingController(text: "刷牙"); + final TextEditingController _inputController = TextEditingController(text: ""); void _handSubmit() { if (_inputController.text.isEmpty) { @@ -36,7 +36,7 @@ class _PlanFormCardState extends State { top: 56, child: SizedBox( height: 100, - child: Image.asset("assets/image/xiaozhi.png"), + child: Image.asset("assets/image/kbn.png"), ), ), Container( @@ -48,14 +48,14 @@ class _PlanFormCardState extends State { children: [ Container( margin: EdgeInsets.only(bottom: 20), - child: Text("有什么事情你一直在拖延?"), + child: Text("What have you been putting off?"), ), TextField( controller: _inputController, style: Theme.of(context).textTheme.bodyMedium, maxLength: 40, decoration: InputDecoration( - hintText: "我躺在床上听歌", + hintText: "Clean the kitchen", fillColor: Theme.of(context).colorScheme.surfaceContainerLow, filled: true, enabledBorder: OutlineInputBorder( @@ -83,7 +83,7 @@ class _PlanFormCardState extends State { border: Border.all(color: Colors.black, width: 1.5), ), child: Text( - "创建计划", + "Create Plan", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, diff --git a/lib/page/home/widget/quote_card.dart b/lib/page/home/widget/quote_card.dart index 096f371..fe67541 100644 --- a/lib/page/home/widget/quote_card.dart +++ b/lib/page/home/widget/quote_card.dart @@ -1,4 +1,7 @@ +import 'package:animated_reorderable_list/animated_reorderable_list.dart'; import 'package:flutter/material.dart'; +import 'package:plan/api/dto/daily_tip_dto.dart'; +import 'package:plan/api/endpoints/other.dart'; import 'package:remixicon/remixicon.dart'; class QuoteCard extends StatefulWidget { @@ -9,8 +12,37 @@ class QuoteCard extends StatefulWidget { } class _QuoteCardState extends State { + List _list = []; + int _currentIndex = 0; + + ///获取当前语句 + DailyTipDto? get currentTip => _list.isEmpty ? null : _list[_currentIndex]; + + @override + void initState() { + super.initState(); + _init(); + } + + void _init() async { + var res = await getDailyTipList(); + setState(() { + _list = res; + }); + } + + ///切换下一条 + void _next() { + setState(() { + _currentIndex = (_currentIndex + 1) % _list.length; + }); + } + @override Widget build(BuildContext context) { + if (_list.isEmpty) { + return SizedBox(); + } return Container( margin: EdgeInsets.only(top: 20), padding: const EdgeInsets.all(3), @@ -30,26 +62,27 @@ class _QuoteCardState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "每个教练都有什么特长?", - style: Theme.of(context).textTheme.titleSmall, + _fadeText( + currentTip?.title ?? "", + Theme.of(context).textTheme.titleSmall, ), - Container( - margin: EdgeInsets.only(top: 10), - child: Text( - "在主页点击教练可以查看介绍", - style: Theme.of(context).textTheme.labelMedium, - ), + SizedBox(height: 10), + _fadeText( + currentTip?.content ?? "", + Theme.of(context).textTheme.labelMedium, ), - Container( - margin: EdgeInsets.only(top: 10), - child: Row( - spacing: 5, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(RemixIcons.arrow_right_circle_line, size: 18), - Text("下一条", style: Theme.of(context).textTheme.bodySmall), - ], + GestureDetector( + onTap: _next, + child: Container( + margin: EdgeInsets.only(top: 10), + child: Row( + spacing: 5, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(RemixIcons.arrow_right_circle_line, size: 18), + Text("Next", style: Theme.of(context).textTheme.bodySmall), + ], + ), ), ), ], @@ -57,4 +90,40 @@ class _QuoteCardState extends State { ), ); } + + ///渐变文字动画 + Widget _fadeText(String text, TextStyle? style) { + return AnimatedSize( + duration: const Duration(milliseconds: 400), + child: Container( + alignment: Alignment.topLeft, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: (Widget child, Animation animation) { + return FadeTransition( + opacity: animation, + child: child, + ); + }, + layoutBuilder: (Widget? currentChild, List previousChildren) { + return Stack( + children: [ + if (currentChild != null) currentChild, // 新 child + Positioned( + top: 0, + child: Opacity( + opacity: 0.3, + child: Column( + children: previousChildren, + ), + ), + ), + ], + ); + }, + child: Text(text, key: Key(text), style: style), + ), + ), + ); + } } diff --git a/lib/page/my/my_page.dart b/lib/page/my/my_page.dart index ece0768..b617c7f 100644 --- a/lib/page/my/my_page.dart +++ b/lib/page/my/my_page.dart @@ -93,7 +93,7 @@ class _MyPageState extends State { return CupertinoPageScaffold( backgroundColor: Colors.white, navigationBar: CupertinoNavigationBar( - middle: Text("个人资料"), + middle: Text("Profile"), leading: IconButton( onPressed: () { widget.scaffoldKey.currentState?.closeDrawer(); @@ -118,7 +118,7 @@ class _MyPageState extends State { padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), onPressed: _handLogout, child: Text( - "退出登录", + "Log Out", style: TextStyle(color: Colors.white, fontSize: 14), ), ), @@ -126,7 +126,7 @@ class _MyPageState extends State { CupertinoButton( onPressed: _handDelete, child: const Text( - "删除账号", + "Delete Account", style: TextStyle(color: CupertinoColors.systemRed, fontSize: 14), ), ), diff --git a/lib/page/my/widget/avatar_name.dart b/lib/page/my/widget/avatar_name.dart index 2c7afd5..17197dc 100644 --- a/lib/page/my/widget/avatar_name.dart +++ b/lib/page/my/widget/avatar_name.dart @@ -132,7 +132,7 @@ class _AvatarNameState extends State { style: Theme.of(context).textTheme.titleSmall, ), child: Text( - "教练如何称呼你?", + "What should we call you?", style: Theme.of(context).textTheme.labelMedium, ), ), @@ -150,7 +150,7 @@ class _AvatarNameState extends State { style: TextStyle(fontSize: 14), maxLength: 20, decoration: InputDecoration( - hintText: "输入你的姓名", + hintText: "Enter your name", isCollapsed: true, contentPadding: EdgeInsets.symmetric(vertical: 8, horizontal: 10), enabledBorder: OutlineInputBorder( diff --git a/lib/page/my/widget/profile_section.dart b/lib/page/my/widget/profile_section.dart index 523e287..5c9fa81 100644 --- a/lib/page/my/widget/profile_section.dart +++ b/lib/page/my/widget/profile_section.dart @@ -20,9 +20,9 @@ class _ProfileSectionState extends State { final TextEditingController _inputController = TextEditingController(); final List _tips = [ - "教练每次为你制定计划时,都会首先参考这里的信息", - "你分享的背景信息越详细,教练就越能为你量身定制,符合你独特情况的行动步骤", - "你可以在这里为教练提需求,比如“我不吃香菜”", + "Whenever your coach creates a plan for you, they’ll start by looking at this information.", + "The more details you share, the better your coach can tailor steps that fit your unique situation.", + "You can also add requests here — for example, ‘I don’t eat cilantro.", ]; //防抖 @@ -67,7 +67,7 @@ class _ProfileSectionState extends State { children: [ Container( margin: EdgeInsets.only(bottom: 20), - child: Text("你的画像"), + child: Text("About You"), ), Container( margin: EdgeInsets.only(bottom: 20), @@ -78,7 +78,7 @@ class _ProfileSectionState extends State { style: TextStyle(fontSize: 14, letterSpacing: 1), onChanged: _onTextChanged, decoration: InputDecoration( - hintText: "我是19岁女生,刷碗时用洗碗机,请不要按手洗拆解步骤..", + hintText: "I’m 19 and always use a dishwasher. Please don’t follow the hand-wash disassembly steps.", enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.grey), // 普通状态 borderRadius: BorderRadius.circular(8), diff --git a/lib/page/plan/detail/navigation/bar_actions.dart b/lib/page/plan/detail/other/bar_actions.dart similarity index 90% rename from lib/page/plan/detail/navigation/bar_actions.dart rename to lib/page/plan/detail/other/bar_actions.dart index 2a122ac..4f4bc86 100644 --- a/lib/page/plan/detail/navigation/bar_actions.dart +++ b/lib/page/plan/detail/other/bar_actions.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:plan/api/endpoints/plan_api.dart'; import 'package:plan/widgets/ui_kit/popup/popup_action.dart'; import 'package:remixicon/remixicon.dart'; @@ -29,7 +28,7 @@ class _BarActionsState extends State { widget.store.updatePlanDetail((dto) { dto.summary = value; }); - await editPlanSummaryApi(int.parse(widget.store.planId), value); + await editPlanSummaryApi(widget.store.planId, value); }, ); } @@ -64,11 +63,11 @@ class _BarActionsState extends State { items: [ PopupMenuItem( value: 'edit_step', - child: Text("编辑步骤"), + child: Text("Edit Steps"), ), PopupMenuItem( value: 'edit_desc', - child: Text("编辑摘要"), + child: Text("Edit Summary"), ), ], child: Icon(RemixIcons.more_fill), diff --git a/lib/page/plan/detail/other/edit_popup.dart b/lib/page/plan/detail/other/edit_popup.dart new file mode 100644 index 0000000..33061e3 --- /dev/null +++ b/lib/page/plan/detail/other/edit_popup.dart @@ -0,0 +1,135 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:plan/api/dto/plan_detail_dto.dart'; + +class EditPopup extends StatefulWidget { + final PlanStepDto step; + final Function(PlanStepDto) onEdit; + + const EditPopup({super.key, required this.step, required this.onEdit}); + + @override + State createState() => _EditPopupState(); +} + +class _EditPopupState extends State { + ///输入框 + final TextEditingController _titleController = TextEditingController(); + final TextEditingController _descriptionController = TextEditingController(); + + @override + void initState() { + super.initState(); + _titleController.text = widget.step.stepContent ?? ""; + _descriptionController.text = widget.step.stepExplain ?? ""; + + // 监听任何一次输入 + _titleController.addListener(_updateSaveButton); + _descriptionController.addListener(_updateSaveButton); + } + + ///是否允许保存 + get isSave => _titleController.text.isNotEmpty && _descriptionController.text.isNotEmpty; + + void _updateSaveButton() => setState(() {}); + + ///保存 + void _handSave() { + //更新数据 + var step = widget.step.copyWith( + stepContent: _titleController.text, + stepExplain: _descriptionController.text, + ); + + widget.onEdit(step); + context.pop(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 40), + child: Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10), + ), + ), + child: CupertinoPageScaffold( + backgroundColor: Theme.of(context).colorScheme.surfaceContainer, + navigationBar: CupertinoNavigationBar( + middle: Text("Edit Steps"), + leading: CupertinoButton( + padding: EdgeInsets.zero, + onPressed: () { + context.pop(); + }, + child: Text("Cancel", style: TextStyle(color: CupertinoColors.black)), + ), + trailing: CupertinoButton( + padding: EdgeInsets.zero, + + onPressed: isSave ? _handSave : null, + child: Text("Save"), + ), + ), + child: SafeArea( + child: ListView( + padding: EdgeInsets.all(15), + children: [ + _smallTitle("STEP DETAILS"), + Container( + padding: EdgeInsets.all(15), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + _input(_titleController, "Task"), + Divider(), + _input(_descriptionController, "Description"), + ], + ), + ), + ], + ), + ), + ), + ), + ); + } + + Widget _smallTitle(String title) { + return Container( + margin: EdgeInsets.only(bottom: 5), + padding: EdgeInsets.only(left: 10), + child: Text( + title, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + fontWeight: FontWeight.w700, + ), + ), + ); + } + + ///输入框 + Widget _input(TextEditingController controller, String hint) { + return TextField( + maxLines: null, + minLines: 3, + // 允许无限换行 + keyboardType: TextInputType.multiline, + controller: controller, + style: TextStyle(fontSize: 16), + decoration: InputDecoration( + hintText: hint, + border: InputBorder.none, + isCollapsed: true, + ), + ); + } +} diff --git a/lib/page/plan/detail/plan_detail_page.dart b/lib/page/plan/detail/plan_detail_page.dart index fa6af2b..0fb825f 100644 --- a/lib/page/plan/detail/plan_detail_page.dart +++ b/lib/page/plan/detail/plan_detail_page.dart @@ -1,10 +1,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:plan/page/plan/detail/navigation/bar_actions.dart'; import 'package:plan/page/plan/detail/viewmodel/plan_detail_store.dart'; import 'package:plan/theme/decorations/app_shadows.dart'; import 'package:provider/provider.dart'; +import 'other/bar_actions.dart'; import 'widgets/avatar_card.dart'; import 'widgets/coach_message.dart'; import 'widgets/plan_list.dart'; @@ -12,7 +12,7 @@ import 'widgets/scroll_box.dart'; import 'widgets/suggested.dart'; class PlanDetailPage extends StatefulWidget { - final String? id; + final int? id; final String? planName; const PlanDetailPage({ @@ -35,7 +35,7 @@ class _PlanDetailPageState extends State { void initState() { super.initState(); store = PlanDetailStore( - planId: widget.id.toString(), + planId: widget.id ?? 0, planContent: widget.planName ?? "", scrollController: scrollController, ); diff --git a/lib/page/plan/detail/viewmodel/plan_detail_store.dart b/lib/page/plan/detail/viewmodel/plan_detail_store.dart index f28344b..f6bdfd6 100644 --- a/lib/page/plan/detail/viewmodel/plan_detail_store.dart +++ b/lib/page/plan/detail/viewmodel/plan_detail_store.dart @@ -8,11 +8,11 @@ class PlanDetailStore extends ChangeNotifier { ///构造函数 PlanDetailStore({ this.planContent = "", - this.planId = "", + required this.planId, required this.scrollController, }) { //如果没有id进行初始化 - if (planId == "0") { + if (planId == 0) { createPlan(); } else { //获取详情 @@ -37,10 +37,10 @@ class PlanDetailStore extends ChangeNotifier { String planContent = ""; ///计划id - String planId = ""; + int planId; ///计划详情 - PlanDetailDto planDetail = PlanDetailDto(summary: "计划详情"); + PlanDetailDto planDetail = PlanDetailDto(summary: "Plan Details"); ///是否正在编辑 bool isEdit = false; @@ -57,7 +57,7 @@ class PlanDetailStore extends ChangeNotifier { ///创建计划 void createPlan() async { var id = await initPlanApi(planContent, 1); - planId = id.toString(); + planId = id; ///生成摘要--------------------------- String summary = ""; @@ -162,7 +162,8 @@ class PlanDetailStore extends ChangeNotifier { steps: planDetail.stepsList, suggestions: planDetail.suggestionsList, ); - EasyLoading.showToast("计划创建成功"); + await getPlanDetail(); + EasyLoading.showToast("Plan created!"); } ///获取详情 @@ -176,4 +177,13 @@ class PlanDetailStore extends ChangeNotifier { updater(planDetail); notifyListeners(); } + + ///更新步骤 + void updateStep(PlanStepDto newStep) { + final int index = planDetail.stepsList.indexWhere((e) => e.id == newStep.id); + if (index != -1) { + planDetail.stepsList[index] = newStep; // 引用变了 → 框架感知 + notifyListeners(); + } + } } diff --git a/lib/page/plan/detail/widgets/avatar_card.dart b/lib/page/plan/detail/widgets/avatar_card.dart index f67d821..70fa6f6 100644 --- a/lib/page/plan/detail/widgets/avatar_card.dart +++ b/lib/page/plan/detail/widgets/avatar_card.dart @@ -94,6 +94,7 @@ class _AvatarCardState extends State with SingleTickerProviderStateM child: Stack( children: [ Container( + width: double.infinity, padding: EdgeInsets.all(4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(3), @@ -119,7 +120,7 @@ class _AvatarCardState extends State with SingleTickerProviderStateM child: Stack( alignment: Alignment.bottomCenter, children: [ - Image.asset("assets/image/xiaozhi.png", height: 100), + Image.asset("assets/image/kbn.png", height: 100), Positioned( top: 20, child: Transform.translate( diff --git a/lib/page/plan/detail/widgets/coach_message.dart b/lib/page/plan/detail/widgets/coach_message.dart index 9bd87e6..ee2df40 100644 --- a/lib/page/plan/detail/widgets/coach_message.dart +++ b/lib/page/plan/detail/widgets/coach_message.dart @@ -21,7 +21,7 @@ class _CoachMessageState extends State { child: Column( children: [ Text( - "你的教练正在拆分", + "Organizing your plan…", style: Theme.of(context).textTheme.bodyMedium, ), Text( diff --git a/lib/page/plan/detail/widgets/plan_list.dart b/lib/page/plan/detail/widgets/plan_list.dart index cb85972..7d80f2f 100644 --- a/lib/page/plan/detail/widgets/plan_list.dart +++ b/lib/page/plan/detail/widgets/plan_list.dart @@ -10,6 +10,8 @@ import 'package:plan/widgets/business/delete_row_item.dart'; import 'package:provider/provider.dart'; import 'package:remixicon/remixicon.dart'; +import '../other/edit_popup.dart'; + class PlanList extends StatefulWidget { const PlanList({super.key}); @@ -30,7 +32,7 @@ class _PlanListState extends State { dto.stepsList = dto.stepsList.where((element) => element.id != id).toList(); }); await editPlanStepApi( - int.parse(store.planId), + store.planId, act: PlanActionType.delete, stepId: id, ); @@ -42,37 +44,56 @@ class _PlanListState extends State { store.updatePlanDetail((dto) { dto.stepsList = list; }); - await editPlanStepOrderApi(int.parse(store.planId), list); + await editPlanStepOrderApi(store.planId, list); } ///确认完成或者取消 - void _handComplete(int id) async { + void _handComplete(PlanStepDto newStep) async { HapticFeedback.vibrate(); var store = context.read(); + newStep.stepStatus = newStep.stepStatus == 2 ? 0 : 2; + //更新数据 + store.updateStep(newStep); + //接口更新 + editPlanStepApi( + store.planId, + act: PlanActionType.complete, + stepId: newStep.id, + ); + } - store.updatePlanDetail((dto) { - dto.stepsList = dto.stepsList.map((step) { - if (step.id == id) { - // 只更新匹配的项 - step.stepStatus = step.stepStatus == 2 ? 0 : 2; - editPlanStepApi( - int.parse(store.planId), - act: PlanActionType.complete, - stepId: id, - ); - } - return step; - }).toList(); - }); + ///打开编辑 + void _handEditDialog(PlanStepDto step) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) { + return EditPopup( + step: step, + onEdit: (newStep) { + var store = context.read(); + store.updateStep(newStep); + // 接口 + editPlanStepApi( + store.planId, + act: PlanActionType.edit, + stepId: newStep.id, + content: newStep.stepContent, + explain: newStep.stepExplain, + ); + }, + ); + }, + ); } @override Widget build(BuildContext context) { final isEdit = context.select((s) => s.isEdit); return Selector>( - selector: (_, store) => store.planDetail.stepsList - .map((e) => '${e.id}_${e.stepExplain}_${e.stepStatus}') - .toList(), + selector: (_, store) => + store.planDetail.stepsList.map((e) => e.toJson().toString()).toList(), builder: (context, list, _) { final list = context.read().planDetail.stepsList; return AnimatedReorderableListView( @@ -87,6 +108,7 @@ class _PlanListState extends State { isEdit: isEdit, onDelete: _handDelete, onComplete: _handComplete, + onEdit: _handEditDialog, ); }, enterTransition: [SlideInDown()], @@ -116,7 +138,8 @@ class PlanItem extends StatelessWidget { final PlanStepDto step; final bool isEdit; final Function(int) onDelete; - final Function(int) onComplete; + final Function(PlanStepDto) onComplete; + final Function(PlanStepDto) onEdit; const PlanItem({ super.key, @@ -124,6 +147,7 @@ class PlanItem extends StatelessWidget { this.isEdit = false, required this.onDelete, required this.onComplete, + required this.onEdit, }); @override @@ -131,7 +155,7 @@ class PlanItem extends StatelessWidget { return GestureDetector( onTap: () { if (!isEdit) { - onComplete(step.id!); + onComplete(step); } }, child: Container( @@ -184,12 +208,17 @@ class PlanItem extends StatelessWidget { SizeTransition( axis: Axis.horizontal, sizeFactor: animate, - child: Container( - alignment: Alignment.center, - margin: EdgeInsets.only(left: 10), - child: Opacity( - opacity: 0.4, - child: Icon(RemixIcons.menu_line), + child: GestureDetector( + onTap: () { + onEdit(step); + }, + child: Container( + alignment: Alignment.center, + margin: EdgeInsets.only(left: 10), + child: Opacity( + opacity: 0.4, + child: Icon(RemixIcons.edit_box_line), + ), ), ), ), diff --git a/lib/page/plan/detail/widgets/suggested.dart b/lib/page/plan/detail/widgets/suggested.dart index 7ca2bf1..f85333d 100644 --- a/lib/page/plan/detail/widgets/suggested.dart +++ b/lib/page/plan/detail/widgets/suggested.dart @@ -37,7 +37,7 @@ class SuggestedTitle extends StatelessWidget { color: Theme.of(context).colorScheme.surfaceContainerHigh, ), Text( - "额外建议", + "Additional Suggestions", style: Theme.of(context).textTheme.titleSmall, ), Container( diff --git a/lib/page/plan/history/plan_history_page.dart b/lib/page/plan/history/plan_history_page.dart index 5bd1993..0dcbdb9 100644 --- a/lib/page/plan/history/plan_history_page.dart +++ b/lib/page/plan/history/plan_history_page.dart @@ -36,13 +36,11 @@ class _PlanHistoryPageState extends State { }); } - ///确认删除 void _confirmDelete(int id) { setState(() { _record.removeWhere((element) => element.id == id); }); - } @override @@ -50,7 +48,7 @@ class _PlanHistoryPageState extends State { return CupertinoPageScaffold( backgroundColor: Theme.of(context).colorScheme.surfaceContainer, navigationBar: CupertinoNavigationBar( - middle: const Text("计划历史"), + middle: const Text("Plan History"), ), child: SafeArea( child: CustomScrollView( @@ -77,6 +75,7 @@ class _PlanHistoryPageState extends State { item: _record[index], showDelete: _isDelete, onDelete: _confirmDelete, + onInit: _onRefresh, ); }, separatorBuilder: (BuildContext context, int index) { diff --git a/lib/page/plan/history/widgets/history_item.dart b/lib/page/plan/history/widgets/history_item.dart index 9253ad3..7db74ca 100644 --- a/lib/page/plan/history/widgets/history_item.dart +++ b/lib/page/plan/history/widgets/history_item.dart @@ -5,7 +5,6 @@ import 'package:plan/api/dto/plan_item_dto.dart'; import 'package:plan/api/endpoints/plan_api.dart'; import 'package:plan/router/config/route_paths.dart'; import 'package:plan/utils/format.dart'; -import 'package:plan/widgets/business/delete_row_item.dart'; import 'package:remixicon/remixicon.dart'; import '../../widgets/edit_desc_dialog.dart'; @@ -14,12 +13,14 @@ class HistoryItem extends StatefulWidget { final PlanItemDto item; final bool showDelete; final Function(int) onDelete; + final Function() onInit; const HistoryItem({ super.key, required this.item, this.showDelete = false, required this.onDelete, + required this.onInit, }); @override @@ -46,8 +47,9 @@ class _HistoryItemState extends State { } ///跳转详情 - void _goDetail() { - context.push(RoutePaths.planDetail(_data.id)); + void _goDetail() async{ + await context.push(RoutePaths.planDetail(_data.id)); + widget.onInit(); } ///编辑摘要 @@ -75,6 +77,7 @@ class _HistoryItemState extends State { @override Widget build(BuildContext context) { return CupertinoContextMenu( + enableHapticFeedback: true, actions: [ // 编辑摘要 CupertinoContextMenuAction( diff --git a/lib/page/system/login/login_page.dart b/lib/page/system/login/login_page.dart index 75aa25d..da32040 100644 --- a/lib/page/system/login/login_page.dart +++ b/lib/page/system/login/login_page.dart @@ -84,7 +84,6 @@ class _LoginPageState extends State { GoogleSignInAccount? user = await _googleSignIn.authenticate(); var auth = user.authentication; - // var res = await Dio().get("https://oauth2.googleapis.com/tokeninfo?id_token=${auth.idToken}"); //登陆 EasyLoading.show(status: "Logging in..."); var res = await thirdLoginApi(auth.idToken!, OtherLoginType.google); @@ -118,8 +117,6 @@ class _LoginPageState extends State { var res = await thirdLoginApi(credential.identityToken!, OtherLoginType.apple); EasyLoading.dismiss(); _onLogin(res); - print('Apple Credential: ${credential.identityToken}'); - print('Apple Email: ${credential.email}'); } catch (e) { print('Error during Apple sign-in: $e'); } diff --git a/lib/page/system/login/widget/agreement_box.dart b/lib/page/system/login/widget/agreement_box.dart index 473f64f..497bb80 100644 --- a/lib/page/system/login/widget/agreement_box.dart +++ b/lib/page/system/login/widget/agreement_box.dart @@ -24,7 +24,10 @@ class AgreementBox extends StatelessWidget { recognizer: TapGestureRecognizer() ..onTap = () => context.push( RoutePaths.agreement, - extra: {"title": "Terms of Service", "url": "https://support.curain.ai/privacy/foodcura/terms_service.html"}, + extra: { + "title": "Terms of Service", + "url": "https://support.curain.ai/privacy/plancura/terms_service.html", + }, ), ), const TextSpan(text: " and "), @@ -34,7 +37,10 @@ class AgreementBox extends StatelessWidget { recognizer: TapGestureRecognizer() ..onTap = () => context.push( RoutePaths.agreement, - extra: {"title": "Privacy", "url": "https://support.curain.ai/privacy/foodcura/privacy_policy.html"}, + extra: { + "title": "Privacy", + "url": "https://support.curain.ai/privacy/plancura/privacy_policy.html", + }, ), ), const TextSpan(text: "."), diff --git a/lib/router/modules/plan.dart b/lib/router/modules/plan.dart index 754a75b..896f8cf 100644 --- a/lib/router/modules/plan.dart +++ b/lib/router/modules/plan.dart @@ -17,7 +17,7 @@ List planRoutes = [ final extraMap = state.extra as Map?; return PlanDetailPage( - id: id, + id: int.parse(id ?? "0"), planName: extraMap?['name'], ); },