Merge NDM-App-221-fix-widget-api-mismatches-build-errors-after-merge into dev
This commit is contained in:
@@ -63,6 +63,12 @@ assets/
|
||||
|
||||
API base URL: `http://localhost:8090/api/v1/`
|
||||
|
||||
## Widget API Patterns
|
||||
- **Dual-param widgets**: `shared/widgets/` accept both entity param (e.g. `task: TaskEntity?`) AND individual raw params — private getters resolve which to use. Keep backward compat when extending.
|
||||
- **`PetType` canonical location**: `domain/entities/pet_type.dart` — never redeclare locally.
|
||||
- **`todayTasksProvider`** is a top-level alias for `tasksProvider` (no date filter in MVP).
|
||||
- **Pet identity vs. pet state**: `petType`/`petName` belong to `userProvider`; `petStateProvider` tracks energy/streak/rewards only.
|
||||
|
||||
## Naming Conventions
|
||||
- **Files**: `snake_case.dart`
|
||||
- **Classes**: `PascalCase`
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
# Widget API Mismatches — Fix
|
||||
|
||||
**Branch:** `NDM-App-221-fix-widget-api-mismatches-build-errors-after-merge`
|
||||
**Commits:** `371e826`, `adc2f72`
|
||||
|
||||
## Overview
|
||||
|
||||
After multiple feature branches were merged independently, the shared widgets and domain entities had diverged APIs causing 8 compilation errors. This fix aligns widget constructors with how `TasksScreen` and `ZenScreen` use them, adds missing entity properties, and resolves provider reference errors.
|
||||
|
||||
---
|
||||
|
||||
## Problems Fixed
|
||||
|
||||
| # | Error | Location |
|
||||
|---|-------|----------|
|
||||
| 1 | `EnergyBar` has no param `energyLevel` | `TasksScreen:65` |
|
||||
| 2 | `PetBubble` missing required param `petName` | `TasksScreen:69` |
|
||||
| 3 | `StreakDots` has no param `streakDays` | `TasksScreen:77` |
|
||||
| 4 | `TaskCard` has no param `task` | `TasksScreen:118,132` |
|
||||
| 5 | `todayTasksProvider` not defined | `TasksScreen:24` |
|
||||
| 6 | `PetStateEntity` missing `petType` getter | `TasksScreen:35` |
|
||||
| 7 | `EnergyBadge` has no param `energyLevel` | `ZenScreen:132` |
|
||||
| 8 | `PetStateEntity` constructor missing `updatedAt` | `pet_providers.dart:43` |
|
||||
|
||||
---
|
||||
|
||||
## Files Changed
|
||||
|
||||
### `lib/shared/widgets/energy_bar.dart`
|
||||
Added optional `energyLevel: int?` parameter alongside the existing `fraction: double?`.
|
||||
Both are now nullable; an `assert` ensures exactly one is provided.
|
||||
Private getter `_effectiveFraction` converts `energyLevel` → fraction via `(level / 3.0).clamp(0.0, 1.0)`.
|
||||
|
||||
```dart
|
||||
EnergyBar(energyLevel: petState.energyToday) // new usage
|
||||
EnergyBar(fraction: 0.66) // existing usage still works
|
||||
```
|
||||
|
||||
### `lib/shared/widgets/energy_badge.dart`
|
||||
Added optional `energyLevel: int?` parameter alongside the existing `level: EnergyLevel?`.
|
||||
Private getter `_effectiveLevel` falls back to `EnergyLevel.fromInt(energyLevel ?? 1)`.
|
||||
Also added `EnergyLevel.fromInt()` static factory (maps 1→low, 2→medium, 3→high).
|
||||
|
||||
```dart
|
||||
EnergyBadge(energyLevel: task.energyLevel) // new usage
|
||||
EnergyBadge(level: EnergyLevel.medium) // existing usage still works
|
||||
EnergyBadge.fromInt(2) // factory convenience
|
||||
```
|
||||
|
||||
### `lib/shared/widgets/streak_dots.dart`
|
||||
Added optional `streakDays: int?` parameter.
|
||||
`_generateDays(int streak)` computes a `List<DayStatus>` from today's weekday index and streak count.
|
||||
Both `days` and `streakCount` are now optional (default to empty/0 when `streakDays` is provided).
|
||||
|
||||
```dart
|
||||
StreakDots(streakDays: petState.streakDays) // new
|
||||
StreakDots(days: myList, streakCount: 3) // existing
|
||||
```
|
||||
|
||||
### `lib/shared/widgets/task_card.dart`
|
||||
Added optional `task: TaskEntity?` and `onComplete: VoidCallback?` parameters.
|
||||
Private getters (`_title`, `_energyLevel`, `_isDone`, `_isSelfCare`, `_isOverdue`, `_toggleCallback`) delegate to `task` fields when set, falling back to individual params.
|
||||
`_isOverdue` computes from `task.deadline` when a task is provided.
|
||||
|
||||
```dart
|
||||
TaskCard(task: entity, onComplete: () => ref.read(...).completeTask(id)) // new
|
||||
TaskCard(title: 'Buy milk', energyLevel: 1, isDone: false) // existing
|
||||
```
|
||||
|
||||
### `lib/shared/widgets/pet_bubble.dart`
|
||||
Removed local `PetType` enum (was duplicated here and in `domain/entities/pet_type.dart`).
|
||||
Now imports `PetType` from the domain layer — single source of truth.
|
||||
|
||||
### `lib/domain/entities/task_entity.dart`
|
||||
Added computed getter:
|
||||
```dart
|
||||
bool get isCompleted => status == TaskStatus.done;
|
||||
```
|
||||
Widgets and screens use this instead of manually comparing `status == TaskStatus.done`.
|
||||
|
||||
### `lib/features/tasks/providers/task_providers.dart`
|
||||
Added `uncompleteTask(int id)` method to `TasksNotifier` — sets status back to `pending`.
|
||||
Added top-level alias:
|
||||
```dart
|
||||
final todayTasksProvider = tasksProvider;
|
||||
```
|
||||
MVP does not filter by date; screens watch `todayTasksProvider` and split pending/completed themselves.
|
||||
|
||||
### `lib/features/tasks/providers/pet_providers.dart`
|
||||
Fixed `PetStateEntity` constructor: `id`/`userId` are `String` (`'1'`), `updatedAt` is required.
|
||||
Added `onTaskCompleted()` method as a public alias for `incrementTasksDone()`.
|
||||
|
||||
### `lib/features/tasks/screens/tasks_screen.dart`
|
||||
- Watches `userProvider` (for `petType`, `petName`) and `petMoodProvider` (for mood string).
|
||||
- `PetBubble` now receives `petName` from `userState` and `mood` from `petMoodProvider`.
|
||||
- `_EmptyState` now accepts `PetType` (enum) instead of `String`, uses `petType.emoji`.
|
||||
- Pet type resolved from `userState.petType`, not `petState.petType` (pet state doesn't own identity).
|
||||
|
||||
---
|
||||
|
||||
## Key Decisions
|
||||
|
||||
**Dual-param widget pattern**: Widgets accept both an entity param (`task: TaskEntity?`) and individual params (`title`, `energyLevel`, etc.). Private getters resolve which to use. This keeps backward compatibility for any callers passing raw values while enabling entity-driven usage from screens.
|
||||
|
||||
**`PetType` canonical location**: `domain/entities/pet_type.dart` is the single source. `PetBubble` used to redeclare it locally — that local copy is removed.
|
||||
|
||||
**`todayTasksProvider` is an alias**: Not a filtered provider. In MVP all tasks are "today" tasks. Screens handle the pending/completed split via `.where()`. If date-filtering is needed later, only this alias needs to change.
|
||||
|
||||
**Pet identity vs. pet state**: `petType` and `petName` belong to the `User` entity (`userProvider`), not `PetStateEntity`. `PetStateEntity` tracks energy/streak/rewards only.
|
||||
|
||||
---
|
||||
|
||||
## Tests Added
|
||||
|
||||
83 new tests across 6 files:
|
||||
|
||||
| File | Tests |
|
||||
|------|-------|
|
||||
| `test/domain/entities/task_entity_test.dart` | 12 |
|
||||
| `test/domain/entities/pet_type_test.dart` | 12 |
|
||||
| `test/domain/entities/pet_state_entity_test.dart` | 8 |
|
||||
| `test/features/tasks/providers/task_providers_test.dart` | 16 |
|
||||
| `test/features/tasks/providers/pet_providers_test.dart` | 24 |
|
||||
| `test/shared/widgets/energy_badge_test.dart` | 13 |
|
||||
|
||||
All 83 new tests pass. 2 pre-existing failures in `test/data/repositories/` require `build_runner` regeneration and are unrelated to this fix.
|
||||
@@ -28,6 +28,8 @@ class TaskEntity {
|
||||
required this.updatedAt,
|
||||
});
|
||||
|
||||
bool get isCompleted => status == TaskStatus.done;
|
||||
|
||||
TaskEntity copyWith({
|
||||
int? id,
|
||||
int? userId,
|
||||
|
||||
@@ -40,18 +40,21 @@ final userProvider =
|
||||
|
||||
class PetStateNotifier extends Notifier<PetStateEntity> {
|
||||
@override
|
||||
PetStateEntity build() => const PetStateEntity(
|
||||
id: 1,
|
||||
userId: 1,
|
||||
PetStateEntity build() => PetStateEntity(
|
||||
id: '1',
|
||||
userId: '1',
|
||||
energyToday: 2,
|
||||
streakDays: 3,
|
||||
totalTasksDone: 12,
|
||||
unlockedItems: 'masle,pelisek',
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
|
||||
void incrementTasksDone() {
|
||||
state = state.copyWith(totalTasksDone: state.totalTasksDone + 1);
|
||||
}
|
||||
|
||||
void onTaskCompleted() => incrementTasksDone();
|
||||
}
|
||||
|
||||
final petStateProvider =
|
||||
|
||||
@@ -21,6 +21,19 @@ class TasksNotifier extends Notifier<List<TaskEntity>> {
|
||||
];
|
||||
}
|
||||
|
||||
void uncompleteTask(int id) {
|
||||
state = [
|
||||
for (final task in state)
|
||||
if (task.id == id)
|
||||
task.copyWith(
|
||||
status: TaskStatus.pending,
|
||||
updatedAt: DateTime.now(),
|
||||
)
|
||||
else
|
||||
task,
|
||||
];
|
||||
}
|
||||
|
||||
void skipTask(int id) {
|
||||
final skipped = state.firstWhere((t) => t.id == id);
|
||||
state = [
|
||||
@@ -34,6 +47,10 @@ class TasksNotifier extends Notifier<List<TaskEntity>> {
|
||||
final tasksProvider =
|
||||
NotifierProvider<TasksNotifier, List<TaskEntity>>(TasksNotifier.new);
|
||||
|
||||
/// Alias for [tasksProvider]. Returns all tasks — no date filtering in MVP.
|
||||
/// Screens are responsible for splitting into pending/completed.
|
||||
final todayTasksProvider = tasksProvider;
|
||||
|
||||
/// Pending tasks only, ordered by sort order.
|
||||
final pendingTasksProvider = Provider<List<TaskEntity>>((ref) {
|
||||
final tasks = ref.watch(tasksProvider);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
/// Provider for current user name.
|
||||
final userNameProvider = Provider<String>((ref) => 'Zuf');
|
||||
import 'pet_providers.dart';
|
||||
|
||||
/// Derived provider for the current user's display name.
|
||||
final userNameProvider = Provider<String>((ref) => ref.watch(userProvider).name);
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
import '../../../core/router/routes.dart';
|
||||
import '../../../core/theme/kittie_colors.dart';
|
||||
import '../../../domain/entities/pet_type.dart';
|
||||
import '../../../shared/utils/czech_date.dart';
|
||||
import '../../../shared/widgets/energy_bar.dart';
|
||||
import '../../../shared/widgets/pet_bubble.dart';
|
||||
@@ -23,6 +24,8 @@ class TasksScreen extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final tasks = ref.watch(todayTasksProvider);
|
||||
final petState = ref.watch(petStateProvider);
|
||||
final userState = ref.watch(userProvider);
|
||||
final petMood = ref.watch(petMoodProvider);
|
||||
final userName = ref.watch(userNameProvider);
|
||||
|
||||
final pendingTasks = tasks.where((t) => !t.isCompleted).toList();
|
||||
@@ -32,7 +35,7 @@ class TasksScreen extends ConsumerWidget {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: tasks.isEmpty
|
||||
? _EmptyState(petType: petState.petType)
|
||||
? _EmptyState(petType: userState.petType)
|
||||
: SingleChildScrollView(
|
||||
padding: const EdgeInsets.fromLTRB(20, 16, 20, 100),
|
||||
child: Column(
|
||||
@@ -67,9 +70,10 @@ class TasksScreen extends ConsumerWidget {
|
||||
|
||||
// 4. Pet area
|
||||
PetBubble(
|
||||
petType: petState.petType,
|
||||
mood: petState.mood,
|
||||
message: _petMessage(petState.mood, petState.petName),
|
||||
petType: userState.petType,
|
||||
petName: userState.petName,
|
||||
mood: petMood.name,
|
||||
message: _petMessage(petMood.name, userState.petName),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
@@ -154,26 +158,21 @@ class TasksScreen extends ConsumerWidget {
|
||||
static String _petMessage(String mood, String petName) {
|
||||
return switch (mood) {
|
||||
'happy' => '$petName je stastna! Jde ti to skvele \u{1F44F}',
|
||||
'neutral' => '$petName te ceka! Co dnes zvladneme? \u{2728}',
|
||||
'sad' => '$petName te podporuje. Zkus jeden ukol \u{1F495}',
|
||||
'sleepy' => '$petName odpociva... Klid je taky dulezity \u{1F4A4}',
|
||||
_ => '$petName te ceka! Co dnes zvladneme? \u{2728}',
|
||||
_ => '$petName je tady pro tebe \u{1F60A}',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class _EmptyState extends StatelessWidget {
|
||||
final String petType;
|
||||
final PetType petType;
|
||||
|
||||
const _EmptyState({required this.petType});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final emoji = switch (petType) {
|
||||
'dog' => '\u{1F436}',
|
||||
'cat' => '\u{1F431}',
|
||||
'capybara' => '\u{1F9AB}',
|
||||
_ => '\u{1F431}',
|
||||
};
|
||||
final emoji = petType.emoji;
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
|
||||
@@ -27,14 +27,22 @@ enum EnergyLevel {
|
||||
/// - medium: orange bg, orange text, sun icon — "Stredni"
|
||||
/// - high: red bg, red text, fire icon — "Narocne"
|
||||
class EnergyBadge extends StatelessWidget {
|
||||
const EnergyBadge({super.key, required this.level});
|
||||
const EnergyBadge({super.key, this.level, this.energyLevel})
|
||||
: assert(level != null || energyLevel != null,
|
||||
'Provide either level or energyLevel');
|
||||
|
||||
final EnergyLevel level;
|
||||
final EnergyLevel? level;
|
||||
|
||||
/// Energy level as raw int (1–3); used when [level] is not provided.
|
||||
final int? energyLevel;
|
||||
|
||||
/// Convenience constructor from raw int (1/2/3).
|
||||
factory EnergyBadge.fromInt(int level) =>
|
||||
EnergyBadge(level: EnergyLevel.fromInt(level));
|
||||
|
||||
EnergyLevel get _effectiveLevel =>
|
||||
level ?? EnergyLevel.fromInt(energyLevel ?? 1);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final (bgColor, textColor, icon, label) = _props;
|
||||
@@ -61,7 +69,7 @@ class EnergyBadge extends StatelessWidget {
|
||||
}
|
||||
|
||||
(Color bg, Color text, IconData icon, String label) get _props =>
|
||||
switch (level) {
|
||||
switch (_effectiveLevel) {
|
||||
EnergyLevel.low => (
|
||||
KittieColors.greenLight,
|
||||
KittieColors.greenDark,
|
||||
|
||||
@@ -8,19 +8,30 @@ import '../../core/theme/kittie_typography.dart';
|
||||
///
|
||||
/// Shows a track with creamDark background and amber fill
|
||||
/// that animates to the current [fraction] (0.0–1.0).
|
||||
/// Alternatively, pass [energyLevel] (1–3) to compute the fraction.
|
||||
class EnergyBar extends StatelessWidget {
|
||||
const EnergyBar({
|
||||
super.key,
|
||||
required this.fraction,
|
||||
this.fraction,
|
||||
this.energyLevel,
|
||||
this.label = 'ENERGIE',
|
||||
}) : assert(fraction >= 0.0 && fraction <= 1.0);
|
||||
}) : assert(fraction != null || energyLevel != null,
|
||||
'Provide either fraction or energyLevel');
|
||||
|
||||
/// Fill amount from 0.0 (empty) to 1.0 (full).
|
||||
final double fraction;
|
||||
final double? fraction;
|
||||
|
||||
/// Energy level 1–3; used when [fraction] is not provided.
|
||||
final int? energyLevel;
|
||||
|
||||
/// Label displayed above the bar.
|
||||
final String label;
|
||||
|
||||
double get _effectiveFraction {
|
||||
if (energyLevel != null) return (energyLevel! / 3.0).clamp(0.0, 1.0);
|
||||
return (fraction ?? 0.0).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
@@ -52,7 +63,7 @@ class EnergyBar extends StatelessWidget {
|
||||
AnimatedContainer(
|
||||
duration: AppConstants.animationNormal,
|
||||
curve: Curves.easeInOut,
|
||||
width: constraints.maxWidth * fraction,
|
||||
width: constraints.maxWidth * _effectiveFraction,
|
||||
color: KittieColors.amber,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -3,16 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import '../../core/constants/app_constants.dart';
|
||||
import '../../core/theme/kittie_colors.dart';
|
||||
import '../../core/theme/kittie_typography.dart';
|
||||
|
||||
/// Pet type determines the displayed emoji.
|
||||
enum PetType {
|
||||
dog('\u{1F436}'),
|
||||
cat('\u{1F431}'),
|
||||
capybara('\u{1F9AB}');
|
||||
|
||||
const PetType(this.emoji);
|
||||
final String emoji;
|
||||
}
|
||||
import '../../domain/entities/pet_type.dart';
|
||||
|
||||
/// Pet area card with a speech bubble.
|
||||
///
|
||||
|
||||
@@ -17,18 +17,39 @@ enum DayStatus { done, today, future }
|
||||
class StreakDots extends StatelessWidget {
|
||||
const StreakDots({
|
||||
super.key,
|
||||
required this.days,
|
||||
required this.streakCount,
|
||||
}) : assert(days.length == 7, 'days must have exactly 7 entries');
|
||||
this.days,
|
||||
this.streakCount = 0,
|
||||
this.streakDays,
|
||||
});
|
||||
|
||||
/// Status for each day Mon–Sun.
|
||||
final List<DayStatus> days;
|
||||
/// Status for each day Mon–Sun. Computed from [streakDays] if omitted.
|
||||
final List<DayStatus>? days;
|
||||
|
||||
/// Current consecutive-day streak count.
|
||||
final int streakCount;
|
||||
|
||||
/// Streak as an integer; used to compute [days] and count when provided.
|
||||
final int? streakDays;
|
||||
|
||||
static const _dayLabels = ['Po', 'Ut', 'St', 'Ct', 'Pa', 'So', 'Ne'];
|
||||
|
||||
static List<DayStatus> _generateDays(int streak) {
|
||||
final todayIndex = DateTime.now().weekday - 1; // 0=Mon, 6=Sun
|
||||
return List.generate(7, (i) {
|
||||
if (i == todayIndex) return DayStatus.today;
|
||||
if (i < todayIndex) {
|
||||
final daysAgo = todayIndex - i;
|
||||
return daysAgo <= streak ? DayStatus.done : DayStatus.future;
|
||||
}
|
||||
return DayStatus.future;
|
||||
});
|
||||
}
|
||||
|
||||
List<DayStatus> get _effectiveDays =>
|
||||
streakDays != null ? _generateDays(streakDays!) : (days ?? List.filled(7, DayStatus.future));
|
||||
|
||||
int get _effectiveCount => streakDays ?? streakCount;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
@@ -41,7 +62,7 @@ class StreakDots extends StatelessWidget {
|
||||
const Text('\u{1F525}', style: TextStyle(fontSize: 16)),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'$streakCount',
|
||||
'$_effectiveCount',
|
||||
style: KittieTypography.labelLarge.copyWith(
|
||||
color: KittieColors.brown,
|
||||
),
|
||||
@@ -55,7 +76,7 @@ class StreakDots extends StatelessWidget {
|
||||
children: List.generate(7, (i) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(left: i > 0 ? 8 : 0),
|
||||
child: _DayDot(label: _dayLabels[i], status: days[i]),
|
||||
child: _DayDot(label: _dayLabels[i], status: _effectiveDays[i]),
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
|
||||
import '../../core/constants/app_constants.dart';
|
||||
import '../../core/theme/kittie_colors.dart';
|
||||
import '../../core/theme/kittie_typography.dart';
|
||||
import '../../domain/entities/task_entity.dart';
|
||||
import 'energy_badge.dart';
|
||||
|
||||
/// A task list item with circular checkbox, title, energy badge, and time.
|
||||
@@ -18,25 +19,40 @@ import 'energy_badge.dart';
|
||||
class TaskCard extends StatelessWidget {
|
||||
const TaskCard({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.energyLevel,
|
||||
this.task,
|
||||
this.title,
|
||||
this.energyLevel,
|
||||
this.isDone = false,
|
||||
this.isSelfCare = false,
|
||||
this.isOverdue = false,
|
||||
this.timeText,
|
||||
this.onComplete,
|
||||
this.onToggle,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final int energyLevel;
|
||||
/// Optional task entity; its fields take precedence over individual params.
|
||||
final TaskEntity? task;
|
||||
final String? title;
|
||||
final int? energyLevel;
|
||||
final bool isDone;
|
||||
final bool isSelfCare;
|
||||
final bool isOverdue;
|
||||
final String? timeText;
|
||||
final VoidCallback? onComplete;
|
||||
final VoidCallback? onToggle;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
String get _title => task?.title ?? title ?? '';
|
||||
int get _energyLevel => task?.energyLevel ?? energyLevel ?? 2;
|
||||
bool get _isDone => task?.isCompleted ?? isDone;
|
||||
bool get _isSelfCare => task?.isSelfCare ?? isSelfCare;
|
||||
bool get _isOverdue {
|
||||
if (task?.deadline != null) return task!.deadline!.isBefore(DateTime.now());
|
||||
return isOverdue;
|
||||
}
|
||||
VoidCallback? get _toggleCallback => onComplete ?? onToggle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
@@ -45,20 +61,20 @@ class TaskCard extends StatelessWidget {
|
||||
constraints: const BoxConstraints(minHeight: AppConstants.minTouchTarget),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: isOverdue
|
||||
color: _isOverdue
|
||||
? KittieColors.redLight
|
||||
: KittieColors.creamDark,
|
||||
borderRadius: BorderRadius.circular(AppConstants.cardBorderRadius),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
_Checkbox(isDone: isDone, onToggle: onToggle),
|
||||
_Checkbox(isDone: _isDone, onToggle: _toggleCallback),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(child: _Content(
|
||||
title: title,
|
||||
energyLevel: energyLevel,
|
||||
isDone: isDone,
|
||||
isSelfCare: isSelfCare,
|
||||
title: _title,
|
||||
energyLevel: _energyLevel,
|
||||
isDone: _isDone,
|
||||
isSelfCare: _isSelfCare,
|
||||
timeText: timeText,
|
||||
)),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user