Refine views for mobile-first and responsive layouts

This commit is contained in:
Rijad Zuzo
2026-02-15 17:20:52 +01:00
parent 4d21c6df82
commit 90c1a21d71
10 changed files with 1431 additions and 728 deletions
+268 -104
View File
@@ -21,15 +21,21 @@ class IdeasView extends ConsumerWidget {
for (final PersonProfile person in data.people) person.id: person,
};
return Padding(
padding: const EdgeInsets.fromLTRB(28, 24, 28, 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final bool compact = constraints.maxWidth < 760;
return Padding(
padding: EdgeInsets.fromLTRB(
compact ? 16 : 28,
compact ? 16 : 24,
compact ? 16 : 28,
compact ? 20 : 28,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Column(
if (compact)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
@@ -42,110 +48,268 @@ class IdeasView extends ConsumerWidget {
style: Theme.of(context).textTheme.bodyLarge
?.copyWith(color: AppTheme.textSecondary),
),
const SizedBox(height: 12),
FilledButton.icon(
onPressed: () => _addIdea(context, ref, data.people),
icon: const Icon(Icons.lightbulb_outline_rounded),
label: const Text('Add Idea'),
),
],
)
else
Row(
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Ideas',
style: Theme.of(
context,
).textTheme.headlineMedium,
),
const SizedBox(height: 6),
Text(
'Capture gift and event ideas before they slip away.',
style: Theme.of(context).textTheme.bodyLarge
?.copyWith(color: AppTheme.textSecondary),
),
],
),
),
FilledButton.icon(
onPressed: () => _addIdea(context, ref, data.people),
icon: const Icon(Icons.lightbulb_outline_rounded),
label: const Text('Add Idea'),
),
],
),
),
FilledButton.icon(
onPressed: () => _addIdea(context, ref, data.people),
icon: const Icon(Icons.lightbulb_outline_rounded),
label: const Text('Add Idea'),
),
],
),
const SizedBox(height: 16),
Expanded(
child: ListView.separated(
itemCount: ideas.length,
separatorBuilder: (_, _) => const SizedBox(height: 12),
itemBuilder: (BuildContext context, int index) {
final RelationshipIdea idea = ideas[index];
final PersonProfile? person = idea.personId == null
? null
: peopleById[idea.personId!];
return FrostedCard(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_IdeaTypeTag(type: idea.type),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
idea.title,
style: Theme.of(context).textTheme.titleMedium
?.copyWith(
decoration: idea.isArchived
? TextDecoration.lineThrough
: null,
const SizedBox(height: 16),
Expanded(
child: ListView.separated(
itemCount: ideas.length,
separatorBuilder: (_, _) => const SizedBox(height: 12),
itemBuilder: (BuildContext context, int index) {
final RelationshipIdea idea = ideas[index];
final PersonProfile? person = idea.personId == null
? null
: peopleById[idea.personId!];
return FrostedCard(
child: compact
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
_IdeaTypeTag(type: idea.type),
const SizedBox(width: 8),
Expanded(
child: Text(
idea.title,
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(
decoration: idea.isArchived
? TextDecoration
.lineThrough
: null,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
if (idea.details.trim().isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
idea.details,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: AppTheme.textSecondary,
),
),
),
),
if (idea.details.trim().isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 6),
child: Text(
idea.details,
const SizedBox(height: 8),
Text(
person == null
? 'Unassigned'
: person.name,
style: Theme.of(context)
.textTheme
.bodyMedium
.labelLarge
?.copyWith(
color: AppTheme.textSecondary,
),
),
),
const SizedBox(height: 8),
Text(
person == null ? 'Unassigned' : person.name,
style: Theme.of(context).textTheme.labelLarge
?.copyWith(color: AppTheme.textSecondary),
const SizedBox(height: 8),
Wrap(
spacing: 6,
runSpacing: 6,
children: <Widget>[
OutlinedButton.icon(
onPressed: () {
ref
.read(
localRepositoryProvider
.notifier,
)
.toggleIdeaArchived(idea.id);
},
icon: Icon(
idea.isArchived
? Icons.unarchive_outlined
: Icons.archive_outlined,
),
label: Text(
idea.isArchived
? 'Unarchive'
: 'Archive',
),
),
OutlinedButton.icon(
onPressed: () => _editIdea(
context,
ref,
data.people,
idea,
),
icon: const Icon(Icons.edit_rounded),
label: const Text('Edit'),
),
TextButton.icon(
onPressed: () {
ref
.read(
localRepositoryProvider
.notifier,
)
.deleteIdea(idea.id);
},
icon: const Icon(
Icons.delete_outline_rounded,
),
label: const Text('Delete'),
),
],
),
],
)
: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_IdeaTypeTag(type: idea.type),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
idea.title,
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(
decoration: idea.isArchived
? TextDecoration
.lineThrough
: null,
),
),
if (idea.details.trim().isNotEmpty)
Padding(
padding: const EdgeInsets.only(
top: 6,
),
child: Text(
idea.details,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: AppTheme
.textSecondary,
),
),
),
const SizedBox(height: 8),
Text(
person == null
? 'Unassigned'
: person.name,
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(
color: AppTheme.textSecondary,
),
),
],
),
),
const SizedBox(width: 8),
Column(
children: <Widget>[
IconButton(
onPressed: () {
ref
.read(
localRepositoryProvider
.notifier,
)
.toggleIdeaArchived(idea.id);
},
icon: Icon(
idea.isArchived
? Icons.unarchive_outlined
: Icons.archive_outlined,
),
tooltip: idea.isArchived
? 'Unarchive'
: 'Archive',
),
IconButton(
onPressed: () => _editIdea(
context,
ref,
data.people,
idea,
),
icon: const Icon(Icons.edit_rounded),
tooltip: 'Edit',
),
IconButton(
onPressed: () {
ref
.read(
localRepositoryProvider
.notifier,
)
.deleteIdea(idea.id);
},
icon: const Icon(
Icons.delete_outline_rounded,
),
tooltip: 'Delete',
),
],
),
],
),
],
),
),
const SizedBox(width: 8),
Column(
children: <Widget>[
IconButton(
onPressed: () {
ref
.read(localRepositoryProvider.notifier)
.toggleIdeaArchived(idea.id);
},
icon: Icon(
idea.isArchived
? Icons.unarchive_outlined
: Icons.archive_outlined,
),
tooltip: idea.isArchived
? 'Unarchive'
: 'Archive',
),
IconButton(
onPressed: () =>
_editIdea(context, ref, data.people, idea),
icon: const Icon(Icons.edit_rounded),
tooltip: 'Edit',
),
IconButton(
onPressed: () {
ref
.read(localRepositoryProvider.notifier)
.deleteIdea(idea.id);
},
icon: const Icon(Icons.delete_outline_rounded),
tooltip: 'Delete',
),
],
),
],
),
);
},
),
);
},
),
),
],
),
],
),
);
},
);
},
loading: () => const Center(child: CircularProgressIndicator()),
@@ -293,9 +457,9 @@ class _IdeaEditorDialogState extends State<_IdeaEditorDialog> {
Widget build(BuildContext context) {
return AlertDialog(
title: Text(widget.title),
content: SizedBox(
width: 430,
child: SingleChildScrollView(
content: SingleChildScrollView(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 430),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[