diff --git a/lib/screens/creators/poll/poll_list.dart b/lib/screens/creators/poll/poll_list.dart index 3ca736f..0c68d33 100644 --- a/lib/screens/creators/poll/poll_list.dart +++ b/lib/screens/creators/poll/poll_list.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/poll.dart'; import 'package:island/pods/network.dart'; +import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/poll/poll_feedback.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -70,7 +71,7 @@ class CreatorPollListScreen extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return Scaffold( + return AppScaffold( appBar: AppBar(title: const Text('Polls')), floatingActionButton: FloatingActionButton( onPressed: () => _createPoll(context), diff --git a/lib/screens/creators/webfeed/webfeed_edit.dart b/lib/screens/creators/webfeed/webfeed_edit.dart index f9c0d64..9f4a964 100644 --- a/lib/screens/creators/webfeed/webfeed_edit.dart +++ b/lib/screens/creators/webfeed/webfeed_edit.dart @@ -114,10 +114,11 @@ class WebFeedEditScreen extends HookConsumerWidget { return feedAsync.when( loading: - () => - const Scaffold(body: Center(child: CircularProgressIndicator())), + () => const AppScaffold( + body: Center(child: CircularProgressIndicator()), + ), error: - (error, stack) => Scaffold( + (error, stack) => AppScaffold( appBar: AppBar(title: const Text('Error')), body: Center(child: Text('Error: $error')), ), diff --git a/lib/screens/poll/poll_editor.dart b/lib/screens/poll/poll_editor.dart index f69ed89..6ff67a5 100644 --- a/lib/screens/poll/poll_editor.dart +++ b/lib/screens/poll/poll_editor.dart @@ -9,6 +9,7 @@ import 'package:gap/gap.dart'; import 'package:island/pods/network.dart'; import 'package:island/widgets/alert.dart'; import 'package:island/models/poll.dart'; +import 'package:island/widgets/app_scaffold.dart'; import 'package:uuid/uuid.dart'; class PollEditorState { @@ -413,7 +414,7 @@ class PollEditorScreen extends ConsumerWidget { }); } - return Scaffold( + return AppScaffold( appBar: AppBar( title: Text(model.id == null ? 'Create Poll' : 'Edit Poll'), actions: [ @@ -428,175 +429,175 @@ class PollEditorScreen extends ConsumerWidget { const Gap(8), ], ), - body: SafeArea( - child: Form( - key: ValueKey(model.id), - child: ListView( - padding: const EdgeInsets.all(16), - children: [ - TextFormField( - initialValue: model.title ?? '', - decoration: const InputDecoration( - labelText: 'Title', - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), - ), - textInputAction: TextInputAction.next, - maxLength: 256, - onChanged: notifier.setTitle, - onTapOutside: - (_) => FocusManager.instance.primaryFocus?.unfocus(), - validator: (v) { - if (v == null || v.trim().isEmpty) { - return 'Title is required'; - } - return null; - }, - ), - const Gap(12), - TextFormField( - initialValue: model.description ?? '', - decoration: const InputDecoration( - labelText: 'Description', - alignLabelWithHint: true, - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), - ), - maxLines: 3, - maxLength: 4096, - onChanged: notifier.setDescription, - onTapOutside: - (_) => FocusManager.instance.primaryFocus?.unfocus(), - ), - const Gap(12), - _EndDatePicker( - value: model.endedAt, - onChanged: notifier.setEndedAt, - ), - const Gap(24), - Row( + body: Column( + children: [ + Expanded( + child: Form( + key: ValueKey(model.id), + child: ListView( + padding: const EdgeInsets.all(16), children: [ - Text( - 'Questions', - style: Theme.of(context).textTheme.titleLarge, - ), - const Spacer(), - MenuAnchor( - builder: (context, controller, child) { - return FilledButton.icon( - onPressed: () { - controller.isOpen - ? controller.close() - : controller.open(); - }, - icon: const Icon(Icons.add), - label: const Text('Add question'), - ); + TextFormField( + initialValue: model.title ?? '', + decoration: const InputDecoration( + labelText: 'Title', + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(16)), + ), + ), + textInputAction: TextInputAction.next, + maxLength: 256, + onChanged: notifier.setTitle, + onTapOutside: + (_) => FocusManager.instance.primaryFocus?.unfocus(), + validator: (v) { + if (v == null || v.trim().isEmpty) { + return 'Title is required'; + } + return null; }, - menuChildren: - SnPollQuestionType.values - .map( - (t) => MenuItemButton( - leadingIcon: Icon(_iconForType(t)), - onPressed: () => notifier.addQuestion(t), - child: Text(_labelForType(t)), - ), - ) - .toList(), ), + const Gap(12), + TextFormField( + initialValue: model.description ?? '', + decoration: const InputDecoration( + labelText: 'Description', + alignLabelWithHint: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(16)), + ), + ), + maxLines: 3, + maxLength: 4096, + onChanged: notifier.setDescription, + onTapOutside: + (_) => FocusManager.instance.primaryFocus?.unfocus(), + ), + const Gap(12), + _EndDatePicker( + value: model.endedAt, + onChanged: notifier.setEndedAt, + ), + const Gap(24), + Row( + children: [ + Text( + 'Questions', + style: Theme.of(context).textTheme.titleLarge, + ), + const Spacer(), + MenuAnchor( + builder: (context, controller, child) { + return FilledButton.icon( + onPressed: () { + controller.isOpen + ? controller.close() + : controller.open(); + }, + icon: const Icon(Icons.add), + label: const Text('Add question'), + ); + }, + menuChildren: + SnPollQuestionType.values + .map( + (t) => MenuItemButton( + leadingIcon: Icon(_iconForType(t)), + onPressed: () => notifier.addQuestion(t), + child: Text(_labelForType(t)), + ), + ) + .toList(), + ), + ], + ), + const Gap(8), + if (model.questions.isEmpty) + _EmptyState( + title: 'No questions yet', + subtitle: + 'Use "Add question" to start building your poll.', + ) + else + ReorderableListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: model.questions.length, + onReorder: (oldIndex, newIndex) { + // Convert to stepwise moves using provided functions + if (newIndex > oldIndex) newIndex -= 1; + final steps = newIndex - oldIndex; + if (steps == 0) return; + if (steps > 0) { + for (int i = 0; i < steps; i++) { + notifier.moveQuestionDown(oldIndex + i); + } + } else { + for (int i = 0; i > steps; i--) { + notifier.moveQuestionUp(oldIndex + i); + } + } + }, + buildDefaultDragHandles: false, + itemBuilder: (context, index) { + final q = model.questions[index]; + return Card( + key: ValueKey('q_$index'), + margin: const EdgeInsets.symmetric(vertical: 8), + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + _QuestionHeader( + index: index, + question: q, + onMoveUp: + index > 0 + ? () => notifier.moveQuestionUp(index) + : null, + onMoveDown: + index < model.questions.length - 1 + ? () => notifier.moveQuestionDown(index) + : null, + onDelete: () => notifier.removeQuestion(index), + ), + const Divider(height: 1), + Padding( + padding: const EdgeInsets.all(16), + child: _QuestionEditor( + index: index, + question: q, + ), + ), + ], + ), + ); + }, + ), + const Gap(96), ], ), - const Gap(8), - if (model.questions.isEmpty) - _EmptyState( - title: 'No questions yet', - subtitle: 'Use "Add question" to start building your poll.', - ) - else - ReorderableListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: model.questions.length, - onReorder: (oldIndex, newIndex) { - // Convert to stepwise moves using provided functions - if (newIndex > oldIndex) newIndex -= 1; - final steps = newIndex - oldIndex; - if (steps == 0) return; - if (steps > 0) { - for (int i = 0; i < steps; i++) { - notifier.moveQuestionDown(oldIndex + i); - } - } else { - for (int i = 0; i > steps; i--) { - notifier.moveQuestionUp(oldIndex + i); - } - } - }, - buildDefaultDragHandles: false, - itemBuilder: (context, index) { - final q = model.questions[index]; - return Card( - key: ValueKey('q_$index'), - margin: const EdgeInsets.symmetric(vertical: 8), - clipBehavior: Clip.antiAlias, - child: Column( - children: [ - _QuestionHeader( - index: index, - question: q, - onMoveUp: - index > 0 - ? () => notifier.moveQuestionUp(index) - : null, - onMoveDown: - index < model.questions.length - 1 - ? () => notifier.moveQuestionDown(index) - : null, - onDelete: () => notifier.removeQuestion(index), - ), - const Divider(height: 1), - Padding( - padding: const EdgeInsets.all(16), - child: _QuestionEditor(index: index, question: q), - ), - ], - ), - ); - }, - ), - const Gap(96), + ), + ), + Row( + children: [ + OutlinedButton.icon( + onPressed: () { + Navigator.of(context).maybePop(); + }, + icon: const Icon(Icons.close), + label: const Text('Cancel'), + ), + const Spacer(), + FilledButton.icon( + onPressed: () { + _submitPoll(context, ref); + }, + icon: const Icon(Icons.cloud_upload_outlined), + label: Text(model.id == null ? 'Create' : 'Update'), + ), ], ), - ), - ), - bottomNavigationBar: Padding( - padding: EdgeInsets.fromLTRB( - 16, - 8, - 16, - 16 + MediaQuery.of(context).padding.bottom, - ), - child: Row( - children: [ - OutlinedButton.icon( - onPressed: () { - Navigator.of(context).maybePop(); - }, - icon: const Icon(Icons.close), - label: const Text('Cancel'), - ), - const Spacer(), - FilledButton.icon( - onPressed: () { - _submitPoll(context, ref); - }, - icon: const Icon(Icons.cloud_upload_outlined), - label: Text(model.id == null ? 'Create' : 'Update'), - ), - ], - ), + ], ), ); }