Refine views for mobile-first and responsive layouts
This commit is contained in:
+268
-104
@@ -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>[
|
||||
|
||||
Reference in New Issue
Block a user