Skip to content

Commit 39317bd

Browse files
authored
182 task fix spacing and refresh (#198)
1 parent aec4a6f commit 39317bd

9 files changed

Lines changed: 104 additions & 51 deletions

lam7a/lib/features/profile/ui/view/edit_profile_page.dart

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class _EditProfilePageState extends ConsumerState<EditProfilePage> {
9393
// );
9494
// }
9595

96-
Widget buildField(String label, TextEditingController c, {int maxLines = 1, int? maxLength}) {
96+
Widget buildField(String label, TextEditingController c, {int maxLines = 1, int? maxLength, Key? fieldKey,}) {
9797
return Padding(
9898
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
9999
child: Column(
@@ -102,6 +102,7 @@ class _EditProfilePageState extends ConsumerState<EditProfilePage> {
102102
Text(label, style: const TextStyle(color: Colors.grey)),
103103
const SizedBox(height: 6),
104104
TextField(
105+
key: fieldKey,
105106
controller: c,
106107
maxLines: maxLines,
107108
maxLength: maxLength, // <-- ADD THIS
@@ -124,30 +125,31 @@ class _EditProfilePageState extends ConsumerState<EditProfilePage> {
124125

125126
return Scaffold(
126127
appBar: AppBar(
127-
leading: IconButton(icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context)),
128+
leading: IconButton(key: const ValueKey('edit_profile_close_button'), icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context)),
128129
title: const Text('Edit profile'),
129130
actions: [
130-
TextButton(onPressed: _saving ? null : save, child: Text('Save', style: TextStyle(color: _saving ? Colors.grey : Colors.blue, fontWeight: FontWeight.bold))),
131+
TextButton(key: const ValueKey('edit_profile_save_button'), onPressed: _saving ? null : save, child: Text('Save', style: TextStyle(color: _saving ? Colors.grey : Colors.blue, fontWeight: FontWeight.bold))),
131132
],
132133
),
133134
body: SingleChildScrollView(
134135
child: Column(children: [
135136
GestureDetector(
137+
key: const ValueKey('edit_profile_banner_picker'),
136138
onTap: pickBanner,
137139
child: Image(image: bannerProvider, width: double.infinity, height: 160, fit: BoxFit.cover),
138140
),
139141
const SizedBox(height: 12),
140142
Container(
141143
alignment: Alignment.centerLeft,
142-
child: GestureDetector(onTap: pickAvatar, child: CircleAvatar(radius: 46, backgroundImage: avatarProvider))
144+
child: GestureDetector(key: const ValueKey('edit_profile_avatar_picker'), onTap: pickAvatar, child: CircleAvatar(radius: 46, backgroundImage: avatarProvider))
143145
),
144146

145147
const SizedBox(height: 12),
146-
buildField('Name', nameCtrl),
147-
buildField('Bio', bioCtrl, maxLines: 3, maxLength: 160),
148-
buildField('Location', locationCtrl),
149-
buildField('Website', websiteCtrl),
150-
buildField('Birthday (YYYY-MM-DD)', birthDateCtrl),
148+
buildField('Name', nameCtrl, fieldKey: const ValueKey('edit_profile_name_field'),),
149+
buildField('Bio', bioCtrl, maxLines: 3, maxLength: 160, fieldKey: const ValueKey('edit_profile_bio_field'),),
150+
buildField('Location', locationCtrl, fieldKey: const ValueKey('edit_profile_location_field'),),
151+
buildField('Website', websiteCtrl, fieldKey: const ValueKey('edit_profile_website_field'),),
152+
buildField('Birthday (YYYY-MM-DD)', birthDateCtrl, fieldKey: const ValueKey('edit_profile_birthdate_field'),),
151153
const SizedBox(height: 20),
152154
]),
153155
),

lam7a/lib/features/profile/ui/view/followers_following_page.dart

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ Widget _buildTile(UserModel u) {
7272
final hasImage = u.profileImageUrl != null && u.profileImageUrl!.isNotEmpty;
7373

7474
return InkWell(
75+
key: ValueKey('user_tile_${u.id}'),
7576
onTap: () {
7677
Navigator.push(
7778
context,
@@ -88,6 +89,7 @@ Widget _buildTile(UserModel u) {
8889
children: [
8990
// Avatar
9091
CircleAvatar(
92+
key: ValueKey('user_avatar_${u.id}'),
9193
radius: 24,
9294
backgroundColor: Colors.grey.shade200,
9395
backgroundImage: hasImage ? NetworkImage(u.profileImageUrl!) : null,
@@ -106,13 +108,15 @@ Widget _buildTile(UserModel u) {
106108
Expanded(
107109
child: Text(
108110
u.name ?? "Unknown",
111+
key: ValueKey('user_name_${u.id}'),
109112
style: const TextStyle(
110113
fontWeight: FontWeight.bold,
111114
fontSize: 16,
112115
),
113116
),
114117
),
115118
FollowButton(
119+
key: ValueKey('follow_button_${u.id}'),
116120
user: u,
117121
onFollowStateChanged: () => _loadData(),
118122
),
@@ -121,6 +125,7 @@ Widget _buildTile(UserModel u) {
121125
const SizedBox(height: 1),
122126
Text(
123127
"@${u.username ?? ''}${(u.bio != null && u.bio!.isNotEmpty) ? '\n${u.bio}' : ''}",
128+
key: ValueKey('user_username_${u.id}'),
124129
maxLines: 2,
125130
overflow: TextOverflow.ellipsis,
126131
style: const TextStyle(color: Colors.grey),
@@ -144,33 +149,39 @@ Widget _buildTile(UserModel u) {
144149
@override
145150
Widget build(BuildContext context) {
146151
return Scaffold(
152+
key: const ValueKey('followers_following_scaffold'),
147153
appBar: AppBar(
154+
key: const ValueKey('followers_following_appbar'),
148155
title: const Text('Connections'),
149156
bottom: TabBar(
157+
key: const ValueKey('followers_following_tabbar'),
150158
controller: _tabController,
151159
tabs: const [
152-
Tab(text: 'Followers'),
153-
Tab(text: 'Following'),
160+
Tab(key: ValueKey('followers_tab'), text: 'Followers'),
161+
Tab(key: ValueKey('following_tab'), text: 'Following'),
154162
],
155163
),
156164
),
157165
body: TabBarView(
166+
key: const ValueKey('followers_following_tabview'),
158167
controller: _tabController,
159168
children: [
160169
_loadingFollowers
161-
? const Center(child: CircularProgressIndicator())
170+
? const Center(key: ValueKey('followers_loading'), child: CircularProgressIndicator())
162171
: (_followers == null || _followers!.isEmpty)
163-
? const Center(child: Text('No followers yet'))
172+
? const Center(key: ValueKey('followers_empty'), child: Text('No followers yet'))
164173
: ListView.separated(
174+
key: const ValueKey('followers_list'),
165175
itemCount: _followers!.length,
166176
separatorBuilder: (_, __) => const Divider(height: 0.5),
167177
itemBuilder: (_, i) => _buildTile(_followers![i]),
168178
),
169179
_loadingFollowing
170-
? const Center(child: CircularProgressIndicator())
180+
? const Center(key: ValueKey('following_loading'), child: CircularProgressIndicator())
171181
: (_following == null || _following!.isEmpty)
172-
? const Center(child: Text('Not following anyone'))
182+
? const Center(key: ValueKey('following_empty'), child: Text('Not following anyone'))
173183
: ListView.separated(
184+
key: const ValueKey('following_list'),
174185
itemCount: _following!.length,
175186
separatorBuilder: (_, __) => const Divider(height: 0.5),
176187
itemBuilder: (_, i) => _buildTile(_following![i]),

lam7a/lib/features/profile/ui/view/profile_screen.dart

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@ class ProfileScreen extends ConsumerWidget {
2323

2424
if (username == null || username.isEmpty) {
2525
return const Scaffold(
26+
key: ValueKey('profile_no_username'),
2627
body: Center(child: Text("No username provided")),
2728
);
2829
}
2930

3031
final asyncUser = ref.watch(profileViewModelProvider(username));
3132

3233
return asyncUser.when(
33-
loading: () => const Scaffold(body: Center(child: CircularProgressIndicator())),
34-
error: (err, _) => Scaffold(body: Center(child: Text("Error: $err"))),
34+
loading: () => const Scaffold(key: ValueKey('profile_loading'), body: Center(child: CircularProgressIndicator())),
35+
error: (err, _) => Scaffold(key: const ValueKey('profile_error'), body: Center(child: Text("Error: $err"))),
3536
data: (user) => _ProfileLoaded(user: user, username: username),
3637
);
3738
}
@@ -68,22 +69,28 @@ class _ProfileLoaded extends ConsumerWidget {
6869
const double avatarRadius = 46;
6970

7071
return Scaffold(
72+
key: const ValueKey('profile_loaded_scaffold'),
7173
body: RefreshIndicator(
74+
key: const ValueKey('profile_refresh_indicator'),
7275
onRefresh: refresh,
7376
child: NestedScrollView(
77+
key: const ValueKey('profile_nested_scroll'),
7478
headerSliverBuilder: (_, __) => [
7579
SliverAppBar(
80+
key: const ValueKey('profile_sliver_appbar'),
7681
pinned: true,
7782
expandedHeight: 120,
7883
backgroundColor: Colors.white,
7984
leading: IconButton(
85+
key: const ValueKey('profile_back_button'),
8086
icon: const Icon(Icons.arrow_back),
8187
onPressed: () => Navigator.pop(context),
8288
),
8389

8490
actions: [
8591
if (!isOwnProfile)
8692
ProfileMoreMenu(
93+
key: const ValueKey('profile_more_menu'),
8794
user: user,
8895
username: username,
8996
onAction: () => refresh(),
@@ -97,16 +104,18 @@ class _ProfileLoaded extends ConsumerWidget {
97104
children: [
98105
Positioned.fill(
99106
child: (user.bannerImageUrl != null && user.bannerImageUrl!.isNotEmpty)
100-
? Image.network(user.bannerImageUrl!, fit: BoxFit.cover)
107+
? Image.network(key: const ValueKey('profile_banner'), user.bannerImageUrl!, fit: BoxFit.cover)
101108
: Container(color: Colors.grey.shade300),
102109
),
103110
Positioned(
104111
bottom: -avatarRadius,
105112
left: 16,
106113
child: CircleAvatar(
114+
key: const ValueKey('profile_avatar_outer'),
107115
radius: avatarRadius,
108116
backgroundColor: Colors.white,
109117
child: CircleAvatar(
118+
key: const ValueKey('profile_avatar_inner'),
110119
radius: avatarRadius - 3,
111120
backgroundImage: (user.profileImageUrl != null &&
112121
user.profileImageUrl!.isNotEmpty)
@@ -127,7 +136,9 @@ class _ProfileLoaded extends ConsumerWidget {
127136

128137
// Profile Header (name, handle, bio...)
129138
SliverToBoxAdapter(
139+
key: const ValueKey('profile_header_section'),
130140
child: ProfileHeaderWidget(
141+
key: const ValueKey('profile_header_widget'),
131142
user: user,
132143
isOwnProfile: isOwnProfile,
133144
onEdited: () => refresh(),
@@ -140,19 +151,21 @@ class _ProfileLoaded extends ConsumerWidget {
140151
child: Column(
141152
children: [
142153
const TabBar(
154+
key: ValueKey('profile_tabbar'),
143155
isScrollable: false,
144156
tabs: [
145-
Tab(text: "Posts"),
146-
Tab(text: "Replies"),
147-
Tab(text: "Likes"),
157+
Tab(key: ValueKey('profile_tab_posts'), text: "Posts"),
158+
Tab(key: ValueKey('profile_tab_replies'), text: "Replies"),
159+
Tab(key: ValueKey('profile_tab_likes'), text: "Likes"),
148160
],
149161
),
150162
Expanded(
151163
child: TabBarView(
164+
key: const ValueKey('profile_tabbar_view'),
152165
children: [
153-
_ProfilePostsTab(userId: user.profileId!.toString()),
154-
_ProfileRepliesTab(userId: user.profileId!.toString()),
155-
_ProfileLikesTab(userId: user.profileId!.toString()),
166+
_ProfilePostsTab(key: const ValueKey('profile_posts_tab'), userId: user.profileId!.toString(),),
167+
_ProfileRepliesTab(key: const ValueKey('profile_replies_tab'), userId: user.profileId!.toString(),),
168+
_ProfileLikesTab(key: const ValueKey('profile_likes_tab'), userId: user.profileId!.toString()),
156169
],
157170
),
158171
),
@@ -168,7 +181,7 @@ class _ProfileLoaded extends ConsumerWidget {
168181
// ----------------------- POSTS TAB -----------------------
169182
class _ProfilePostsTab extends ConsumerWidget {
170183
final String userId;
171-
const _ProfilePostsTab({required this.userId});
184+
const _ProfilePostsTab({super.key, required this.userId});
172185

173186
@override
174187
Widget build(BuildContext context, WidgetRef ref) {
@@ -177,20 +190,22 @@ class _ProfilePostsTab extends ConsumerWidget {
177190
return asyncPosts.when(
178191
data: (tweets) {
179192
if (tweets.isEmpty) {
180-
return const Center(child: Text("No posts yet"));
193+
return const Center(key: ValueKey('profile_posts_empty'), child: Text("No posts yet"));
181194
}
182195
return ListView.builder(
196+
key: const ValueKey('profile_posts_list'),
183197
physics: const AlwaysScrollableScrollPhysics(),
184198
primary: false,
185199
shrinkWrap: true,
186200
itemCount: tweets.length,
187201
itemBuilder: (_, i) => TweetSummaryWidget(
202+
key: ValueKey('profile_post_${tweets[i].id}'),
188203
tweetId: tweets[i].id,
189204
tweetData: tweets[i],
190205
),
191206
);
192207
},
193-
loading: () => const Center(child: CircularProgressIndicator()),
208+
loading: () => const Center(key: ValueKey('profile_posts_loading'), child: CircularProgressIndicator()),
194209
error: (_, __) => const Center(child: Text("Error loading posts")),
195210
);
196211
}
@@ -199,7 +214,7 @@ class _ProfilePostsTab extends ConsumerWidget {
199214
// ----------------------- REPLIES TAB -----------------------
200215
class _ProfileRepliesTab extends ConsumerWidget {
201216
final String userId;
202-
const _ProfileRepliesTab({required this.userId});
217+
const _ProfileRepliesTab({super.key, required this.userId});
203218

204219
@override
205220
Widget build(BuildContext context, WidgetRef ref) {
@@ -211,26 +226,28 @@ class _ProfileRepliesTab extends ConsumerWidget {
211226
return const Center(child: Text("No replies yet"));
212227
}
213228
return ListView.builder(
229+
key: ValueKey('profile_replies_list'),
214230
physics: const AlwaysScrollableScrollPhysics(),
215231
primary: false,
216232
shrinkWrap: true,
217233
itemCount: tweets.length,
218234
itemBuilder: (_, i) => TweetSummaryWidget(
235+
key: ValueKey('profile_reply_${tweets[i].id}'),
219236
tweetId: tweets[i].id,
220237
tweetData: tweets[i],
221238
),
222239
);
223240
},
224-
loading: () => const Center(child: CircularProgressIndicator()),
225-
error: (_, __) => const Center(child: Text("Error loading replies")),
241+
loading: () => const Center(key: ValueKey('profile_replies_loading'), child: CircularProgressIndicator()),
242+
error: (_, __) => const Center(key: ValueKey('profile_replies_empty'), child: Text("Error loading replies")),
226243
);
227244
}
228245
}
229246

230247
// ----------------------- LIKES TAB -----------------------
231248
class _ProfileLikesTab extends ConsumerWidget {
232249
final String userId;
233-
const _ProfileLikesTab({required this.userId});
250+
const _ProfileLikesTab({super.key, required this.userId});
234251

235252
@override
236253
Widget build(BuildContext context, WidgetRef ref) {
@@ -242,18 +259,20 @@ class _ProfileLikesTab extends ConsumerWidget {
242259
return const Center(child: Text("No liked posts"));
243260
}
244261
return ListView.builder(
262+
key: ValueKey('profile_likes_list'),
245263
physics: const AlwaysScrollableScrollPhysics(),
246264
primary: false,
247265
shrinkWrap: true,
248266
itemCount: tweets.length,
249267
itemBuilder: (_, i) => TweetSummaryWidget(
268+
key: ValueKey('profile_like_${tweets[i].id}'),
250269
tweetId: tweets[i].id,
251270
tweetData: tweets[i],
252271
),
253272
);
254273
},
255-
loading: () => const Center(child: CircularProgressIndicator()),
256-
error: (_, __) => const Center(child: Text("Error loading liked posts")),
274+
loading: () => const Center(key: ValueKey('profile_likes_loading'), child: CircularProgressIndicator()),
275+
error: (_, __) => const Center(key: ValueKey('profile_likes_empty'), child: Text("Error loading liked posts")),
257276
);
258277
}
259278
}

lam7a/lib/features/profile/ui/widgets/blocked_profile_view.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ class BlockedProfileView extends ConsumerWidget {
2020
final repo = ref.watch(profileRepositoryProvider);
2121

2222
return Scaffold(
23+
key: const ValueKey('blocked_profile_screen'),
2324
appBar: AppBar(
2425
leading: IconButton(
26+
key: const ValueKey('blocked_profile_back_button'),
2527
icon: const Icon(Icons.arrow_back),
2628
onPressed: () => Navigator.pop(context),
2729
),
@@ -36,25 +38,28 @@ class BlockedProfileView extends ConsumerWidget {
3638
child: Column(
3739
mainAxisAlignment: MainAxisAlignment.center,
3840
children: [
39-
const Icon(Icons.block, size: 80, color: Colors.red),
41+
const Icon(Icons.block, size: 80, color: Colors.red, key: ValueKey('blocked_profile_icon')),
4042
const SizedBox(height: 20),
4143

4244
Text(
4345
"You blocked @$username",
46+
key: const ValueKey('blocked_profile_title'),
4447
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
4548
textAlign: TextAlign.center,
4649
),
4750

4851
const SizedBox(height: 12),
4952
const Text(
5053
"You can't follow or see their posts.",
54+
key: ValueKey('blocked_profile_subtitle'),
5155
style: TextStyle(color: Colors.grey),
5256
textAlign: TextAlign.center,
5357
),
5458

5559
const SizedBox(height: 25),
5660

5761
ElevatedButton(
62+
key: const ValueKey('blocked_profile_unblock_button'),
5863
onPressed: () async {
5964
await repo.unblockUser(userId);
6065
onUnblock();

0 commit comments

Comments
 (0)