chore: scroll activity dialog image with other content (#2860)

This commit is contained in:
ggurdin 2025-05-20 17:07:22 -04:00 committed by GitHub
parent a5539b4bea
commit 8d5429771c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 346 additions and 312 deletions

View file

@ -211,7 +211,7 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
}
Future<void> clearEdits() async {
_resetActivity();
await _resetActivity();
if (mounted) {
setState(() {
isEditing = false;

View file

@ -86,320 +86,350 @@ class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: _width,
child: widget.controller.avatar != null
? Image.memory(
widget.controller.avatar!,
fit: BoxFit.cover,
)
: widget.controller.updatedActivity.imageURL !=
null
? widget.controller.updatedActivity
.imageURL!
.startsWith("mxc")
? MxcImage(
uri: Uri.parse(
widget.controller.updatedActivity
.imageURL!,
),
width: _width,
height: 200,
cacheKey: widget.controller
.updatedActivity.bookmarkId,
fit: BoxFit.cover,
)
: CachedNetworkImage(
imageUrl: widget.controller
.updatedActivity.imageURL!,
fit: BoxFit.cover,
placeholder: (context, url) =>
const Center(
child:
CircularProgressIndicator(),
),
errorWidget:
(context, url, error) =>
const SizedBox(),
)
: null,
),
if (widget.controller.isEditing)
Positioned(
bottom: 8.0,
child: InkWell(
borderRadius: BorderRadius.circular(90),
onTap: widget.controller.selectAvatar,
child: const CircleAvatar(
radius: 24.0,
child: Icon(
Icons.add_a_photo_outlined,
size: 24.0,
),
),
),
),
],
),
Flexible(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
spacing: 8.0,
mainAxisSize: MainAxisSize.min,
children: [
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Icons.event_note_outlined,
child: TextFormField(
controller:
widget.controller.titleController,
decoration: InputDecoration(
labelText:
L10n.of(context).activityTitle,
),
maxLines: 2,
minLines: 1,
),
)
else
ActivitySuggestionCardRow(
icon: Icons.event_note_outlined,
child: Text(
widget.controller.updatedActivity.title,
style:
theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
maxLines: 6,
overflow: TextOverflow.ellipsis,
),
),
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Symbols.target,
child: TextFormField(
controller: widget.controller
.learningObjectivesController,
decoration: InputDecoration(
labelText: L10n.of(context)
.learningObjectiveLabel,
),
maxLines: 4,
minLines: 1,
),
)
else
ActivitySuggestionCardRow(
icon: Symbols.target,
child: Text(
widget.controller.updatedActivity
.learningObjective,
maxLines: 6,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodyLarge,
),
),
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Symbols.steps,
child: TextFormField(
controller: widget
.controller.instructionsController,
decoration: InputDecoration(
labelText:
L10n.of(context).instructions,
),
maxLines: 8,
minLines: 1,
),
)
else
ActivitySuggestionCardRow(
icon: Symbols.steps,
child: Text(
widget.controller.updatedActivity
.instructions,
maxLines: 8,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodyLarge,
),
),
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Icons.group_outlined,
child: TextFormField(
controller: widget
.controller.participantsController,
decoration: InputDecoration(
labelText: L10n.of(context).classRoster,
),
maxLines: 1,
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return null;
}
try {
final val = int.parse(value);
if (val <= 0) {
return L10n.of(context)
.pleaseEnterInt;
}
} catch (e) {
return L10n.of(context)
.pleaseEnterANumber;
}
return null;
},
),
)
else
ActivitySuggestionCardRow(
icon: Icons.group_outlined,
child: Text(
L10n.of(context).countParticipants(
widget.controller.updatedActivity.req
.numberOfParticipants,
),
style: theme.textTheme.bodyLarge,
),
),
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Symbols.dictionary,
child: ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 60.0,
),
child: SingleChildScrollView(
child: Wrap(
spacing: 4.0,
runSpacing: 4.0,
children: widget.controller.vocab
.mapIndexed(
(i, vocab) => Container(
padding: const EdgeInsets
.symmetric(
vertical: 4.0,
horizontal: 8.0,
),
decoration: BoxDecoration(
color: theme
.colorScheme.primary
.withAlpha(20),
borderRadius:
BorderRadius.circular(
24.0,
),
),
child: MouseRegion(
cursor: SystemMouseCursors
.click,
child: GestureDetector(
onTap: () => widget
child: Column(
spacing: 8.0,
mainAxisSize: MainAxisSize.min,
children: [
Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: _width,
child: widget.controller.avatar != null
? Image.memory(
widget.controller.avatar!,
fit: BoxFit.cover,
)
: widget.controller.updatedActivity
.imageURL !=
null
? widget.controller.updatedActivity
.imageURL!
.startsWith("mxc")
? MxcImage(
uri: Uri.parse(
widget
.controller
.removeVocab(i),
child: Row(
spacing: 4.0,
mainAxisSize:
MainAxisSize.min,
children: [
Text(vocab.lemma),
const Icon(
Icons.close,
size: 12.0,
),
],
),
.updatedActivity
.imageURL!,
),
),
),
)
.toList(),
),
),
),
)
else
ActivitySuggestionCardRow(
icon: Symbols.dictionary,
child: ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 60.0,
),
child: SingleChildScrollView(
child: Wrap(
spacing: 4.0,
runSpacing: 4.0,
children: widget.controller.vocab
.map(
(vocab) => Container(
padding: const EdgeInsets
.symmetric(
vertical: 4.0,
horizontal: 8.0,
),
decoration: BoxDecoration(
color: theme
.colorScheme.primary
.withAlpha(20),
borderRadius:
BorderRadius.circular(
24.0,
width: _width,
height: 200,
cacheKey: widget
.controller
.updatedActivity
.bookmarkId,
fit: BoxFit.cover,
)
: CachedNetworkImage(
imageUrl: widget
.controller
.updatedActivity
.imageURL!,
fit: BoxFit.cover,
placeholder:
(context, url) =>
const Center(
child:
CircularProgressIndicator(),
),
),
child: Text(
vocab.lemma,
style: theme
.textTheme.bodyMedium,
),
),
)
.toList(),
),
),
),
errorWidget: (
context,
url,
error,
) =>
const SizedBox(),
)
: null,
),
if (widget.controller.isEditing)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 4.0,
),
child: Row(
spacing: 4.0,
children: [
Expanded(
child: TextFormField(
controller: widget
.controller.vocabController,
decoration: InputDecoration(
hintText: L10n.of(context)
.addVocabulary,
),
maxLines: 1,
onFieldSubmitted: (_) =>
widget.controller.addVocab(),
if (widget.controller.isEditing)
Positioned(
bottom: 8.0,
child: InkWell(
borderRadius: BorderRadius.circular(90),
onTap: widget.controller.selectAvatar,
child: const CircleAvatar(
radius: 24.0,
child: Icon(
Icons.add_a_photo_outlined,
size: 24.0,
),
),
IconButton(
padding: const EdgeInsets.all(0.0),
constraints:
const BoxConstraints(), // override default min size of 48px
iconSize: 16.0,
icon: const Icon(Icons.add_outlined),
onPressed: widget.controller.addVocab,
),
],
),
),
),
],
),
],
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: Column(
children: [
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Icons.event_note_outlined,
child: TextFormField(
controller:
widget.controller.titleController,
decoration: InputDecoration(
labelText:
L10n.of(context).activityTitle,
),
maxLines: 2,
minLines: 1,
),
)
else
ActivitySuggestionCardRow(
icon: Icons.event_note_outlined,
child: Text(
widget
.controller.updatedActivity.title,
style: theme.textTheme.titleLarge
?.copyWith(
fontWeight: FontWeight.bold,
),
maxLines: 6,
overflow: TextOverflow.ellipsis,
),
),
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Symbols.target,
child: TextFormField(
controller: widget.controller
.learningObjectivesController,
decoration: InputDecoration(
labelText: L10n.of(context)
.learningObjectiveLabel,
),
maxLines: 4,
minLines: 1,
),
)
else
ActivitySuggestionCardRow(
icon: Symbols.target,
child: Text(
widget.controller.updatedActivity
.learningObjective,
maxLines: 6,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodyLarge,
),
),
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Symbols.steps,
child: TextFormField(
controller: widget.controller
.instructionsController,
decoration: InputDecoration(
labelText:
L10n.of(context).instructions,
),
maxLines: 8,
minLines: 1,
),
)
else
ActivitySuggestionCardRow(
icon: Symbols.steps,
child: Text(
widget.controller.updatedActivity
.instructions,
maxLines: 8,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodyLarge,
),
),
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Icons.group_outlined,
child: TextFormField(
controller: widget.controller
.participantsController,
decoration: InputDecoration(
labelText:
L10n.of(context).classRoster,
),
maxLines: 1,
keyboardType: TextInputType.number,
validator: (value) {
if (value == null ||
value.isEmpty) {
return null;
}
try {
final val = int.parse(value);
if (val <= 0) {
return L10n.of(context)
.pleaseEnterInt;
}
} catch (e) {
return L10n.of(context)
.pleaseEnterANumber;
}
return null;
},
),
)
else
ActivitySuggestionCardRow(
icon: Icons.group_outlined,
child: Text(
L10n.of(context).countParticipants(
widget.controller.updatedActivity
.req.numberOfParticipants,
),
style: theme.textTheme.bodyLarge,
),
),
if (widget.controller.isEditing)
ActivitySuggestionCardRow(
icon: Symbols.dictionary,
child: ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 60.0,
),
child: SingleChildScrollView(
child: Wrap(
spacing: 4.0,
runSpacing: 4.0,
children: widget.controller.vocab
.mapIndexed(
(i, vocab) => Container(
padding: const EdgeInsets
.symmetric(
vertical: 4.0,
horizontal: 8.0,
),
decoration: BoxDecoration(
color: theme
.colorScheme.primary
.withAlpha(20),
borderRadius:
BorderRadius
.circular(
24.0,
),
),
child: MouseRegion(
cursor:
SystemMouseCursors
.click,
child: GestureDetector(
onTap: () => widget
.controller
.removeVocab(i),
child: Row(
spacing: 4.0,
mainAxisSize:
MainAxisSize
.min,
children: [
Text(vocab.lemma),
const Icon(
Icons.close,
size: 12.0,
),
],
),
),
),
),
)
.toList(),
),
),
),
)
else
ActivitySuggestionCardRow(
icon: Symbols.dictionary,
child: ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 60.0,
),
child: SingleChildScrollView(
child: Wrap(
spacing: 4.0,
runSpacing: 4.0,
children: widget.controller.vocab
.map(
(vocab) => Container(
padding: const EdgeInsets
.symmetric(
vertical: 4.0,
horizontal: 8.0,
),
decoration: BoxDecoration(
color: theme
.colorScheme.primary
.withAlpha(20),
borderRadius:
BorderRadius
.circular(
24.0,
),
),
child: Text(
vocab.lemma,
style: theme.textTheme
.bodyMedium,
),
),
)
.toList(),
),
),
),
),
if (widget.controller.isEditing)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 4.0,
),
child: Row(
spacing: 4.0,
children: [
Expanded(
child: TextFormField(
controller: widget
.controller.vocabController,
decoration: InputDecoration(
hintText: L10n.of(context)
.addVocabulary,
),
maxLines: 1,
onFieldSubmitted: (_) => widget
.controller
.addVocab(),
),
),
IconButton(
padding:
const EdgeInsets.all(0.0),
constraints:
const BoxConstraints(), // override default min size of 48px
iconSize: 16.0,
icon: const Icon(
Icons.add_outlined,
),
onPressed:
widget.controller.addVocab,
),
],
),
),
],
),
),
],
),
),
),
@ -459,13 +489,17 @@ class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
),
),
if (widget.controller.isEditing)
GestureDetector(
child: const Icon(
Icons.close_outlined,
size: 16.0,
IconButton.filled(
style: IconButton.styleFrom(
backgroundColor: theme.colorScheme.primary,
),
onTap: () {
widget.controller.clearEdits();
padding: const EdgeInsets.all(6.0),
constraints:
const BoxConstraints(), // override default min size of 48px
iconSize: 24.0,
icon: const Icon(Icons.close_outlined),
onPressed: () async {
await widget.controller.clearEdits();
widget.controller.setEditing(false);
},
)