Skip to content

Commit

Permalink
feat(TodoList): Tags can now also occur inline of a todo, limit todo …
Browse files Browse the repository at this point in the history
…to two lines
  • Loading branch information
tmaegel committed Mar 18, 2024
1 parent b5f4171 commit 2b73fe3
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 89 deletions.
41 changes: 41 additions & 0 deletions lib/common_widgets/chip.dart
@@ -1,5 +1,46 @@
import 'package:flutter/material.dart';

class BasicIconChip extends StatelessWidget {
final String label;
final IconData iconData;
final bool mono;

const BasicIconChip({
required this.label,
required this.iconData,
this.mono = false,
super.key,
});

@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 0.0),
decoration: BoxDecoration(
color: mono
? Theme.of(context).colorScheme.surfaceVariant
: Theme.of(context).colorScheme.secondaryContainer,
borderRadius: BorderRadius.circular(4),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(iconData, size: 14.0),
const SizedBox(width: 2.0),
Text(
label,
style: TextStyle(
color: mono
? Theme.of(context).colorScheme.onSurfaceVariant
: Theme.of(context).colorScheme.onSecondaryContainer,
),
)
],
),
);
}
}

class BasicChip extends StatelessWidget {
final String label;
final bool mono;
Expand Down
88 changes: 60 additions & 28 deletions lib/presentation/todo/pages/todo_list_page.dart
Expand Up @@ -327,44 +327,76 @@ class TodoListTile extends StatelessWidget {
Widget build(BuildContext context) {
return ListTile(
key: key,
title: Text(
todo.fmtDescription,
style: TextStyle(
decoration: todo.completion ? TextDecoration.lineThrough : null,
decorationThickness: 5.0,
),
title: _buildTitle(context),
subtitle: Padding(
padding: const EdgeInsets.only(top: 2.0),
child: _buildSubtitle(),
),
subtitle: _buildSubtitle(),
onTap: () => context.pushNamed('todo-edit', extra: todo),
);
}

Widget? _buildSubtitle() {
final List<String> items = [
if (todo.priority != Priority.none) todo.priority.name,
for (String p in todo.fmtProjects) p,
for (String c in todo.fmtContexts) c,
for (String kv in todo.fmtKeyValues) kv,
]..removeWhere((value) => value.isEmpty);

if (items.isEmpty) {
return null;
}
Widget _buildTitle(BuildContext context) {
final List<String> items = todo.description.split(' ')
..removeWhere(
(String item) => item.startsWith('due:'),
);

List<String> shortenedItems;
if (items.length > 5) {
shortenedItems = items.sublist(0, 5);
shortenedItems.add('...');
} else {
shortenedItems = [...items];
}
return RichText(
text: TextSpan(
style: todo.completion
? Theme.of(context).textTheme.titleMedium?.copyWith(
decoration: TextDecoration.lineThrough,
decorationThickness: 4.0,
)
: Theme.of(context).textTheme.titleMedium,
text: '',
children: <TextSpan>[
for (int i = 0; i < items.length; i++)
TextSpan(
text: i == items.length - 1 ? items[i] : '${items[i]} ',
style: Todo.matchProject(items[i]) ||
Todo.matchContext(items[i]) ||
Todo.matchKeyValue(items[i])
? const TextStyle(fontWeight: FontWeight.bold)
: null),
],
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
);
}

Widget? _buildSubtitle() {
return Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 4.0, // gap between adjacent chips
runSpacing: 4.0, // gap between lines
spacing: 2.0, // gap between adjacent chips
runSpacing: 2.0, // gap between lines
children: <Widget>[
for (String attr in shortenedItems) BasicChip(label: attr, mono: true),
if (todo.priority != Priority.none)
BasicIconChip(
mono: true,
iconData: Icons.flag_outlined,
label: todo.priority.name,
),
if (todo.creationDate != null)
BasicIconChip(
mono: true,
iconData: Icons.edit_calendar,
label: Todo.differenceToToday(todo.creationDate!),
),
if (todo.completionDate != null && todo.completion)
BasicIconChip(
mono: true,
iconData: Icons.event_available,
label: Todo.differenceToToday(todo.completionDate!),
),
if (todo.dueDate != null)
BasicIconChip(
mono: true,
iconData: Icons.event,
label: Todo.date2Str(todo.dueDate!)!,
)
],
);
}
Expand Down
90 changes: 61 additions & 29 deletions lib/presentation/todo/pages/todo_search_page.dart
Expand Up @@ -153,45 +153,77 @@ class TodoSearchTile extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
key: key,
title: Text(
todo.fmtDescription,
style: TextStyle(
decoration: todo.completion ? TextDecoration.lineThrough : null,
decorationThickness: 2.0,
),
title: _buildTitle(context),
subtitle: Padding(
padding: const EdgeInsets.only(top: 2.0),
child: _buildSubtitle(),
),
subtitle: _buildSubtitle(),
onTap: () => _onTapAction(context),
),
);
}

Widget? _buildSubtitle() {
final List<String> items = [
if (todo.priority != Priority.none) todo.priority.name,
for (String p in todo.fmtProjects) p,
for (String c in todo.fmtContexts) c,
for (String kv in todo.fmtKeyValues) kv,
]..removeWhere((value) => value.isEmpty);

if (items.isEmpty) {
return null;
}

List<String> shortenedItems;
if (items.length > 5) {
shortenedItems = items.sublist(0, 5);
shortenedItems.add('...');
} else {
shortenedItems = [...items];
}
Widget _buildTitle(BuildContext context) {
final List<String> items = todo.description.split(' ')
..removeWhere(
(String item) => item.startsWith('due:'),
);

return RichText(
text: TextSpan(
style: todo.completion
? Theme.of(context).textTheme.titleMedium?.copyWith(
decoration: TextDecoration.lineThrough,
decorationThickness: 4.0,
)
: Theme.of(context).textTheme.titleMedium,
text: '',
children: <TextSpan>[
for (int i = 0; i < items.length; i++)
TextSpan(
text: i == items.length - 1 ? items[i] : '${items[i]} ',
style: Todo.matchProject(items[i]) ||
Todo.matchContext(items[i]) ||
Todo.matchKeyValue(items[i])
? const TextStyle(fontWeight: FontWeight.bold)
: null),
],
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
);
}

Widget? _buildSubtitle() {
return Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 4.0, // gap between adjacent chips
runSpacing: 4.0, // gap between lines
spacing: 2.0, // gap between adjacent chips
runSpacing: 2.0, // gap between lines
children: <Widget>[
for (String attr in shortenedItems) BasicChip(label: attr, mono: true),
if (todo.priority != Priority.none)
BasicIconChip(
mono: true,
iconData: Icons.flag_outlined,
label: todo.priority.name,
),
if (todo.creationDate != null)
BasicIconChip(
mono: true,
iconData: Icons.edit_calendar,
label: Todo.differenceToToday(todo.creationDate!),
),
if (todo.completionDate != null && todo.completion)
BasicIconChip(
mono: true,
iconData: Icons.event_available,
label: Todo.differenceToToday(todo.completionDate!),
),
if (todo.dueDate != null)
BasicIconChip(
mono: true,
iconData: Icons.event,
label: Todo.date2Str(todo.dueDate!)!,
)
],
);
}
Expand Down

0 comments on commit 2b73fe3

Please sign in to comment.