+685
-27
lib/screens/home/create_post_screen.dart
+685
-27
lib/screens/home/create_post_screen.dart
···
1
1
import 'package:flutter/material.dart';
2
+
import 'package:provider/provider.dart';
2
3
3
4
import '../../constants/app_colors.dart';
5
+
import '../../models/community.dart';
6
+
import '../../models/post.dart';
7
+
import '../../providers/auth_provider.dart';
8
+
import '../../services/api_exceptions.dart';
9
+
import '../../services/coves_api_service.dart';
10
+
import '../compose/community_picker_screen.dart';
11
+
import 'post_detail_screen.dart';
4
12
5
-
class CreatePostScreen extends StatelessWidget {
6
-
const CreatePostScreen({super.key});
13
+
/// Language options for posts
14
+
const Map<String, String> languages = {
15
+
'en': 'English',
16
+
'es': 'Spanish',
17
+
'pt': 'Portuguese',
18
+
'de': 'German',
19
+
'fr': 'French',
20
+
'ja': 'Japanese',
21
+
'ko': 'Korean',
22
+
'zh': 'Chinese',
23
+
};
24
+
25
+
/// Content limits from backend lexicon (social.coves.community.post)
26
+
/// Using grapheme limits as they are the user-facing character counts
27
+
const int kTitleMaxLength = 300;
28
+
const int kContentMaxLength = 10000;
29
+
30
+
/// Create Post Screen
31
+
///
32
+
/// Full-screen interface for creating a new post in a community.
33
+
///
34
+
/// Features:
35
+
/// - Community selector (required)
36
+
/// - Optional title, URL, thumbnail, and body fields
37
+
/// - Language dropdown and NSFW toggle
38
+
/// - Form validation (at least one of title/body/URL required)
39
+
/// - Loading states and error handling
40
+
/// - Keyboard handling with scroll support
41
+
class CreatePostScreen extends StatefulWidget {
42
+
const CreatePostScreen({this.onNavigateToFeed, super.key});
43
+
44
+
/// Callback to navigate to feed tab (used when in tab navigation)
45
+
final VoidCallback? onNavigateToFeed;
46
+
47
+
@override
48
+
State<CreatePostScreen> createState() => _CreatePostScreenState();
49
+
}
50
+
51
+
class _CreatePostScreenState extends State<CreatePostScreen>
52
+
with WidgetsBindingObserver {
53
+
// Text controllers
54
+
final TextEditingController _titleController = TextEditingController();
55
+
final TextEditingController _urlController = TextEditingController();
56
+
final TextEditingController _thumbnailController = TextEditingController();
57
+
final TextEditingController _bodyController = TextEditingController();
58
+
59
+
// Scroll and focus
60
+
final ScrollController _scrollController = ScrollController();
61
+
final FocusNode _titleFocusNode = FocusNode();
62
+
final FocusNode _urlFocusNode = FocusNode();
63
+
final FocusNode _thumbnailFocusNode = FocusNode();
64
+
final FocusNode _bodyFocusNode = FocusNode();
65
+
double _lastKeyboardHeight = 0;
66
+
67
+
// Form state
68
+
CommunityView? _selectedCommunity;
69
+
String _language = 'en';
70
+
bool _isNsfw = false;
71
+
bool _isSubmitting = false;
72
+
73
+
// Computed state
74
+
bool get _isFormValid {
75
+
return _selectedCommunity != null &&
76
+
(_titleController.text.trim().isNotEmpty ||
77
+
_bodyController.text.trim().isNotEmpty ||
78
+
_urlController.text.trim().isNotEmpty);
79
+
}
80
+
81
+
@override
82
+
void initState() {
83
+
super.initState();
84
+
WidgetsBinding.instance.addObserver(this);
85
+
// Listen to text changes to update button state
86
+
_titleController.addListener(_onTextChanged);
87
+
_urlController.addListener(_onTextChanged);
88
+
_bodyController.addListener(_onTextChanged);
89
+
}
90
+
91
+
@override
92
+
void dispose() {
93
+
WidgetsBinding.instance.removeObserver(this);
94
+
_titleController.dispose();
95
+
_urlController.dispose();
96
+
_thumbnailController.dispose();
97
+
_bodyController.dispose();
98
+
_scrollController.dispose();
99
+
_titleFocusNode.dispose();
100
+
_urlFocusNode.dispose();
101
+
_thumbnailFocusNode.dispose();
102
+
_bodyFocusNode.dispose();
103
+
super.dispose();
104
+
}
105
+
106
+
@override
107
+
void didChangeMetrics() {
108
+
super.didChangeMetrics();
109
+
if (!mounted) {
110
+
return;
111
+
}
112
+
113
+
final keyboardHeight = View.of(context).viewInsets.bottom;
114
+
115
+
// Detect keyboard closing and unfocus all text fields
116
+
if (_lastKeyboardHeight > 0 && keyboardHeight == 0) {
117
+
FocusManager.instance.primaryFocus?.unfocus();
118
+
}
119
+
120
+
_lastKeyboardHeight = keyboardHeight;
121
+
}
122
+
123
+
void _onTextChanged() {
124
+
// Force rebuild to update Post button state
125
+
setState(() {});
126
+
}
127
+
128
+
Future<void> _selectCommunity() async {
129
+
final result = await Navigator.push<CommunityView>(
130
+
context,
131
+
MaterialPageRoute(
132
+
builder: (context) => const CommunityPickerScreen(),
133
+
),
134
+
);
135
+
136
+
if (result != null && mounted) {
137
+
setState(() {
138
+
_selectedCommunity = result;
139
+
});
140
+
}
141
+
}
142
+
143
+
Future<void> _handleSubmit() async {
144
+
if (!_isFormValid || _isSubmitting) {
145
+
return;
146
+
}
147
+
148
+
setState(() {
149
+
_isSubmitting = true;
150
+
});
151
+
152
+
try {
153
+
final authProvider = context.read<AuthProvider>();
154
+
155
+
// Create API service with auth
156
+
final apiService = CovesApiService(
157
+
tokenGetter: authProvider.getAccessToken,
158
+
tokenRefresher: authProvider.refreshToken,
159
+
signOutHandler: authProvider.signOut,
160
+
);
161
+
162
+
// Build embed if URL is provided
163
+
ExternalEmbedInput? embed;
164
+
final url = _urlController.text.trim();
165
+
if (url.isNotEmpty) {
166
+
// Validate URL
167
+
final uri = Uri.tryParse(url);
168
+
if (uri == null ||
169
+
!uri.hasScheme ||
170
+
(!uri.scheme.startsWith('http'))) {
171
+
if (mounted) {
172
+
ScaffoldMessenger.of(context).showSnackBar(
173
+
SnackBar(
174
+
content: const Text('Please enter a valid URL (http or https)'),
175
+
backgroundColor: Colors.red[700],
176
+
behavior: SnackBarBehavior.floating,
177
+
),
178
+
);
179
+
}
180
+
setState(() {
181
+
_isSubmitting = false;
182
+
});
183
+
return;
184
+
}
185
+
186
+
embed = ExternalEmbedInput(
187
+
uri: url,
188
+
title: _titleController.text.trim().isNotEmpty
189
+
? _titleController.text.trim()
190
+
: null,
191
+
thumb: _thumbnailController.text.trim().isNotEmpty
192
+
? _thumbnailController.text.trim()
193
+
: null,
194
+
);
195
+
}
196
+
197
+
// Build labels if NSFW is enabled
198
+
SelfLabels? labels;
199
+
if (_isNsfw) {
200
+
labels = const SelfLabels(values: [SelfLabel(val: 'nsfw')]);
201
+
}
202
+
203
+
// Create post
204
+
final response = await apiService.createPost(
205
+
community: _selectedCommunity!.did,
206
+
title: _titleController.text.trim().isNotEmpty
207
+
? _titleController.text.trim()
208
+
: null,
209
+
content: _bodyController.text.trim().isNotEmpty
210
+
? _bodyController.text.trim()
211
+
: null,
212
+
embed: embed,
213
+
langs: [_language],
214
+
labels: labels,
215
+
);
216
+
217
+
if (mounted) {
218
+
// Build optimistic post for immediate display
219
+
final optimisticPost = _buildOptimisticPost(
220
+
response: response,
221
+
authProvider: authProvider,
222
+
);
223
+
224
+
// Reset form first
225
+
_resetForm();
226
+
227
+
// Navigate to post detail with optimistic data
228
+
await Navigator.push(
229
+
context,
230
+
MaterialPageRoute(
231
+
builder: (context) => PostDetailScreen(
232
+
post: optimisticPost,
233
+
isOptimistic: true,
234
+
),
235
+
),
236
+
);
237
+
}
238
+
} on ApiException catch (e) {
239
+
if (mounted) {
240
+
ScaffoldMessenger.of(context).showSnackBar(
241
+
SnackBar(
242
+
content: Text('Failed to create post: ${e.message}'),
243
+
backgroundColor: Colors.red[700],
244
+
behavior: SnackBarBehavior.floating,
245
+
),
246
+
);
247
+
}
248
+
} on Exception catch (e) {
249
+
if (mounted) {
250
+
ScaffoldMessenger.of(context).showSnackBar(
251
+
SnackBar(
252
+
content: Text('Failed to create post: ${e.toString()}'),
253
+
backgroundColor: Colors.red[700],
254
+
behavior: SnackBarBehavior.floating,
255
+
),
256
+
);
257
+
}
258
+
} finally {
259
+
if (mounted) {
260
+
setState(() {
261
+
_isSubmitting = false;
262
+
});
263
+
}
264
+
}
265
+
}
266
+
267
+
void _resetForm() {
268
+
setState(() {
269
+
_titleController.clear();
270
+
_urlController.clear();
271
+
_thumbnailController.clear();
272
+
_bodyController.clear();
273
+
_selectedCommunity = null;
274
+
_language = 'en';
275
+
_isNsfw = false;
276
+
});
277
+
}
278
+
279
+
/// Build optimistic post for immediate display after creation
280
+
FeedViewPost _buildOptimisticPost({
281
+
required CreatePostResponse response,
282
+
required AuthProvider authProvider,
283
+
}) {
284
+
// Extract rkey from AT-URI (at://did/collection/rkey)
285
+
final uriParts = response.uri.split('/');
286
+
final rkey = uriParts.isNotEmpty ? uriParts.last : '';
287
+
288
+
// Build embed if URL was provided
289
+
PostEmbed? embed;
290
+
final url = _urlController.text.trim();
291
+
if (url.isNotEmpty) {
292
+
embed = PostEmbed(
293
+
type: 'social.coves.embed.external',
294
+
external: ExternalEmbed(
295
+
uri: url,
296
+
title: _titleController.text.trim().isNotEmpty
297
+
? _titleController.text.trim()
298
+
: null,
299
+
thumb: _thumbnailController.text.trim().isNotEmpty
300
+
? _thumbnailController.text.trim()
301
+
: null,
302
+
),
303
+
data: {
304
+
r'$type': 'social.coves.embed.external',
305
+
'external': {
306
+
'uri': url,
307
+
if (_titleController.text.trim().isNotEmpty)
308
+
'title': _titleController.text.trim(),
309
+
if (_thumbnailController.text.trim().isNotEmpty)
310
+
'thumb': _thumbnailController.text.trim(),
311
+
},
312
+
},
313
+
);
314
+
}
315
+
316
+
final now = DateTime.now();
317
+
318
+
return FeedViewPost(
319
+
post: PostView(
320
+
uri: response.uri,
321
+
cid: response.cid,
322
+
rkey: rkey,
323
+
author: AuthorView(
324
+
did: authProvider.did ?? '',
325
+
handle: authProvider.handle ?? 'unknown',
326
+
displayName: null,
327
+
avatar: null,
328
+
),
329
+
community: CommunityRef(
330
+
did: _selectedCommunity!.did,
331
+
name: _selectedCommunity!.name,
332
+
handle: _selectedCommunity!.handle,
333
+
avatar: _selectedCommunity!.avatar,
334
+
),
335
+
createdAt: now,
336
+
indexedAt: now,
337
+
text: _bodyController.text.trim(),
338
+
title: _titleController.text.trim().isNotEmpty
339
+
? _titleController.text.trim()
340
+
: null,
341
+
stats: PostStats(
342
+
upvotes: 0,
343
+
downvotes: 0,
344
+
score: 0,
345
+
commentCount: 0,
346
+
),
347
+
embed: embed,
348
+
viewer: ViewerState(),
349
+
),
350
+
);
351
+
}
7
352
8
353
@override
9
354
Widget build(BuildContext context) {
10
-
return Scaffold(
11
-
backgroundColor: const Color(0xFF0B0F14),
12
-
appBar: AppBar(
13
-
backgroundColor: const Color(0xFF0B0F14),
14
-
foregroundColor: Colors.white,
355
+
final authProvider = context.watch<AuthProvider>();
356
+
final userHandle = authProvider.handle ?? 'Unknown';
357
+
358
+
return PopScope(
359
+
canPop: widget.onNavigateToFeed == null,
360
+
onPopInvokedWithResult: (didPop, result) {
361
+
if (!didPop && widget.onNavigateToFeed != null) {
362
+
widget.onNavigateToFeed!();
363
+
}
364
+
},
365
+
child: Scaffold(
366
+
backgroundColor: AppColors.background,
367
+
appBar: AppBar(
368
+
backgroundColor: AppColors.background,
369
+
surfaceTintColor: Colors.transparent,
370
+
foregroundColor: AppColors.textPrimary,
15
371
title: const Text('Create Post'),
372
+
elevation: 0,
16
373
automaticallyImplyLeading: false,
374
+
leading: IconButton(
375
+
icon: const Icon(Icons.close),
376
+
onPressed: () {
377
+
// Use callback if available (tab navigation), otherwise pop
378
+
if (widget.onNavigateToFeed != null) {
379
+
widget.onNavigateToFeed!();
380
+
} else {
381
+
Navigator.pop(context);
382
+
}
383
+
},
384
+
),
385
+
actions: [
386
+
Padding(
387
+
padding: const EdgeInsets.only(right: 8),
388
+
child: TextButton(
389
+
onPressed: _isFormValid && !_isSubmitting ? _handleSubmit : null,
390
+
style: TextButton.styleFrom(
391
+
backgroundColor: _isFormValid && !_isSubmitting
392
+
? AppColors.primary
393
+
: AppColors.textSecondary.withValues(alpha: 0.3),
394
+
foregroundColor: AppColors.textPrimary,
395
+
padding: const EdgeInsets.symmetric(
396
+
horizontal: 16,
397
+
vertical: 8,
398
+
),
399
+
shape: RoundedRectangleBorder(
400
+
borderRadius: BorderRadius.circular(20),
401
+
),
402
+
),
403
+
child:
404
+
_isSubmitting
405
+
? const SizedBox(
406
+
width: 16,
407
+
height: 16,
408
+
child: CircularProgressIndicator(
409
+
strokeWidth: 2,
410
+
valueColor: AlwaysStoppedAnimation<Color>(
411
+
AppColors.textPrimary,
412
+
),
413
+
),
414
+
)
415
+
: const Text('Post'),
416
+
),
417
+
),
418
+
],
17
419
),
18
-
body: const Center(
19
-
child: Padding(
20
-
padding: EdgeInsets.all(24),
420
+
body: SafeArea(
421
+
child: SingleChildScrollView(
422
+
controller: _scrollController,
423
+
padding: const EdgeInsets.all(16),
21
424
child: Column(
22
-
mainAxisAlignment: MainAxisAlignment.center,
425
+
crossAxisAlignment: CrossAxisAlignment.stretch,
23
426
children: [
24
-
Icon(
25
-
Icons.add_circle_outline,
26
-
size: 64,
27
-
color: AppColors.primary,
427
+
// Community selector
428
+
_buildCommunitySelector(),
429
+
430
+
const SizedBox(height: 16),
431
+
432
+
// User info row
433
+
_buildUserInfo(userHandle),
434
+
435
+
const SizedBox(height: 24),
436
+
437
+
// Title field
438
+
_buildTextField(
439
+
controller: _titleController,
440
+
focusNode: _titleFocusNode,
441
+
hintText: 'Title',
442
+
maxLines: 1,
443
+
maxLength: kTitleMaxLength,
444
+
),
445
+
446
+
const SizedBox(height: 16),
447
+
448
+
// URL field
449
+
_buildTextField(
450
+
controller: _urlController,
451
+
focusNode: _urlFocusNode,
452
+
hintText: 'URL',
453
+
maxLines: 1,
454
+
keyboardType: TextInputType.url,
28
455
),
29
-
SizedBox(height: 24),
30
-
Text(
31
-
'Create Post',
32
-
style: TextStyle(
33
-
fontSize: 28,
34
-
color: Colors.white,
35
-
fontWeight: FontWeight.bold,
456
+
457
+
// Thumbnail field (only visible when URL is filled)
458
+
if (_urlController.text.trim().isNotEmpty) ...[
459
+
const SizedBox(height: 16),
460
+
_buildTextField(
461
+
controller: _thumbnailController,
462
+
focusNode: _thumbnailFocusNode,
463
+
hintText: 'Thumbnail URL',
464
+
maxLines: 1,
465
+
keyboardType: TextInputType.url,
36
466
),
467
+
],
468
+
469
+
const SizedBox(height: 16),
470
+
471
+
// Body field (multiline)
472
+
_buildTextField(
473
+
controller: _bodyController,
474
+
focusNode: _bodyFocusNode,
475
+
hintText: 'What are your thoughts?',
476
+
minLines: 8,
477
+
maxLines: null,
478
+
maxLength: kContentMaxLength,
37
479
),
38
-
SizedBox(height: 16),
39
-
Text(
40
-
'Share your thoughts with the community',
41
-
style: TextStyle(fontSize: 16, color: Color(0xFFB6C2D2)),
42
-
textAlign: TextAlign.center,
480
+
481
+
const SizedBox(height: 24),
482
+
483
+
// Language dropdown and NSFW toggle
484
+
Row(
485
+
children: [
486
+
// Language dropdown
487
+
Expanded(
488
+
child: _buildLanguageDropdown(),
489
+
),
490
+
491
+
const SizedBox(width: 16),
492
+
493
+
// NSFW toggle
494
+
Expanded(
495
+
child: _buildNsfwToggle(),
496
+
),
497
+
],
43
498
),
499
+
500
+
const SizedBox(height: 24),
44
501
],
45
502
),
46
503
),
504
+
),
505
+
),
506
+
);
507
+
}
508
+
509
+
Widget _buildCommunitySelector() {
510
+
return Material(
511
+
color: Colors.transparent,
512
+
child: InkWell(
513
+
onTap: _selectCommunity,
514
+
borderRadius: BorderRadius.circular(12),
515
+
child: Container(
516
+
padding: const EdgeInsets.all(16),
517
+
decoration: BoxDecoration(
518
+
color: AppColors.backgroundSecondary,
519
+
border: Border.all(color: AppColors.border),
520
+
borderRadius: BorderRadius.circular(12),
521
+
),
522
+
child: Row(
523
+
children: [
524
+
const Icon(
525
+
Icons.workspaces_outlined,
526
+
color: AppColors.textSecondary,
527
+
size: 20,
528
+
),
529
+
const SizedBox(width: 12),
530
+
Expanded(
531
+
child: Text(
532
+
_selectedCommunity?.displayName ??
533
+
_selectedCommunity?.name ??
534
+
'Select a community',
535
+
style:
536
+
TextStyle(
537
+
color:
538
+
_selectedCommunity != null
539
+
? AppColors.textPrimary
540
+
: AppColors.textSecondary,
541
+
fontSize: 16,
542
+
),
543
+
maxLines: 1,
544
+
overflow: TextOverflow.ellipsis,
545
+
),
546
+
),
547
+
const Icon(
548
+
Icons.chevron_right,
549
+
color: AppColors.textSecondary,
550
+
size: 20,
551
+
),
552
+
],
553
+
),
554
+
),
555
+
),
556
+
);
557
+
}
558
+
559
+
Widget _buildUserInfo(String handle) {
560
+
return Row(
561
+
children: [
562
+
const Icon(
563
+
Icons.person,
564
+
color: AppColors.textSecondary,
565
+
size: 16,
566
+
),
567
+
const SizedBox(width: 8),
568
+
Text(
569
+
'@$handle',
570
+
style: const TextStyle(
571
+
color: AppColors.textSecondary,
572
+
fontSize: 14,
573
+
),
574
+
),
575
+
],
576
+
);
577
+
}
578
+
579
+
Widget _buildTextField({
580
+
required TextEditingController controller,
581
+
required String hintText,
582
+
FocusNode? focusNode,
583
+
int? maxLines,
584
+
int? minLines,
585
+
int? maxLength,
586
+
TextInputType? keyboardType,
587
+
TextInputAction? textInputAction,
588
+
}) {
589
+
// For multiline fields, use newline action and multiline keyboard
590
+
final isMultiline = minLines != null && minLines > 1;
591
+
final effectiveKeyboardType =
592
+
keyboardType ?? (isMultiline ? TextInputType.multiline : TextInputType.text);
593
+
final effectiveTextInputAction =
594
+
textInputAction ?? (isMultiline ? TextInputAction.newline : TextInputAction.next);
595
+
596
+
return TextField(
597
+
controller: controller,
598
+
focusNode: focusNode,
599
+
maxLines: maxLines,
600
+
minLines: minLines,
601
+
maxLength: maxLength,
602
+
keyboardType: effectiveKeyboardType,
603
+
textInputAction: effectiveTextInputAction,
604
+
textCapitalization: TextCapitalization.sentences,
605
+
style: const TextStyle(
606
+
color: AppColors.textPrimary,
607
+
fontSize: 16,
608
+
),
609
+
decoration: InputDecoration(
610
+
hintText: hintText,
611
+
hintStyle: const TextStyle(color: Color(0xFF5A6B7F)),
612
+
filled: true,
613
+
fillColor: const Color(0xFF1A2028),
614
+
counterStyle: const TextStyle(color: AppColors.textSecondary),
615
+
border: OutlineInputBorder(
616
+
borderRadius: BorderRadius.circular(12),
617
+
borderSide: const BorderSide(color: Color(0xFF2A3441)),
618
+
),
619
+
enabledBorder: OutlineInputBorder(
620
+
borderRadius: BorderRadius.circular(12),
621
+
borderSide: const BorderSide(color: Color(0xFF2A3441)),
622
+
),
623
+
focusedBorder: OutlineInputBorder(
624
+
borderRadius: BorderRadius.circular(12),
625
+
borderSide: const BorderSide(
626
+
color: AppColors.primary,
627
+
width: 2,
628
+
),
629
+
),
630
+
contentPadding: const EdgeInsets.all(16),
631
+
),
632
+
);
633
+
}
634
+
635
+
Widget _buildLanguageDropdown() {
636
+
return Container(
637
+
padding: const EdgeInsets.symmetric(horizontal: 12),
638
+
decoration: BoxDecoration(
639
+
color: AppColors.backgroundSecondary,
640
+
border: Border.all(color: AppColors.border),
641
+
borderRadius: BorderRadius.circular(12),
642
+
),
643
+
child: DropdownButtonHideUnderline(
644
+
child: DropdownButton<String>(
645
+
value: _language,
646
+
dropdownColor: AppColors.backgroundSecondary,
647
+
style: const TextStyle(
648
+
color: AppColors.textPrimary,
649
+
fontSize: 16,
650
+
),
651
+
icon: const Icon(
652
+
Icons.arrow_drop_down,
653
+
color: AppColors.textSecondary,
654
+
),
655
+
items:
656
+
languages.entries.map((entry) {
657
+
return DropdownMenuItem<String>(
658
+
value: entry.key,
659
+
child: Text(entry.value),
660
+
);
661
+
}).toList(),
662
+
onChanged: (value) {
663
+
if (value != null) {
664
+
setState(() {
665
+
_language = value;
666
+
});
667
+
}
668
+
},
669
+
),
670
+
),
671
+
);
672
+
}
673
+
674
+
Widget _buildNsfwToggle() {
675
+
return Container(
676
+
padding: const EdgeInsets.symmetric(horizontal: 12),
677
+
decoration: BoxDecoration(
678
+
color: AppColors.backgroundSecondary,
679
+
border: Border.all(color: AppColors.border),
680
+
borderRadius: BorderRadius.circular(12),
681
+
),
682
+
child: Row(
683
+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
684
+
children: [
685
+
const Text(
686
+
'NSFW',
687
+
style: TextStyle(
688
+
color: AppColors.textPrimary,
689
+
fontSize: 16,
690
+
),
691
+
),
692
+
Transform.scale(
693
+
scale: 0.8,
694
+
child: Switch.adaptive(
695
+
value: _isNsfw,
696
+
activeTrackColor: AppColors.primary,
697
+
onChanged: (value) {
698
+
setState(() {
699
+
_isNsfw = value;
700
+
});
701
+
},
702
+
),
703
+
),
704
+
],
47
705
),
48
706
);
49
707
}
+7
-1
lib/screens/home/main_shell_screen.dart
+7
-1
lib/screens/home/main_shell_screen.dart
···
30
30
});
31
31
}
32
32
33
+
void _onNavigateToFeed() {
34
+
setState(() {
35
+
_selectedIndex = 0; // Switch to feed tab
36
+
});
37
+
}
38
+
33
39
@override
34
40
Widget build(BuildContext context) {
35
41
return Scaffold(
···
38
44
children: [
39
45
FeedScreen(onSearchTap: _onCommunitiesTap),
40
46
const CommunitiesScreen(),
41
-
const CreatePostScreen(),
47
+
CreatePostScreen(onNavigateToFeed: _onNavigateToFeed),
42
48
const NotificationsScreen(),
43
49
const ProfileScreen(),
44
50
],