import 'package:flutter/cupertino.dart'; import 'package:stacked/stacked.dart'; import '../../about/external/data/Goal.dart'; import '../../about/internal/application/TextType.dart'; import '../../configs/Navigator.dart'; import '../../designs/Component.dart'; import '../../designs/Responsive.dart'; import '../../designs/Shell.dart'; import '../../designs/buttons/Buttons.dart'; import '../../designs/text/Text.dart'; import '../../utils/Colors.dart'; import '../../utils/CommonUtils.dart'; import 'ConnectGoals.dart'; import 'GoalDetail.dart'; import 'GoalEditor.dart'; import 'Goals.dart'; import 'ViewGoals.dart'; class GoalsState extends State implements ConnectGoals { ViewGoals? _model; List _goals = []; @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( viewModelBuilder: () => ViewGoals(context, this), onViewModelReady: (viewModel) { _model = viewModel; _initiate(); }, builder: (context, viewModel, child) => LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Responsive( mobile: _mobileView(constraints), tablet: _mobileView(constraints), desktop: _mobileView(constraints), ); }, ), ); } void _initiate() { _model?.loadGoals(); } void _onBack() { Navigator.pop(context); } void _onOpenGoal(Goal goal) async { final result = await GroundedNavigation() .navigateToPageWithData(GoalDetail(goal: goal), context); if (result == true) { _model?.loadGoals(); } } void _onNewGoal() async { final result = await GroundedNavigation() .navigateToPageWithData(const GoalEditor(), context); if (result is Goal) { _model?.loadGoals(); } } Widget _mobileView(BoxConstraints constraints) { return Sheet( eyebrow: "Grounded", title: "Goals", onBack: _onBack, action: chromeAction(CupertinoIcons.add, _onNewGoal), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ displayTitle("What you are\nworking toward."), const SizedBox(height: 14), text( "A goal holds the tasks that get you there. The goal never carries debt โ€” the tasks inside it do.", 14, TextType.Regular, color: colorGrey2, height: 1.55, ), const SizedBox(height: 28), if (_goals.isEmpty) emptyState( CupertinoIcons.flag, "No goals yet", "Create one โ€” Workout, say โ€” then put the actual sessions inside it.", ) else ..._goals.map(_goalCard), const SizedBox(height: 24), roundedCornerButton("New goal", _onNewGoal, icon: CupertinoIcons.add), ], ), ); } Widget _goalCard(Goal goal) { final Color accent = goal.slipping ? colorStandingGrounded : goalColor(goal.colourHex); return Container( margin: const EdgeInsets.only(bottom: 12), child: card( onTap: () => _onOpenGoal(goal), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Container( width: 8, height: 8, decoration: BoxDecoration( color: goalColor(goal.colourHex), shape: BoxShape.circle, ), ), const SizedBox(width: 8), text( [ if (goal.category.isNotEmpty) goal.category, goal.targetDate == null ? "No deadline" : targetLabel(goal.targetDate), ].join(" ยท ").toUpperCase(), 9, TextType.Bold, color: goal.targetDate != null && goal.targetDate!.isBefore(DateTime.now()) ? colorStandingGrounded : colorGrey2, letterSpacing: 1.0, ), ], ), const SizedBox(height: 8), text(goal.title, 19, TextType.Light, color: colorPrimaryDark, maxLines: 2, overflow: TextOverflow.ellipsis), if (goal.description.isNotEmpty) ...[ const SizedBox(height: 6), text(goal.description, 12, TextType.Regular, color: colorGrey2, maxLines: 2, overflow: TextOverflow.ellipsis), ], ], ), ), const SizedBox(width: 10), if (goal.overdueTasks > 0) pill("${goal.overdueTasks} late", colorStandingGrounded, colorStandingGroundedBg, textSize: 9), ], ), const SizedBox(height: 18), meter(goal.progress, fill: accent), const SizedBox(height: 12), Row( children: [ Expanded( child: labelled( "Done", "${goal.completedTasks} of ${goal.totalTasks}", valueSize: 13, ), ), Expanded( child: labelled( "Remaining", "${goal.remainingTasks}", valueSize: 13, ), ), Expanded( child: labelled( "Debt", formatDebt(goal.debtContribution), valueSize: 13, valueColor: goal.debtContribution > 0 ? colorStandingGrounded : colorPrimaryDark, ), ), ], ), ], ), ), ); } @override void onGoalsLoaded(List goals) { setState(() { _goals = goals; }); } }