[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: simplify lint setup

+686 -1767
+4 -22
analysis_options.yaml
··· 1 1 include: 2 - - package:very_good_analysis/analysis_options.yaml 3 2 - package:flutter_lints/flutter.yaml 4 3 5 4 analyzer: 6 5 errors: 7 - avoid_catches_without_on_clauses: ignore 8 - avoid_dynamic_calls: ignore 9 - inference_failure_on_function_invocation: ignore 10 - inference_failure_on_instance_creation: ignore 11 6 invalid_annotation_target: ignore 12 - comment_references: ignore # enable when starting documentation 13 - public_member_api_docs: ignore # enable when starting documentation 14 - prefer_null_aware_method_calls: ignore 15 - strict_raw_type: ignore 16 - avoid_equals_and_hash_code_on_mutable_classes: ignore 17 - inference_failure_on_function_return_type: ignore 18 - inference_failure_on_untyped_parameter: ignore 19 - flutter_style_todos: ignore 20 - specify_nonobvious_property_types: ignore 21 - sort_constructors_first: ignore 22 - unawaited_futures: ignore 23 - avoid_positional_boolean_parameters: ignore 24 - use_setters_to_change_properties: ignore 25 - document_ignores: ignore 26 - one_member_abstracts: ignore 27 - discarded_futures: ignore 7 + language: 8 + strict-casts: true 9 + strict-raw-types: true 28 10 exclude: 29 - - '**/*.g.dart' 11 + - "**/*.g.dart"
+2 -4
lib/src/core/auth/data/models/login_result.dart
··· 6 6 /// Result of a login attempt 7 7 @freezed 8 8 abstract class LoginResult with _$LoginResult { 9 - const factory LoginResult({ 10 - required LoginStatus status, 11 - String? error, 12 - }) = _LoginResult; 9 + const factory LoginResult({required LoginStatus status, String? error}) = 10 + _LoginResult; 13 11 const LoginResult._(); 14 12 15 13 factory LoginResult.success() =>
+3 -12
lib/src/core/auth/data/repositories/auth_repository_impl.dart
··· 173 173 ? Uri.parse(_pdsEndpoint!).host 174 174 : null; 175 175 176 - _atProto = ATProto.fromOAuthSession( 177 - _oauthSession!, 178 - service: pdsHost, 179 - ); 176 + _atProto = ATProto.fromOAuthSession(_oauthSession!, service: pdsHost); 180 177 } catch (e) { 181 178 _logger.e('Error loading saved account', error: e); 182 179 } ··· 382 379 // Create ATProto client from OAuth session 383 380 if (_pdsEndpoint != null) { 384 381 final pdsHost = Uri.parse(_pdsEndpoint!).host; 385 - _atProto = ATProto.fromOAuthSession( 386 - _oauthSession!, 387 - service: pdsHost, 388 - ); 382 + _atProto = ATProto.fromOAuthSession(_oauthSession!, service: pdsHost); 389 383 } else { 390 384 _logger.e('PDS endpoint is null, cannot create ATProto client'); 391 385 return LoginResult.failed('PDS endpoint not found'); ··· 480 474 final pdsHost = _pdsEndpoint != null 481 475 ? Uri.parse(_pdsEndpoint!).host 482 476 : null; 483 - _atProto = ATProto.fromOAuthSession( 484 - _oauthSession!, 485 - service: pdsHost, 486 - ); 477 + _atProto = ATProto.fromOAuthSession(_oauthSession!, service: pdsHost); 487 478 488 479 await _saveSession(); 489 480 return true;
+1 -3
lib/src/core/auth/data/repositories/onboarding_repository_impl.dart
··· 55 55 if (_did == null) return null; 56 56 57 57 try { 58 - final uri = AtUri.parse( 59 - 'at://$_did/app.bsky.actor.profile/self', 60 - ); 58 + final uri = AtUri.parse('at://$_did/app.bsky.actor.profile/self'); 61 59 final response = await _repoRepository.getRecord(uri: uri); 62 60 return ActorProfileRecord.fromJson(response.record.toJson()); 63 61 } catch (e) {
+1 -4
lib/src/core/design_system/components/atoms/avatar_stack.dart
··· 5 5 6 6 /// Data class representing an avatar in the stack. 7 7 class AvatarData { 8 - const AvatarData({ 9 - required this.imageUrl, 10 - required this.username, 11 - }); 8 + const AvatarData({required this.imageUrl, required this.username}); 12 9 13 10 final String imageUrl; 14 11 final String username;
+1 -5
lib/src/core/design_system/components/atoms/buttons/accent_button.dart
··· 9 9 final String label; 10 10 final VoidCallback? onPressed; 11 11 12 - const AccentButton({ 13 - required this.label, 14 - super.key, 15 - this.onPressed, 16 - }); 12 + const AccentButton({required this.label, super.key, this.onPressed}); 17 13 18 14 @override 19 15 Widget build(BuildContext context) {
+1 -5
lib/src/core/design_system/components/atoms/buttons/action_button.dart
··· 26 26 const pressedScale = 0.9; 27 27 const iconSize = 34.0; 28 28 29 - final sizedIcon = SizedBox( 30 - width: iconSize, 31 - height: iconSize, 32 - child: icon, 33 - ); 29 + final sizedIcon = SizedBox(width: iconSize, height: iconSize, child: icon); 34 30 35 31 return SizedBox( 36 32 width: baseSize,
+1 -5
lib/src/core/design_system/components/atoms/buttons/circle_icon_button.dart
··· 46 46 ); 47 47 48 48 if (semanticLabel != null) { 49 - return Semantics( 50 - label: semanticLabel, 51 - button: true, 52 - child: content, 53 - ); 49 + return Semantics(label: semanticLabel, button: true, child: content); 54 50 } 55 51 56 52 return content;
+2 -8
lib/src/core/design_system/components/atoms/buttons/live_button.dart
··· 7 7 final Widget child; 8 8 final VoidCallback? onTap; 9 9 10 - const LiveButton({ 11 - required this.child, 12 - super.key, 13 - this.onTap, 14 - }); 10 + const LiveButton({required this.child, super.key, this.onTap}); 15 11 16 12 @override 17 13 Widget build(BuildContext context) { ··· 36 32 ), 37 33 borderRadius: BorderRadius.circular(50), 38 34 ), 39 - child: Center( 40 - child: child, 41 - ), 35 + child: Center(child: child), 42 36 ), 43 37 ), 44 38 ),
+1 -4
lib/src/core/design_system/components/atoms/buttons/primary_button.dart
··· 59 59 color: AppColors.greyWhite, 60 60 ), 61 61 ), 62 - if (trailing != null) ...[ 63 - const SizedBox(width: 8), 64 - trailing!, 65 - ], 62 + if (trailing != null) ...[const SizedBox(width: 8), trailing!], 66 63 ], 67 64 ), 68 65 ),
+1 -5
lib/src/core/design_system/components/atoms/buttons/tappable_text.dart
··· 6 6 final String text; 7 7 final VoidCallback onTap; 8 8 9 - const TappableText({ 10 - required this.text, 11 - required this.onTap, 12 - super.key, 13 - }); 9 + const TappableText({required this.text, required this.onTap, super.key}); 14 10 15 11 @override 16 12 Widget build(BuildContext context) {
+2 -9
lib/src/core/design_system/components/atoms/buttons/text_button.dart
··· 7 7 final String label; 8 8 final VoidCallback? onTap; 9 9 10 - const TextButton({ 11 - required this.label, 12 - super.key, 13 - this.onTap, 14 - }); 10 + const TextButton({required this.label, super.key, this.onTap}); 15 11 16 12 @override 17 13 Widget build(BuildContext context) { ··· 27 23 color: isDark ? AppColors.darkGreyButton : AppColors.lightGreyButton, 28 24 borderRadius: const BorderRadius.all(Radius.circular(8)), 29 25 border: const Border.fromBorderSide( 30 - BorderSide( 31 - color: AppColors.greyBorder, 32 - width: 1.14667, 33 - ), 26 + BorderSide(color: AppColors.greyBorder, width: 1.14667), 34 27 ), 35 28 ), 36 29 child: Align(
+1 -3
lib/src/core/design_system/components/atoms/tags/feed_tag.dart
··· 43 43 decoration: BoxDecoration( 44 44 color: Colors.white.withAlpha(50), 45 45 borderRadius: BorderRadius.circular(9), 46 - border: Border.all( 47 - color: Colors.white.withAlpha(37), 48 - ), 46 + border: Border.all(color: Colors.white.withAlpha(37)), 49 47 ), 50 48 child: Center( 51 49 child: Text(
+1 -3
lib/src/core/design_system/components/atoms/tags/hashtag.dart
··· 28 28 decoration: BoxDecoration( 29 29 color: isDark ? AppColors.darkGreyButton : AppColors.lightGreyButton, 30 30 borderRadius: BorderRadius.circular(13), 31 - border: Border.all( 32 - color: AppColors.greyBorder, 33 - ), 31 + border: Border.all(color: AppColors.greyBorder), 34 32 ), 35 33 child: Row( 36 34 mainAxisAlignment: MainAxisAlignment.center,
+1 -3
lib/src/core/design_system/components/atoms/tags/tag.dart
··· 28 28 decoration: BoxDecoration( 29 29 color: isDark ? AppColors.darkGreyButton : AppColors.lightGreyButton, 30 30 borderRadius: BorderRadius.circular(13), 31 - border: Border.all( 32 - color: AppColors.greyBorder, 33 - ), 31 + border: Border.all(color: AppColors.greyBorder), 34 32 ), 35 33 child: Row( 36 34 mainAxisAlignment: MainAxisAlignment.center,
+1 -3
lib/src/core/design_system/components/atoms/toggles/follow_button.dart
··· 46 46 color: isDark ? AppColors.red900 : AppColors.red50, 47 47 borderRadius: const BorderRadius.all(Radius.circular(8)), 48 48 border: Border.fromBorderSide( 49 - BorderSide( 50 - color: isDark ? AppColors.red800 : AppColors.red200, 51 - ), 49 + BorderSide(color: isDark ? AppColors.red800 : AppColors.red200), 52 50 ), 53 51 ), 54 52 child: Align(
+1 -3
lib/src/core/design_system/components/atoms/toggles/glass_follow_button.dart
··· 40 40 decoration: BoxDecoration( 41 41 color: Colors.white.withAlpha(51), 42 42 borderRadius: BorderRadius.circular(100), 43 - border: Border.all( 44 - color: Colors.white.withAlpha(37), 45 - ), 43 + border: Border.all(color: Colors.white.withAlpha(37)), 46 44 ), 47 45 child: Center( 48 46 child: Text(
+1 -1
lib/src/core/design_system/components/molecules/create_media_sheet.dart
··· 3 3 /// Shows a standardized bottom sheet for creating media: 4 4 /// Record, Upload Video, Upload Images. 5 5 /// Only renders the actions whose callbacks are provided (non-null). 6 - Future showCreateMediaSheet( 6 + Future<dynamic> showCreateMediaSheet( 7 7 BuildContext context, { 8 8 VoidCallback? onRecord, 9 9 VoidCallback? onUploadVideo,
+4 -17
lib/src/core/design_system/components/molecules/feed_card.dart
··· 120 120 ); 121 121 122 122 if (onTap != null) { 123 - return GestureDetector( 124 - onTap: onTap, 125 - child: content, 126 - ); 123 + return GestureDetector(onTap: onTap, child: content); 127 124 } 128 125 129 126 return content; ··· 169 166 mainAxisSize: MainAxisSize.min, 170 167 children: [ 171 168 if (isLiked) 172 - AppIcons.likeFilled( 173 - size: 14, 174 - color: AppColors.primary600, 175 - ) 169 + AppIcons.likeFilled(size: 14, color: AppColors.primary600) 176 170 else 177 171 AppIcons.like( 178 172 size: 14, 179 173 color: isDark ? AppColors.grey200 : AppColors.grey400, 180 174 ), 181 175 const SizedBox(width: 4), 182 - Text( 183 - _formatCount(likeCount), 184 - style: AppTypography.textExtraSmallThin, 185 - ), 176 + Text(_formatCount(likeCount), style: AppTypography.textExtraSmallThin), 186 177 const SizedBox(width: 8), 187 178 ], 188 179 ); ··· 305 296 ), 306 297 ), 307 298 ) 308 - : const Icon( 309 - FluentIcons.feed_24_regular, 310 - color: iconColor, 311 - size: 18, 312 - ), 299 + : const Icon(FluentIcons.feed_24_regular, color: iconColor, size: 18), 313 300 ); 314 301 } 315 302 }
+2 -8
lib/src/core/design_system/components/molecules/feed_tag_list.dart
··· 118 118 119 119 return Transform.scale( 120 120 scale: scale, 121 - child: Material( 122 - color: Colors.transparent, 123 - child: child, 124 - ), 121 + child: Material(color: Colors.transparent, child: child), 125 122 ); 126 123 }, 127 124 child: child, ··· 220 217 ) 221 218 : listView; 222 219 223 - return SizedBox( 224 - height: 30, 225 - child: fadedList, 226 - ); 220 + return SizedBox(height: 30, child: fadedList); 227 221 } 228 222 }
+3 -12
lib/src/core/design_system/components/molecules/known_interactions_bar.dart
··· 14 14 /// Shows reposts on the left with a green repost icon, and likes on the right 15 15 /// with a pink heart icon. Only renders if there are actual interactions. 16 16 class KnownInteractionsBar extends StatelessWidget { 17 - const KnownInteractionsBar({ 18 - required this.interactions, 19 - super.key, 20 - }); 17 + const KnownInteractionsBar({required this.interactions, super.key}); 21 18 22 19 /// List of known interactions to display. 23 20 final List<KnownInteraction>? interactions; ··· 75 72 76 73 /// A single interaction pill with icon and avatar stack. 77 74 class _InteractionPill extends StatelessWidget { 78 - const _InteractionPill({ 79 - required this.icon, 80 - required this.avatars, 81 - }); 75 + const _InteractionPill({required this.icon, required this.avatars}); 82 76 83 77 final Widget icon; 84 78 final List<AvatarData> avatars; ··· 104 98 children: [ 105 99 icon, 106 100 const SizedBox(width: 6), 107 - AvatarStack( 108 - avatars: avatars, 109 - largeSize: 32, 110 - ), 101 + AvatarStack(avatars: avatars, largeSize: 32), 111 102 ], 112 103 ), 113 104 ),
+7 -18
lib/src/core/design_system/components/molecules/post_tile.dart
··· 62 62 fadeInDuration: Duration.zero, 63 63 imageUrl: thumbnailUrl, 64 64 fit: BoxFit.cover, 65 - placeholder: (context, url) => const ColoredBox( 66 - color: AppColors.grey800, 67 - ), 65 + placeholder: (context, url) => 66 + const ColoredBox(color: AppColors.grey800), 68 67 errorWidget: (context, url, error) => const ColoredBox( 69 68 color: AppColors.grey800, 70 - child: Icon( 71 - Icons.broken_image, 72 - color: AppColors.grey400, 73 - ), 69 + child: Icon(Icons.broken_image, color: AppColors.grey400), 74 70 ), 75 71 ), 76 72 ) ··· 79 75 fadeInDuration: Duration.zero, 80 76 imageUrl: thumbnailUrl, 81 77 fit: BoxFit.cover, 82 - placeholder: (context, url) => const ColoredBox( 83 - color: AppColors.grey800, 84 - ), 78 + placeholder: (context, url) => 79 + const ColoredBox(color: AppColors.grey800), 85 80 errorWidget: (context, url, error) => const ColoredBox( 86 81 color: AppColors.grey800, 87 - child: Icon( 88 - Icons.broken_image, 89 - color: AppColors.grey400, 90 - ), 82 + child: Icon(Icons.broken_image, color: AppColors.grey400), 91 83 ), 92 84 ), 93 - if (seen) 94 - Container( 95 - color: Colors.black.withAlpha(180), 96 - ), 85 + if (seen) Container(color: Colors.black.withAlpha(180)), 97 86 Positioned( 98 87 bottom: 0, 99 88 right: 0,
+2 -10
lib/src/core/design_system/components/molecules/profile_action_buttons.dart
··· 28 28 Widget build(BuildContext context) { 29 29 if (isCurrentUser) { 30 30 return Row( 31 - children: [ 32 - Expanded( 33 - child: _EditButton( 34 - onTap: onEditTap, 35 - ), 36 - ), 37 - ], 31 + children: [Expanded(child: _EditButton(onTap: onEditTap))], 38 32 ); 39 33 } 40 34 ··· 59 53 60 54 /// Edit button that matches the FollowButton's following state style 61 55 class _EditButton extends StatelessWidget { 62 - const _EditButton({ 63 - this.onTap, 64 - }); 56 + const _EditButton({this.onTap}); 65 57 66 58 final VoidCallback? onTap; 67 59
+1 -4
lib/src/core/design_system/components/molecules/profile_card.dart
··· 174 174 ); 175 175 176 176 if (onTap != null) { 177 - return GestureDetector( 178 - onTap: onTap, 179 - child: content, 180 - ); 177 + return GestureDetector(onTap: onTap, child: content); 181 178 } 182 179 183 180 return content;
+1 -4
lib/src/core/design_system/components/molecules/profile_info.dart
··· 112 112 final spans = _buildTextSpans(text, usernameMatches); 113 113 114 114 return RichText( 115 - text: TextSpan( 116 - style: style, 117 - children: spans, 118 - ), 115 + text: TextSpan(style: style, children: spans), 119 116 ); 120 117 } 121 118 }
+1 -4
lib/src/core/design_system/components/molecules/profile_stats.dart
··· 40 40 } 41 41 42 42 class _StatItem extends StatelessWidget { 43 - const _StatItem({ 44 - required this.count, 45 - required this.label, 46 - }); 43 + const _StatItem({required this.count, required this.label}); 47 44 48 45 final String count; 49 46 final String label;
+1 -4
lib/src/core/design_system/components/molecules/recording_button.dart
··· 144 144 height: _outerSize, 145 145 decoration: BoxDecoration( 146 146 shape: BoxShape.circle, 147 - border: Border.all( 148 - color: Colors.white, 149 - width: _ringWidth, 150 - ), 147 + border: Border.all(color: Colors.white, width: _ringWidth), 151 148 ), 152 149 ), 153 150 // Animated inner shape (white circle -> red square)
+5 -20
lib/src/core/design_system/components/molecules/settings_feed_card.dart
··· 7 7 import 'package:spark/src/core/design_system/tokens/typography.dart'; 8 8 import 'package:spark/src/core/network/atproto/data/models/feed_models.dart'; 9 9 10 - enum SettingsFeedCardMode { 11 - display, 12 - edit, 13 - } 10 + enum SettingsFeedCardMode { display, edit } 14 11 15 12 class SettingsFeedCard extends StatelessWidget { 16 13 const SettingsFeedCard({ ··· 155 152 child: Container( 156 153 width: 40, 157 154 height: 40, 158 - decoration: BoxDecoration( 159 - borderRadius: BorderRadius.circular(8), 160 - ), 155 + decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)), 161 156 child: _avatarUrl.isNotEmpty 162 157 ? Image.network( 163 158 _avatarUrl, ··· 244 239 ), 245 240 ), 246 241 child: _isLiked 247 - ? AppIcons.likeFilled( 248 - size: 16, 249 - color: Colors.white, 250 - ) 251 - : AppIcons.like( 252 - size: 16, 253 - color: Colors.grey, 254 - ), 242 + ? AppIcons.likeFilled(size: 16, color: Colors.white) 243 + : AppIcons.like(size: 16, color: Colors.grey), 255 244 ), 256 245 ); 257 246 } ··· 271 260 color: Colors.red.shade500, 272 261 borderRadius: BorderRadius.circular(6), 273 262 ), 274 - child: const Icon( 275 - Icons.delete, 276 - size: 16, 277 - color: Colors.white, 278 - ), 263 + child: const Icon(Icons.delete, size: 16, color: Colors.white), 279 264 ), 280 265 ), 281 266 ],
+2 -7
lib/src/core/design_system/components/molecules/story_circle.dart
··· 48 48 } 49 49 50 50 /// Constructor variant for a "Close Friends" story with a green border. 51 - factory StoryCircle.cf({ 52 - required String userName, 53 - required String imageUrl, 54 - }) { 51 + factory StoryCircle.cf({required String userName, required String imageUrl}) { 55 52 return StoryCircle._( 56 53 type: StoryType.cf, 57 54 userName: userName, ··· 180 177 } 181 178 182 179 class _LiveBadge extends StatelessWidget { 183 - const _LiveBadge({ 184 - required this.live, 185 - }); 180 + const _LiveBadge({required this.live}); 186 181 187 182 final String live; 188 183
+3 -11
lib/src/core/design_system/components/organisms/bottom_nav_bar.dart
··· 32 32 children: [ 33 33 _BarBackground( 34 34 child: Container( 35 - padding: EdgeInsets.only( 36 - top: 12, 37 - bottom: 12 + bottomPadding, 38 - ), 35 + padding: EdgeInsets.only(top: 12, bottom: 12 + bottomPadding), 39 36 color: isDark 40 37 ? const Color.fromARGB(51, 0, 0, 0) 41 38 : const Color.fromARGB(178, 255, 255, 255), ··· 242 239 height: 34, 243 240 decoration: BoxDecoration( 244 241 shape: BoxShape.circle, 245 - border: Border.all( 246 - color: Colors.white, 247 - ), 242 + border: Border.all(color: Colors.white), 248 243 image: image is AssetImage 249 244 ? null 250 245 : DecorationImage(image: image, fit: BoxFit.cover), ··· 260 255 ) 261 256 : null, 262 257 ); 263 - return GestureDetector( 264 - onTap: onTap, 265 - child: avatar, 266 - ); 258 + return GestureDetector(onTap: onTap, child: avatar); 267 259 } 268 260 }
+5 -20
lib/src/core/design_system/components/organisms/side_action_bar.dart
··· 219 219 soundCover.startsWith('https://'))) { 220 220 children.addAll([ 221 221 const SizedBox(height: 13), 222 - _SoundItem( 223 - cover: soundCover, 224 - onTap: widget.onSoundTap, 225 - ), 222 + _SoundItem(cover: soundCover, onTap: widget.onSoundTap), 226 223 ]); 227 224 } 228 225 229 - return Column( 230 - mainAxisSize: MainAxisSize.min, 231 - children: children, 232 - ); 226 + return Column(mainAxisSize: MainAxisSize.min, children: children); 233 227 } 234 228 } 235 229 ··· 330 324 } 331 325 332 326 class _SoundItem extends StatelessWidget { 333 - const _SoundItem({ 334 - required this.cover, 335 - this.onTap, 336 - }); 327 + const _SoundItem({required this.cover, this.onTap}); 337 328 338 329 final String cover; 339 330 final VoidCallback? onTap; ··· 357 348 shape: BoxShape.circle, 358 349 color: hasValidCover ? null : Colors.grey[800], 359 350 image: hasValidCover 360 - ? DecorationImage( 361 - image: NetworkImage(cover), 362 - fit: BoxFit.cover, 363 - ) 351 + ? DecorationImage(image: NetworkImage(cover), fit: BoxFit.cover) 364 352 : null, 365 353 ), 366 354 ), ··· 405 393 ), 406 394 ), 407 395 if (i < destinations.length - 1) 408 - Container( 409 - height: 1, 410 - color: Colors.white.withValues(alpha: 0.25), 411 - ), 396 + Container(height: 1, color: Colors.white.withValues(alpha: 0.25)), 412 397 ], 413 398 ], 414 399 ),
+2 -8
lib/src/core/design_system/components/organisms/sticky_profile_tab_bar.dart
··· 1 1 import 'package:flutter/material.dart'; 2 2 3 3 class StickyProfileTabBar extends SliverPersistentHeaderDelegate { 4 - StickyProfileTabBar({ 5 - required this.child, 6 - this.height = 50.0, 7 - }); 4 + StickyProfileTabBar({required this.child, this.height = 50.0}); 8 5 9 6 final Widget child; 10 7 final double height; ··· 15 12 double shrinkOffset, 16 13 bool overlapsContent, 17 14 ) { 18 - return Material( 19 - elevation: shrinkOffset > 0 ? 1.0 : 0.0, 20 - child: child, 21 - ); 15 + return Material(elevation: shrinkOffset > 0 ? 1.0 : 0.0, child: child); 22 16 } 23 17 24 18 @override
+2 -7
lib/src/core/design_system/templates/explore_page_template.dart
··· 29 29 child: Column( 30 30 crossAxisAlignment: CrossAxisAlignment.start, 31 31 children: [ 32 - Padding( 33 - padding: const EdgeInsets.all(16), 34 - child: searchWidget, 35 - ), 32 + Padding(padding: const EdgeInsets.all(16), child: searchWidget), 36 33 if (showTabs && tabsWidget != null) tabsWidget!, 37 - Expanded( 38 - child: showTabs ? contentWidget : emptyStateWidget, 39 - ), 34 + Expanded(child: showTabs ? contentWidget : emptyStateWidget), 40 35 ], 41 36 ), 42 37 ),
+2 -7
lib/src/core/design_system/templates/feeds_bar_template.dart
··· 60 60 gradient: LinearGradient( 61 61 begin: Alignment.topCenter, 62 62 end: Alignment.bottomCenter, 63 - colors: [ 64 - Color.fromARGB(110, 0, 0, 0), 65 - Colors.transparent, 66 - ], 63 + colors: [Color.fromARGB(110, 0, 0, 0), Colors.transparent], 67 64 ), 68 65 ), 69 66 ), ··· 87 84 message: 'Create post', 88 85 child: GestureDetector( 89 86 onTap: onLeadingPressed, 90 - child: Center( 91 - child: AppIcons.addPostFilled(size: 28), 92 - ), 87 + child: Center(child: AppIcons.addPostFilled(size: 28)), 93 88 ), 94 89 ), 95 90 ),
+3 -10
lib/src/core/design_system/templates/image_review_page_template.dart
··· 77 77 color: theme.textTheme.titleLarge?.color, 78 78 tooltip: 'Back', 79 79 ), 80 - title: Text( 81 - title, 82 - ), 80 + title: Text(title), 83 81 centerTitle: false, 84 82 ), 85 83 body: SafeArea( ··· 341 339 } 342 340 343 341 class _DescriptionSection extends StatelessWidget { 344 - const _DescriptionSection({ 345 - required this.controller, 346 - required this.maxChars, 347 - }); 342 + const _DescriptionSection({required this.controller, required this.maxChars}); 348 343 349 344 final TextEditingController controller; 350 345 final int maxChars; ··· 411 406 ), 412 407 title: Text( 413 408 'Post to Bluesky', 414 - style: AppTypography.textMediumBold.copyWith( 415 - color: titleColor, 416 - ), 409 + style: AppTypography.textMediumBold.copyWith(color: titleColor), 417 410 ), 418 411 trailing: Switch(value: value, onChanged: onChanged), 419 412 onTap: () => onChanged(!value),
+1 -3
lib/src/core/design_system/templates/info_bar_template.dart
··· 116 116 if (widget.showFollowButton) 117 117 Padding( 118 118 padding: const EdgeInsets.only(left: 8, top: 2), 119 - child: FollowPillButton( 120 - onPressed: widget.onFollow ?? () {}, 121 - ), 119 + child: FollowPillButton(onPressed: widget.onFollow ?? () {}), 122 120 ), 123 121 ], 124 122 ),
+1 -5
lib/src/core/design_system/templates/profile_page_template.dart
··· 88 88 return Scaffold( 89 89 appBar: AppBar( 90 90 centerTitle: isCurrentUser, 91 - title: appBarTitle != null 92 - ? Text( 93 - appBarTitle!, 94 - ) 95 - : null, 91 + title: appBarTitle != null ? Text(appBarTitle!) : null, 96 92 elevation: 0, 97 93 actions: appBarActions, 98 94 leading: leading ?? const AppLeadingButton(),
+3 -11
lib/src/core/design_system/templates/recording_page_template.dart
··· 78 78 Positioned.fill( 79 79 child: Transform.scale( 80 80 scale: scale, 81 - child: Center( 82 - child: cameraPreview, 83 - ), 81 + child: Center(child: cameraPreview), 84 82 ), 85 83 ), 86 84 // Top controls aligned within rounded view ··· 122 120 } 123 121 124 122 class _TopOverlay extends StatelessWidget { 125 - const _TopOverlay({ 126 - required this.onBack, 127 - required this.timer, 128 - }); 123 + const _TopOverlay({required this.onBack, required this.timer}); 129 124 130 125 final VoidCallback onBack; 131 126 final Widget timer; ··· 206 201 gradient: LinearGradient( 207 202 begin: Alignment.bottomCenter, 208 203 end: Alignment.topCenter, 209 - colors: [ 210 - Colors.black.withAlpha(180), 211 - Colors.transparent, 212 - ], 204 + colors: [Colors.black.withAlpha(180), Colors.transparent], 213 205 ), 214 206 ), 215 207 padding: EdgeInsets.only(
+2 -8
lib/src/core/design_system/templates/video_review_page_template.dart
··· 192 192 } 193 193 194 194 class _DescriptionSection extends StatelessWidget { 195 - const _DescriptionSection({ 196 - required this.controller, 197 - required this.maxChars, 198 - }); 195 + const _DescriptionSection({required this.controller, required this.maxChars}); 199 196 200 197 final TextEditingController controller; 201 198 final int maxChars; ··· 226 223 } 227 224 228 225 class _CrossPostSection extends StatelessWidget { 229 - const _CrossPostSection({ 230 - required this.value, 231 - required this.onChanged, 232 - }); 226 + const _CrossPostSection({required this.value, required this.onChanged}); 233 227 234 228 final bool value; 235 229 final ValueChanged<bool> onChanged;
+4 -12
lib/src/core/design_system/theme/app_theme.dart
··· 31 31 borderRadius: BorderRadius.circular(12), 32 32 ), 33 33 padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), 34 - textStyle: const TextStyle( 35 - fontWeight: FontWeight.bold, 36 - ), 34 + textStyle: const TextStyle(fontWeight: FontWeight.bold), 37 35 ), 38 36 ), 39 37 outlinedButtonTheme: OutlinedButtonThemeData( ··· 44 42 borderRadius: BorderRadius.circular(12), 45 43 ), 46 44 padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), 47 - textStyle: const TextStyle( 48 - fontWeight: FontWeight.bold, 49 - ), 45 + textStyle: const TextStyle(fontWeight: FontWeight.bold), 50 46 ), 51 47 ), 52 48 inputDecorationTheme: InputDecorationTheme( ··· 120 116 borderRadius: BorderRadius.circular(12), 121 117 ), 122 118 padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), 123 - textStyle: const TextStyle( 124 - fontWeight: FontWeight.bold, 125 - ), 119 + textStyle: const TextStyle(fontWeight: FontWeight.bold), 126 120 ), 127 121 ), 128 122 outlinedButtonTheme: OutlinedButtonThemeData( ··· 133 127 borderRadius: BorderRadius.circular(12), 134 128 ), 135 129 padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), 136 - textStyle: const TextStyle( 137 - fontWeight: FontWeight.bold, 138 - ), 130 + textStyle: const TextStyle(fontWeight: FontWeight.bold), 139 131 ), 140 132 ), 141 133 inputDecorationTheme: InputDecorationTheme(
+2 -4
lib/src/core/design_system/theme/text_theme.dart
··· 37 37 38 38 /// Dark theme text theme 39 39 /// Same typography but optimized for dark backgrounds 40 - static TextTheme get dark => light.apply( 41 - bodyColor: Colors.white, 42 - displayColor: Colors.white, 43 - ); 40 + static TextTheme get dark => 41 + light.apply(bodyColor: Colors.white, displayColor: Colors.white); 44 42 }
+36 -170
lib/src/core/design_system/tokens/gradients.dart
··· 6 6 static const accent = LinearGradient( 7 7 begin: Alignment(-1.3914, 1.0057999999999998), 8 8 end: Alignment(2.0484, -1.3258), 9 - stops: [ 10 - 0, 11 - 1, 12 - ], 13 - colors: [ 14 - Color(0xffff97cd), 15 - Color(0xffff349d), 16 - ], 9 + stops: [0, 1], 10 + colors: [Color(0xffff97cd), Color(0xffff349d)], 17 11 ); 18 12 19 13 static const linear2 = LinearGradient( 20 14 begin: Alignment(-1.3914, 1.0057999999999998), 21 15 end: Alignment(2.0484, -1.3258), 22 - stops: [ 23 - 0, 24 - 1, 25 - ], 26 - colors: [ 27 - Color(0xffffcee2), 28 - Color(0xfff99bb1), 29 - ], 16 + stops: [0, 1], 17 + colors: [Color(0xffffcee2), Color(0xfff99bb1)], 30 18 ); 31 19 32 20 static const gradientLinearGlass = LinearGradient( 33 21 begin: Alignment.topLeft, 34 22 end: Alignment(1, 1.0028000000000001), 35 - stops: [ 36 - 0, 37 - 0.37, 38 - 0.72, 39 - 1, 40 - ], 23 + stops: [0, 0.37, 0.72, 1], 41 24 colors: [ 42 25 Color(0x4dffffff), 43 26 Color(0x26ffffff), ··· 49 32 static const gradientLinearSecondaryGradient = LinearGradient( 50 33 begin: Alignment(-0.6918, 1), 51 34 end: Alignment(0.679, -1), 52 - stops: [ 53 - 0, 54 - 1, 55 - ], 56 - colors: [ 57 - Color(0xffffcee2), 58 - Color(0xfff99bb1), 59 - ], 35 + stops: [0, 1], 36 + colors: [Color(0xffffcee2), Color(0xfff99bb1)], 60 37 ); 61 38 62 39 static const gradientLinearPrimaryGradient = LinearGradient( 63 40 begin: Alignment(-0.28200000000000003, 1), 64 41 end: Alignment(0.28200000000000003, -1), 65 - stops: [ 66 - 0, 67 - 1, 68 - ], 69 - colors: [ 70 - Color(0xffff97cd), 71 - Color(0xffff349d), 72 - ], 42 + stops: [0, 1], 43 + colors: [Color(0xffff97cd), Color(0xffff349d)], 73 44 ); 74 45 75 46 static const gradientLinearGr4 = LinearGradient( 76 47 begin: Alignment.topLeft, 77 48 end: Alignment.bottomRight, 78 - stops: [ 79 - 0, 80 - 0.2822, 81 - 1, 82 - ], 83 - colors: [ 84 - Color(0xffff7f65), 85 - Color(0xfff63d68), 86 - Color(0xffffd9d1), 87 - ], 49 + stops: [0, 0.2822, 1], 50 + colors: [Color(0xffff7f65), Color(0xfff63d68), Color(0xffffd9d1)], 88 51 ); 89 52 90 53 static const gradientLinearGr5 = LinearGradient( 91 54 begin: Alignment.topLeft, 92 55 end: Alignment(0.6112, 0.1976), 93 - stops: [ 94 - 0, 95 - 0.5312, 96 - 1, 97 - ], 98 - colors: [ 99 - Color(0xfffbb1c3), 100 - Color(0xff2834e7), 101 - Color(0xff000000), 102 - ], 56 + stops: [0, 0.5312, 1], 57 + colors: [Color(0xfffbb1c3), Color(0xff2834e7), Color(0xff000000)], 103 58 ); 104 59 105 60 static const gradientLinearGr6 = LinearGradient( 106 61 begin: Alignment.topLeft, 107 62 end: Alignment(0.6112, 0.1976), 108 - stops: [ 109 - 0, 110 - 0.5312, 111 - 1, 112 - ], 113 - colors: [ 114 - Color(0xffffd9d1), 115 - Color(0xff7042d2), 116 - Color(0xff000000), 117 - ], 63 + stops: [0, 0.5312, 1], 64 + colors: [Color(0xffffd9d1), Color(0xff7042d2), Color(0xff000000)], 118 65 ); 119 66 120 67 static const gradientLinearGr7 = LinearGradient( 121 68 begin: Alignment.topLeft, 122 69 end: Alignment(0.6112, 0.1976), 123 - stops: [ 124 - 0, 125 - 0.5312, 126 - 1, 127 - ], 128 - colors: [ 129 - Color(0xff91f3f3), 130 - Color(0xff4648ff), 131 - Color(0xff000000), 132 - ], 70 + stops: [0, 0.5312, 1], 71 + colors: [Color(0xff91f3f3), Color(0xff4648ff), Color(0xff000000)], 133 72 ); 134 73 135 74 static const gradientLinearGr8 = LinearGradient( 136 75 begin: Alignment.topLeft, 137 76 end: Alignment(0.5524, 0.4710000000000001), 138 - stops: [ 139 - 0, 140 - 0.2822, 141 - 1, 142 - ], 143 - colors: [ 144 - Color(0xffffccc1), 145 - Color(0xfff63d68), 146 - Color(0xff000000), 147 - ], 77 + stops: [0, 0.2822, 1], 78 + colors: [Color(0xffffccc1), Color(0xfff63d68), Color(0xff000000)], 148 79 ); 149 80 150 81 static const gradientGreyGrey1 = LinearGradient( 151 82 begin: Alignment.topLeft, 152 83 end: Alignment.bottomRight, 153 - stops: [ 154 - 0, 155 - 1, 156 - ], 157 - colors: [ 158 - Color(0xff16171f), 159 - Color(0xff000000), 160 - ], 84 + stops: [0, 1], 85 + colors: [Color(0xff16171f), Color(0xff000000)], 161 86 ); 162 87 163 88 static const gradientGreyGrey2 = LinearGradient( 164 89 begin: Alignment.topLeft, 165 90 end: Alignment.bottomRight, 166 - stops: [ 167 - 0, 168 - 1, 169 - ], 170 - colors: [ 171 - Color(0xff20212b), 172 - Color(0xff16171f), 173 - ], 91 + stops: [0, 1], 92 + colors: [Color(0xff20212b), Color(0xff16171f)], 174 93 ); 175 94 176 95 static const gradientGreyGrey3 = LinearGradient( 177 96 begin: Alignment.topLeft, 178 97 end: Alignment.bottomRight, 179 - stops: [ 180 - 0, 181 - 1, 182 - ], 183 - colors: [ 184 - Color(0xff373946), 185 - Color(0xff16171f), 186 - ], 98 + stops: [0, 1], 99 + colors: [Color(0xff373946), Color(0xff16171f)], 187 100 ); 188 101 189 102 static const gradientGreyGrey4 = LinearGradient( 190 103 begin: Alignment.topLeft, 191 104 end: Alignment.bottomRight, 192 - stops: [ 193 - 0, 194 - 1, 195 - ], 196 - colors: [ 197 - Color(0xff4d4f60), 198 - Color(0xff292a36), 199 - ], 105 + stops: [0, 1], 106 + colors: [Color(0xff4d4f60), Color(0xff292a36)], 200 107 ); 201 108 202 109 static const backgroundLinearGlass = LinearGradient( 203 110 begin: Alignment(-0.8200000000000001, -0.8), 204 111 end: Alignment(0.72, 0.8200000000000001), 205 - stops: [ 206 - 0, 207 - 0.3702, 208 - 0.7212, 209 - 1, 210 - ], 112 + stops: [0, 0.3702, 0.7212, 1], 211 113 colors: [ 212 114 Color(0x4dffffff), 213 115 Color(0x26ffffff), ··· 220 122 begin: Alignment(-0.8200000000000001, -0.8), 221 123 end: Alignment(0.72, 0.8200000000000001), 222 124 transform: GradientRotation(45 * (math.pi / 180)), 223 - stops: [ 224 - 0, 225 - 0.3702, 226 - 0.7212, 227 - 1, 228 - ], 125 + stops: [0, 0.3702, 0.7212, 1], 229 126 colors: [ 230 127 Color(0x3FFFFFFF), // was 6 * 5, now 9 * 7 231 128 Color(0x1CFFFFFF), // was 3 * 5, now 4 * 7 ··· 237 134 static const glassStrokeLight = LinearGradient( 238 135 begin: Alignment(-0.8200000000000001, -0.8), 239 136 end: Alignment(0.72, 0.8200000000000001), 240 - stops: [ 241 - 0, 242 - 0.3702, 243 - 0.7212, 244 - 1, 245 - ], 137 + stops: [0, 0.3702, 0.7212, 1], 246 138 colors: [ 247 139 Color(0x4d000000), 248 140 Color(0x26000000), ··· 255 147 begin: Alignment(-0.8200000000000001, -0.8), 256 148 end: Alignment(0.72, 0.8200000000000001), 257 149 transform: GradientRotation(45 * (math.pi / 180)), 258 - stops: [ 259 - 0, 260 - 0.3702, 261 - 0.7212, 262 - 1, 263 - ], 150 + stops: [0, 0.3702, 0.7212, 1], 264 151 colors: [ 265 152 Color(0xFF313131), 266 153 Color(0xFF232323), ··· 273 160 begin: Alignment(-0.8200000000000001, -0.8), 274 161 end: Alignment(0.72, 0.8200000000000001), 275 162 transform: GradientRotation(45 * (math.pi / 180)), 276 - stops: [ 277 - 0, 278 - 0.7212, 279 - 1, 280 - ], 281 - colors: [ 282 - Color(0xFF6F6F6F), 283 - Color(0xFF3D3D3D), 284 - Color(0xFF6F6F6F), 285 - ], 163 + stops: [0, 0.7212, 1], 164 + colors: [Color(0xFF6F6F6F), Color(0xFF3D3D3D), Color(0xFF6F6F6F)], 286 165 ); 287 166 288 167 static const lightStroke = LinearGradient( 289 168 begin: Alignment(-0.8200000000000001, -0.8), 290 169 end: Alignment(0.72, 0.8200000000000001), 291 170 transform: GradientRotation(45 * (math.pi / 180)), 292 - stops: [ 293 - 0, 294 - 0.3702, 295 - 0.7212, 296 - 1, 297 - ], 171 + stops: [0, 0.3702, 0.7212, 1], 298 172 colors: [ 299 173 Color(0xFFFEFEFE), 300 174 Color(0xFFFCFCFC), ··· 307 181 begin: Alignment(-0.8200000000000001, -0.8), 308 182 end: Alignment(0.72, 0.8200000000000001), 309 183 transform: GradientRotation(45 * (math.pi / 180)), 310 - stops: [ 311 - 0, 312 - 0.7212, 313 - 1, 314 - ], 315 - colors: [ 316 - Color(0xFFB0B0B0), 317 - Color(0xFFD2D2D2), 318 - Color(0xFFB0B0B0), 319 - ], 184 + stops: [0, 0.7212, 1], 185 + colors: [Color(0xFFB0B0B0), Color(0xFFD2D2D2), Color(0xFFB0B0B0)], 320 186 ); 321 187 322 188 static const green = LinearGradient(
+45 -135
lib/src/core/design_system/tokens/typography.dart
··· 9 9 decoration: TextDecoration.none, 10 10 letterSpacing: -4.48, 11 11 leadingDistribution: TextLeadingDistribution.even, 12 - fontVariations: [ 13 - FontVariation('wght', 700), 14 - ], 12 + fontVariations: [FontVariation('wght', 700)], 15 13 ); 16 14 17 15 static const displayXxlMedium = TextStyle( ··· 23 21 decoration: TextDecoration.none, 24 22 letterSpacing: -4.48, 25 23 leadingDistribution: TextLeadingDistribution.even, 26 - fontVariations: [ 27 - FontVariation('wght', 600), 28 - ], 24 + fontVariations: [FontVariation('wght', 600)], 29 25 ); 30 26 31 27 static const displayXxlLight = TextStyle( ··· 37 33 decoration: TextDecoration.none, 38 34 letterSpacing: -4.48, 39 35 leadingDistribution: TextLeadingDistribution.even, 40 - fontVariations: [ 41 - FontVariation('wght', 300), 42 - ], 36 + fontVariations: [FontVariation('wght', 300)], 43 37 ); 44 38 45 39 static const displayXlLight = TextStyle( ··· 51 45 decoration: TextDecoration.none, 52 46 letterSpacing: -3.84, 53 47 leadingDistribution: TextLeadingDistribution.even, 54 - fontVariations: [ 55 - FontVariation('wght', 300), 56 - ], 48 + fontVariations: [FontVariation('wght', 300)], 57 49 ); 58 50 59 51 static const displayXlMedium = TextStyle( ··· 65 57 decoration: TextDecoration.none, 66 58 letterSpacing: -3.84, 67 59 leadingDistribution: TextLeadingDistribution.even, 68 - fontVariations: [ 69 - FontVariation('wght', 600), 70 - ], 60 + fontVariations: [FontVariation('wght', 600)], 71 61 ); 72 62 73 63 static const displayXlBold = TextStyle( ··· 79 69 decoration: TextDecoration.none, 80 70 letterSpacing: -3.84, 81 71 leadingDistribution: TextLeadingDistribution.even, 82 - fontVariations: [ 83 - FontVariation('wght', 700), 84 - ], 72 + fontVariations: [FontVariation('wght', 700)], 85 73 ); 86 74 87 75 static const displayLargeThin = TextStyle( ··· 93 81 decoration: TextDecoration.none, 94 82 letterSpacing: -3.52, 95 83 leadingDistribution: TextLeadingDistribution.even, 96 - fontVariations: [ 97 - FontVariation('wght', 300), 98 - ], 84 + fontVariations: [FontVariation('wght', 300)], 99 85 ); 100 86 101 87 static const displayLargeMedium = TextStyle( ··· 107 93 decoration: TextDecoration.none, 108 94 letterSpacing: -3.52, 109 95 leadingDistribution: TextLeadingDistribution.even, 110 - fontVariations: [ 111 - FontVariation('wght', 600), 112 - ], 96 + fontVariations: [FontVariation('wght', 600)], 113 97 ); 114 98 115 99 static const displayLargeBold = TextStyle( ··· 121 105 decoration: TextDecoration.none, 122 106 letterSpacing: -3.52, 123 107 leadingDistribution: TextLeadingDistribution.even, 124 - fontVariations: [ 125 - FontVariation('wght', 700), 126 - ], 108 + fontVariations: [FontVariation('wght', 700)], 127 109 ); 128 110 129 111 static const displayMediumThin = TextStyle( ··· 135 117 decoration: TextDecoration.none, 136 118 letterSpacing: -2.88, 137 119 leadingDistribution: TextLeadingDistribution.even, 138 - fontVariations: [ 139 - FontVariation('wght', 300), 140 - ], 120 + fontVariations: [FontVariation('wght', 300)], 141 121 ); 142 122 143 123 static const displayMediumMedium = TextStyle( ··· 149 129 decoration: TextDecoration.none, 150 130 letterSpacing: -2.88, 151 131 leadingDistribution: TextLeadingDistribution.even, 152 - fontVariations: [ 153 - FontVariation('wght', 600), 154 - ], 132 + fontVariations: [FontVariation('wght', 600)], 155 133 ); 156 134 157 135 static const displayMediumBold = TextStyle( ··· 163 141 decoration: TextDecoration.none, 164 142 letterSpacing: -2.88, 165 143 leadingDistribution: TextLeadingDistribution.even, 166 - fontVariations: [ 167 - FontVariation('wght', 700), 168 - ], 144 + fontVariations: [FontVariation('wght', 700)], 169 145 ); 170 146 171 147 static const displaySmallThin = TextStyle( ··· 177 153 decoration: TextDecoration.none, 178 154 letterSpacing: -1.92, 179 155 leadingDistribution: TextLeadingDistribution.even, 180 - fontVariations: [ 181 - FontVariation('wght', 300), 182 - ], 156 + fontVariations: [FontVariation('wght', 300)], 183 157 ); 184 158 185 159 static const displaySmallMedium = TextStyle( ··· 191 165 decoration: TextDecoration.none, 192 166 letterSpacing: -1.92, 193 167 leadingDistribution: TextLeadingDistribution.even, 194 - fontVariations: [ 195 - FontVariation('wght', 600), 196 - ], 168 + fontVariations: [FontVariation('wght', 600)], 197 169 ); 198 170 199 171 static const displaySmallBold = TextStyle( ··· 205 177 decoration: TextDecoration.none, 206 178 letterSpacing: -1.92, 207 179 leadingDistribution: TextLeadingDistribution.even, 208 - fontVariations: [ 209 - FontVariation('wght', 700), 210 - ], 180 + fontVariations: [FontVariation('wght', 700)], 211 181 ); 212 182 213 183 static const headlineXlThin = TextStyle( ··· 219 189 decoration: TextDecoration.none, 220 190 letterSpacing: -1.68, 221 191 leadingDistribution: TextLeadingDistribution.even, 222 - fontVariations: [ 223 - FontVariation('wght', 300), 224 - ], 192 + fontVariations: [FontVariation('wght', 300)], 225 193 ); 226 194 227 195 static const headlineXlMedium = TextStyle( ··· 233 201 decoration: TextDecoration.none, 234 202 letterSpacing: -1.68, 235 203 leadingDistribution: TextLeadingDistribution.even, 236 - fontVariations: [ 237 - FontVariation('wght', 500), 238 - ], 204 + fontVariations: [FontVariation('wght', 500)], 239 205 ); 240 206 241 207 static const headlineXlBold = TextStyle( ··· 247 213 decoration: TextDecoration.none, 248 214 letterSpacing: -1.68, 249 215 leadingDistribution: TextLeadingDistribution.even, 250 - fontVariations: [ 251 - FontVariation('wght', 700), 252 - ], 216 + fontVariations: [FontVariation('wght', 700)], 253 217 ); 254 218 255 219 static const headlineLargeThin = TextStyle( ··· 261 225 decoration: TextDecoration.none, 262 226 letterSpacing: -1.44, 263 227 leadingDistribution: TextLeadingDistribution.even, 264 - fontVariations: [ 265 - FontVariation('wght', 300), 266 - ], 228 + fontVariations: [FontVariation('wght', 300)], 267 229 ); 268 230 269 231 static const headlineLargeMedium = TextStyle( ··· 275 237 decoration: TextDecoration.none, 276 238 letterSpacing: -1.44, 277 239 leadingDistribution: TextLeadingDistribution.even, 278 - fontVariations: [ 279 - FontVariation('wght', 500), 280 - ], 240 + fontVariations: [FontVariation('wght', 500)], 281 241 ); 282 242 283 243 static const headlineLargeBold = TextStyle( ··· 289 249 decoration: TextDecoration.none, 290 250 letterSpacing: -1.44, 291 251 leadingDistribution: TextLeadingDistribution.even, 292 - fontVariations: [ 293 - FontVariation('wght', 700), 294 - ], 252 + fontVariations: [FontVariation('wght', 700)], 295 253 ); 296 254 297 255 static const headlineMediumThin = TextStyle( ··· 303 261 decoration: TextDecoration.none, 304 262 letterSpacing: -0.64, 305 263 leadingDistribution: TextLeadingDistribution.even, 306 - fontVariations: [ 307 - FontVariation('wght', 300), 308 - ], 264 + fontVariations: [FontVariation('wght', 300)], 309 265 ); 310 266 311 267 static const headlineMediumMedium = TextStyle( ··· 317 273 decoration: TextDecoration.none, 318 274 letterSpacing: -0.64, 319 275 leadingDistribution: TextLeadingDistribution.even, 320 - fontVariations: [ 321 - FontVariation('wght', 500), 322 - ], 276 + fontVariations: [FontVariation('wght', 500)], 323 277 ); 324 278 325 279 static const headlineMediumBold = TextStyle( ··· 331 285 decoration: TextDecoration.none, 332 286 letterSpacing: -0.64, 333 287 leadingDistribution: TextLeadingDistribution.even, 334 - fontVariations: [ 335 - FontVariation('wght', 700), 336 - ], 288 + fontVariations: [FontVariation('wght', 700)], 337 289 ); 338 290 339 291 static const headlineSmallThin = TextStyle( ··· 345 297 decoration: TextDecoration.none, 346 298 letterSpacing: -0.48, 347 299 leadingDistribution: TextLeadingDistribution.even, 348 - fontVariations: [ 349 - FontVariation('wght', 300), 350 - ], 300 + fontVariations: [FontVariation('wght', 300)], 351 301 ); 352 302 353 303 static const headlineSmallMedium = TextStyle( ··· 359 309 decoration: TextDecoration.none, 360 310 letterSpacing: -0.48, 361 311 leadingDistribution: TextLeadingDistribution.even, 362 - fontVariations: [ 363 - FontVariation('wght', 500), 364 - ], 312 + fontVariations: [FontVariation('wght', 500)], 365 313 ); 366 314 367 315 static const headlineSmallBold = TextStyle( ··· 373 321 decoration: TextDecoration.none, 374 322 letterSpacing: -0.48, 375 323 leadingDistribution: TextLeadingDistribution.even, 376 - fontVariations: [ 377 - FontVariation('wght', 700), 378 - ], 324 + fontVariations: [FontVariation('wght', 700)], 379 325 ); 380 326 381 327 static const textSmallThin = TextStyle( ··· 387 333 decoration: TextDecoration.none, 388 334 letterSpacing: 0, 389 335 leadingDistribution: TextLeadingDistribution.even, 390 - fontVariations: [ 391 - FontVariation('wght', 300), 392 - ], 336 + fontVariations: [FontVariation('wght', 300)], 393 337 ); 394 338 395 339 static const textSmallMedium = TextStyle( ··· 401 345 decoration: TextDecoration.none, 402 346 letterSpacing: 0, 403 347 leadingDistribution: TextLeadingDistribution.even, 404 - fontVariations: [ 405 - FontVariation('wght', 500), 406 - ], 348 + fontVariations: [FontVariation('wght', 500)], 407 349 ); 408 350 409 351 static const textSmallBold = TextStyle( ··· 415 357 decoration: TextDecoration.none, 416 358 letterSpacing: 0, 417 359 leadingDistribution: TextLeadingDistribution.even, 418 - fontVariations: [ 419 - FontVariation('wght', 700), 420 - ], 360 + fontVariations: [FontVariation('wght', 700)], 421 361 ); 422 362 423 363 static const textExtraSmallThin = TextStyle( ··· 429 369 decoration: TextDecoration.none, 430 370 letterSpacing: 0, 431 371 leadingDistribution: TextLeadingDistribution.even, 432 - fontVariations: [ 433 - FontVariation('wght', 300), 434 - ], 372 + fontVariations: [FontVariation('wght', 300)], 435 373 ); 436 374 437 375 static const textExtraSmallMedium = TextStyle( ··· 443 381 decoration: TextDecoration.none, 444 382 letterSpacing: 0, 445 383 leadingDistribution: TextLeadingDistribution.even, 446 - fontVariations: [ 447 - FontVariation('wght', 500), 448 - ], 384 + fontVariations: [FontVariation('wght', 500)], 449 385 ); 450 386 451 387 static const textExtraSmallBold = TextStyle( ··· 457 393 decoration: TextDecoration.none, 458 394 letterSpacing: 0, 459 395 leadingDistribution: TextLeadingDistribution.even, 460 - fontVariations: [ 461 - FontVariation('wght', 700), 462 - ], 396 + fontVariations: [FontVariation('wght', 700)], 463 397 ); 464 398 465 399 static const textLargeThin = TextStyle( ··· 471 405 decoration: TextDecoration.none, 472 406 letterSpacing: 0, 473 407 leadingDistribution: TextLeadingDistribution.even, 474 - fontVariations: [ 475 - FontVariation('wght', 300), 476 - ], 408 + fontVariations: [FontVariation('wght', 300)], 477 409 ); 478 410 479 411 static const textLargeMedium = TextStyle( ··· 485 417 decoration: TextDecoration.none, 486 418 letterSpacing: 0, 487 419 leadingDistribution: TextLeadingDistribution.even, 488 - fontVariations: [ 489 - FontVariation('wght', 500), 490 - ], 420 + fontVariations: [FontVariation('wght', 500)], 491 421 ); 492 422 493 423 static const textLargeBold = TextStyle( ··· 499 429 decoration: TextDecoration.none, 500 430 letterSpacing: 0, 501 431 leadingDistribution: TextLeadingDistribution.even, 502 - fontVariations: [ 503 - FontVariation('wght', 700), 504 - ], 432 + fontVariations: [FontVariation('wght', 700)], 505 433 ); 506 434 507 435 static const textMediumThin = TextStyle( ··· 513 441 decoration: TextDecoration.none, 514 442 letterSpacing: 0, 515 443 leadingDistribution: TextLeadingDistribution.even, 516 - fontVariations: [ 517 - FontVariation('wght', 300), 518 - ], 444 + fontVariations: [FontVariation('wght', 300)], 519 445 ); 520 446 521 447 static const textMediumMedium = TextStyle( ··· 527 453 decoration: TextDecoration.none, 528 454 letterSpacing: 0, 529 455 leadingDistribution: TextLeadingDistribution.even, 530 - fontVariations: [ 531 - FontVariation('wght', 500), 532 - ], 456 + fontVariations: [FontVariation('wght', 500)], 533 457 ); 534 458 535 459 static const textMediumBold = TextStyle( ··· 541 465 decoration: TextDecoration.none, 542 466 letterSpacing: 0, 543 467 leadingDistribution: TextLeadingDistribution.even, 544 - fontVariations: [ 545 - FontVariation('wght', 700), 546 - ], 468 + fontVariations: [FontVariation('wght', 700)], 547 469 ); 548 470 549 471 static const textXlThin = TextStyle( ··· 555 477 decoration: TextDecoration.none, 556 478 letterSpacing: 0, 557 479 leadingDistribution: TextLeadingDistribution.even, 558 - fontVariations: [ 559 - FontVariation('wght', 300), 560 - ], 480 + fontVariations: [FontVariation('wght', 300)], 561 481 ); 562 482 563 483 static const textXlMedium = TextStyle( ··· 569 489 decoration: TextDecoration.none, 570 490 letterSpacing: 0, 571 491 leadingDistribution: TextLeadingDistribution.even, 572 - fontVariations: [ 573 - FontVariation('wght', 500), 574 - ], 492 + fontVariations: [FontVariation('wght', 500)], 575 493 ); 576 494 577 495 static const textXlBold = TextStyle( ··· 583 501 decoration: TextDecoration.none, 584 502 letterSpacing: 0, 585 503 leadingDistribution: TextLeadingDistribution.even, 586 - fontVariations: [ 587 - FontVariation('wght', 700), 588 - ], 504 + fontVariations: [FontVariation('wght', 700)], 589 505 ); 590 506 591 507 static const bannerLMedium = TextStyle( ··· 597 513 decoration: TextDecoration.none, 598 514 letterSpacing: -16, 599 515 leadingDistribution: TextLeadingDistribution.even, 600 - fontVariations: [ 601 - FontVariation('wght', 600), 602 - ], 516 + fontVariations: [FontVariation('wght', 600)], 603 517 ); 604 518 605 519 static const bannerMMedium = TextStyle( ··· 611 525 decoration: TextDecoration.none, 612 526 letterSpacing: -12, 613 527 leadingDistribution: TextLeadingDistribution.even, 614 - fontVariations: [ 615 - FontVariation('wght', 600), 616 - ], 528 + fontVariations: [FontVariation('wght', 600)], 617 529 ); 618 530 619 531 static const metaTitle = TextStyle( ··· 624 536 decoration: TextDecoration.none, 625 537 letterSpacing: -4.48, 626 538 leadingDistribution: TextLeadingDistribution.even, 627 - fontVariations: [ 628 - FontVariation('wght', 590), 629 - ], 539 + fontVariations: [FontVariation('wght', 590)], 630 540 ); 631 541 632 542 AppTypography._();
+2 -7
lib/src/core/media/create_media_actions.dart
··· 103 103 .openStoryImageEditor(context, pickedImage); 104 104 if (editedImage != null && context.mounted) { 105 105 // Post directly 106 - await context.router.push( 107 - StoryPostRoute(imageFile: editedImage), 108 - ); 106 + await context.router.push(StoryPostRoute(imageFile: editedImage)); 109 107 } 110 108 } 111 109 } else { ··· 113 111 final pickedImages = await ImagePicker().pickMultiImage(limit: 12); 114 112 if (context.mounted && pickedImages.isNotEmpty) { 115 113 await context.router.push( 116 - ImageReviewRoute( 117 - imageFiles: pickedImages, 118 - storyMode: storyMode, 119 - ), 114 + ImageReviewRoute(imageFiles: pickedImages, storyMode: storyMode), 120 115 ); 121 116 } 122 117 }
+4 -16
lib/src/core/network/atproto/data/adapters/bsky/feed_adapter.dart
··· 269 269 final text = record['text'] as String? ?? ''; 270 270 final facets = record['facets'] as List<dynamic>? ?? []; 271 271 272 - record['caption'] = { 273 - 'text': text, 274 - 'facets': facets, 275 - }; 272 + record['caption'] = {'text': text, 'facets': facets}; 276 273 277 274 record[r'$type'] = 'so.sprk.feed.post'; 278 275 ··· 403 400 switch (media) { 404 401 case MediaImage(:final image, :final alt): 405 402 // Convert single Spark image to Bluesky embed images 406 - final bskyImage = EmbedImagesImage( 407 - alt: alt ?? '', 408 - image: image, 409 - ); 403 + final bskyImage = EmbedImagesImage(alt: alt ?? '', image: image); 410 404 return UFeedPostEmbed.embedImages( 411 405 data: EmbedImages(images: [bskyImage]), 412 406 ); ··· 462 456 return FeedPostRecord( 463 457 text: text, 464 458 createdAt: createdAt, 465 - reply: ReplyRef( 466 - root: reply.root, 467 - parent: reply.parent, 468 - ), 459 + reply: ReplyRef(root: reply.root, parent: reply.parent), 469 460 embed: embed, 470 461 ); 471 462 } ··· 642 633 643 634 /// Check if a FeedViewPost is a reply 644 635 bool _feedViewPostIsReply(FeedViewPost feedViewPost) { 645 - return feedViewPost.map( 646 - post: (p) => p.reply != null, 647 - reply: (r) => true, 648 - ); 636 + return feedViewPost.map(post: (p) => p.reply != null, reply: (r) => true); 649 637 } 650 638 651 639 /// Process raw Bluesky FeedViewPost list and convert to Spark format
+16 -34
lib/src/core/network/atproto/data/models/feed_models.dart
··· 44 44 @freezed 45 45 abstract class GeneratorViewerState with _$GeneratorViewerState { 46 46 @JsonSerializable(explicitToJson: true) 47 - const factory GeneratorViewerState({ 48 - @AtUriConverter() AtUri? like, 49 - }) = _GeneratorViewerState; 47 + const factory GeneratorViewerState({@AtUriConverter() AtUri? like}) = 48 + _GeneratorViewerState; 50 49 const GeneratorViewerState._(); 51 50 52 51 factory GeneratorViewerState.fromJson(Map<String, dynamic> json) => ··· 95 94 @freezed 96 95 abstract class SkeletonFeedPost with _$SkeletonFeedPost { 97 96 @JsonSerializable(explicitToJson: true) 98 - const factory SkeletonFeedPost({ 99 - @AtUriConverter() required AtUri uri, 100 - }) = _SkeletonFeedPost; 97 + const factory SkeletonFeedPost({@AtUriConverter() required AtUri uri}) = 98 + _SkeletonFeedPost; 101 99 102 100 factory SkeletonFeedPost.fromJson(Map<String, dynamic> json) => 103 101 _$SkeletonFeedPostFromJson(json); ··· 124 122 125 123 @FreezedUnionValue('so.sprk.feed.defs#feedPostView') 126 124 @JsonSerializable(explicitToJson: true) 127 - const factory FeedViewPost.post({ 128 - required PostView post, 129 - ReplyRef? reply, 130 - }) = FeedViewPostPost; 125 + const factory FeedViewPost.post({required PostView post, ReplyRef? reply}) = 126 + FeedViewPostPost; 131 127 132 128 @FreezedUnionValue('so.sprk.feed.defs#feedReplyView') 133 129 @JsonSerializable(explicitToJson: true) ··· 142 138 PostView? get asPost => mapOrNull(post: (p) => p.post); 143 139 ReplyView? get asReply => mapOrNull(reply: (r) => r.reply); 144 140 145 - ProfileViewBasic get author => map( 146 - post: (p) => p.post.author, 147 - reply: (r) => r.reply.author, 148 - ); 141 + ProfileViewBasic get author => 142 + map(post: (p) => p.post.author, reply: (r) => r.reply.author); 149 143 150 - AtUri get uri => map( 151 - post: (p) => p.post.uri, 152 - reply: (r) => r.reply.uri, 153 - ); 144 + AtUri get uri => map(post: (p) => p.post.uri, reply: (r) => r.reply.uri); 154 145 155 - String get cid => map( 156 - post: (p) => p.post.cid, 157 - reply: (r) => r.reply.cid, 158 - ); 146 + String get cid => map(post: (p) => p.post.cid, reply: (r) => r.reply.cid); 159 147 160 - MediaView? get media => map( 161 - post: (p) => p.post.displayMedia, 162 - reply: (r) => r.reply.media, 163 - ); 148 + MediaView? get media => 149 + map(post: (p) => p.post.displayMedia, reply: (r) => r.reply.media); 164 150 165 151 ViewerState? get viewerState => mapOrNull(post: (p) => p.post.viewer); 166 152 ReplyViewerState? get replyViewerState => 167 153 mapOrNull(reply: (r) => r.reply.viewer); 168 154 169 - String get displayText => map( 170 - post: (p) => p.post.displayText, 171 - reply: (r) => r.reply.displayText, 172 - ); 155 + String get displayText => 156 + map(post: (p) => p.post.displayText, reply: (r) => r.reply.displayText); 173 157 174 158 List<Facet> get displayFacets => map( 175 159 post: (p) => p.post.displayFacets, ··· 180 164 @freezed 181 165 abstract class FeedView with _$FeedView { 182 166 @JsonSerializable(explicitToJson: true) 183 - const factory FeedView({ 184 - required List<FeedViewPost> feed, 185 - String? cursor, 186 - }) = _FeedView; 167 + const factory FeedView({required List<FeedViewPost> feed, String? cursor}) = 168 + _FeedView; 187 169 const FeedView._(); 188 170 189 171 factory FeedView.fromJson(Map<String, dynamic> json) =>
+3 -6
lib/src/core/network/atproto/data/models/labeler_models.dart
··· 23 23 enum Blurs { 24 24 content('content'), 25 25 media('media'), 26 - none('none') 27 - ; 26 + none('none'); 28 27 29 28 final String value; 30 29 const Blurs(this.value); ··· 40 39 enum Severity { 41 40 alert('alert'), 42 41 inform('inform'), 43 - none('none') 44 - ; 42 + none('none'); 45 43 46 44 final String value; 47 45 const Severity(this.value); ··· 57 55 enum Setting { 58 56 hide('hide'), 59 57 warn('warn'), 60 - ignore('ignore') 61 - ; 58 + ignore('ignore'); 62 59 63 60 final String value; 64 61 const Setting(this.value);
+4 -6
lib/src/core/network/atproto/data/models/notification_models.dart
··· 45 45 @freezed 46 46 abstract class UnreadCountResponse with _$UnreadCountResponse { 47 47 @JsonSerializable(explicitToJson: true) 48 - const factory UnreadCountResponse({ 49 - required int count, 50 - }) = _UnreadCountResponse; 48 + const factory UnreadCountResponse({required int count}) = 49 + _UnreadCountResponse; 51 50 const UnreadCountResponse._(); 52 51 53 52 factory UnreadCountResponse.fromJson(Map<String, dynamic> json) => ··· 57 56 @freezed 58 57 abstract class UpdateSeenRequest with _$UpdateSeenRequest { 59 58 @JsonSerializable(explicitToJson: true) 60 - const factory UpdateSeenRequest({ 61 - required DateTime seenAt, 62 - }) = _UpdateSeenRequest; 59 + const factory UpdateSeenRequest({required DateTime seenAt}) = 60 + _UpdateSeenRequest; 63 61 const UpdateSeenRequest._(); 64 62 65 63 factory UpdateSeenRequest.fromJson(Map<String, dynamic> json) =>
+10 -18
lib/src/core/network/atproto/data/models/pref_models.dart
··· 7 7 @freezed 8 8 abstract class Preferences with _$Preferences { 9 9 @JsonSerializable(explicitToJson: true) 10 - factory Preferences({ 11 - required List<Preference> preferences, 12 - }) { 10 + factory Preferences({required List<Preference> preferences}) { 13 11 final contentLabelPrefs = <ContentLabelPref>[]; 14 12 final savedFeeds = <SavedFeed>[]; 15 13 final labelers = <LabelerPrefItem>[]; ··· 123 121 124 122 @FreezedUnionValue('so.sprk.actor.defs#savedFeedsPref') 125 123 @JsonSerializable(explicitToJson: true) 126 - const factory Preference.savedFeedsPref({ 127 - required List<SavedFeed> items, 128 - }) = _SavedFeedsPref; 124 + const factory Preference.savedFeedsPref({required List<SavedFeed> items}) = 125 + _SavedFeedsPref; 129 126 bool isSavedFeedsPref(Preference preference) => 130 127 preference.mapOrNull(savedFeedsPref: (pref) => pref) != null; 131 128 132 129 @FreezedUnionValue('so.sprk.actor.defs#personalDetailsPref') 133 130 @JsonSerializable(explicitToJson: true) 134 - const factory Preference.personalDetailsPref({ 135 - required DateTime? birthDate, 136 - }) = _PersonalDetailsPref; 131 + const factory Preference.personalDetailsPref({required DateTime? birthDate}) = 132 + _PersonalDetailsPref; 137 133 bool isPersonalDetailsPref(Preference preference) => 138 134 preference.mapOrNull(personalDetailsPref: (pref) => pref) != null; 139 135 ··· 161 157 162 158 @FreezedUnionValue('so.sprk.actor.defs#interestsPref') 163 159 @JsonSerializable(explicitToJson: true) 164 - const factory Preference.interestsPref({ 165 - required List<String> tags, 166 - }) = _InterestsPref; 160 + const factory Preference.interestsPref({required List<String> tags}) = 161 + _InterestsPref; 167 162 bool isInterestsPref(Preference preference) => 168 163 preference.mapOrNull(interestsPref: (pref) => pref) != null; 169 164 170 165 @FreezedUnionValue('so.sprk.actor.defs#mutedWordsPref') 171 166 @JsonSerializable(explicitToJson: true) 172 - const factory Preference.mutedWordsPref({ 173 - required List<MutedWord> words, 174 - }) = _MutedWordsPref; 167 + const factory Preference.mutedWordsPref({required List<MutedWord> words}) = 168 + _MutedWordsPref; 175 169 bool isMutedWordsPref(Preference preference) => 176 170 preference.mapOrNull(mutedWordsPref: (pref) => pref) != null; 177 171 ··· 253 247 @freezed 254 248 abstract class LabelerPrefItem with _$LabelerPrefItem { 255 249 @JsonSerializable(explicitToJson: true) 256 - const factory LabelerPrefItem({ 257 - required String did, 258 - }) = _LabelerPrefItem; 250 + const factory LabelerPrefItem({required String did}) = _LabelerPrefItem; 259 251 const LabelerPrefItem._(); 260 252 261 253 factory LabelerPrefItem.fromJson(Map<String, dynamic> json) =>
+6 -15
lib/src/core/network/atproto/data/models/record_models.dart
··· 124 124 // Spark media types (new schema) 125 125 @FreezedUnionValue('so.sprk.media.video') 126 126 @JsonSerializable(explicitToJson: true) 127 - const factory Media.video({ 128 - required Blob video, 129 - String? alt, 130 - }) = MediaVideo; 127 + const factory Media.video({required Blob video, String? alt}) = MediaVideo; 131 128 132 129 @FreezedUnionValue('so.sprk.media.image') 133 130 @JsonSerializable(explicitToJson: true) 134 - const factory Media.image({ 135 - required Blob image, 136 - String? alt, 137 - }) = MediaImage; 131 + const factory Media.image({required Blob image, String? alt}) = MediaImage; 138 132 139 133 @FreezedUnionValue('so.sprk.media.images') 140 134 @JsonSerializable(explicitToJson: true) ··· 143 137 // Bluesky embed types 144 138 @FreezedUnionValue('app.bsky.embed.video') 145 139 @JsonSerializable(explicitToJson: true) 146 - const factory Media.bskyVideo({ 147 - required Blob video, 148 - String? alt, 149 - }) = MediaBskyVideo; 140 + const factory Media.bskyVideo({required Blob video, String? alt}) = 141 + MediaBskyVideo; 150 142 151 143 @FreezedUnionValue('app.bsky.embed.images') 152 144 @JsonSerializable(explicitToJson: true) ··· 167 159 168 160 @FreezedUnionValue('app.bsky.embed.external') 169 161 @JsonSerializable(explicitToJson: true) 170 - const factory Media.bskyExternal({ 171 - required EmbedExternal external, 172 - }) = MediaBskyExternal; 162 + const factory Media.bskyExternal({required EmbedExternal external}) = 163 + MediaBskyExternal; 173 164 174 165 factory Media.fromJson(Map<String, dynamic> json) => _$MediaFromJson(json); 175 166 }
+1 -4
lib/src/core/network/atproto/data/models/sound_models.dart
··· 30 30 @freezed 31 31 abstract class AudioDetails with _$AudioDetails { 32 32 @JsonSerializable(explicitToJson: true) 33 - const factory AudioDetails({ 34 - String? artist, 35 - String? title, 36 - }) = _AudioDetails; 33 + const factory AudioDetails({String? artist, String? title}) = _AudioDetails; 37 34 38 35 factory AudioDetails.fromJson(Map<String, dynamic> json) => 39 36 _$AudioDetailsFromJson(json);
+2 -7
lib/src/core/network/atproto/data/repositories/actor_repository_impl.dart
··· 64 64 jsonDecode(utf8.decode(uint8 as List<int>)) as Map<String, dynamic>, 65 65 ); 66 66 _logger.d('Profile retrieved successfully from Spark'); 67 - return ProfileViewDetailed.fromJson( 68 - result.data as Map<String, dynamic>, 69 - ); 67 + return ProfileViewDetailed.fromJson(result.data as Map<String, dynamic>); 70 68 }); 71 69 } 72 70 ··· 132 130 final clampedLimit = limit.clamp(1, 100); 133 131 final result = await atproto.get( 134 132 NSID.parse('so.sprk.actor.searchActorsTypeahead'), 135 - parameters: { 136 - 'q': query, 137 - 'limit': clampedLimit.toString(), 138 - }, 133 + parameters: {'q': query, 'limit': clampedLimit.toString()}, 139 134 headers: {'atproto-proxy': _client.sprkDid}, 140 135 to: (jsonMap) => jsonMap, 141 136 adaptor: (uint8) =>
+9 -26
lib/src/core/network/atproto/data/repositories/feed_repository_impl.dart
··· 327 327 if (oauthSession == null) { 328 328 throw Exception('No OAuth session available'); 329 329 } 330 - final resultBsky = 331 - await bsky.Bluesky.fromOAuthSession( 332 - oauthSession, 333 - ).feed.getAuthorFeed( 330 + final resultBsky = await bsky.Bluesky.fromOAuthSession(oauthSession) 331 + .feed 332 + .getAuthorFeed( 334 333 actor: actorUri.hostname, 335 334 limit: limit, 336 335 cursor: cursor, ··· 1122 1121 1123 1122 final byteData = await image.toByteData(format: ui.ImageByteFormat.png); 1124 1123 if (byteData == null) { 1125 - _logger.w( 1126 - 'Failed to encode image $imageName with dart:ui codec', 1127 - ); 1124 + _logger.w('Failed to encode image $imageName with dart:ui codec'); 1128 1125 return null; 1129 1126 } 1130 1127 return byteData.buffer.asUint8List(); ··· 1228 1225 Uri.parse( 1229 1226 '${AppConfig.videoServiceUrl}/xrpc/so.sprk.video.getJobStatus', 1230 1227 ).replace( 1231 - queryParameters: { 1232 - 'jobId': responseData['jobStatus']?['jobId'], 1233 - }, 1228 + queryParameters: {'jobId': responseData['jobStatus']?['jobId']}, 1234 1229 ), 1235 1230 headers: { 1236 1231 'Authorization': 'Bearer $serviceToken', ··· 1328 1323 if (linkUrl != null) { 1329 1324 final linkStart = text.isEmpty ? 0 : text.length; 1330 1325 facets = [ 1331 - bskyFeedAdapter.createLinkFacet( 1332 - linkUrl: linkUrl, 1333 - byteStart: linkStart, 1334 - ), 1326 + bskyFeedAdapter.createLinkFacet(linkUrl: linkUrl, byteStart: linkStart), 1335 1327 ]; 1336 1328 } 1337 1329 ··· 1353 1345 } 1354 1346 1355 1347 /// Prepare text for Bluesky post, handling link addition and truncation 1356 - String _prepareTextWithLink({ 1357 - required String text, 1358 - String? linkUrl, 1359 - }) { 1348 + String _prepareTextWithLink({required String text, String? linkUrl}) { 1360 1349 if (linkUrl == null) { 1361 1350 return text; 1362 1351 } ··· 1688 1677 throw Exception('AtProto not initialized'); 1689 1678 } 1690 1679 1691 - final parameters = <String, dynamic>{ 1692 - 'actor': actor, 1693 - 'limit': limit, 1694 - }; 1680 + final parameters = <String, dynamic>{'actor': actor, 'limit': limit}; 1695 1681 1696 1682 if (cursor != null) { 1697 1683 parameters['cursor'] = cursor; ··· 1766 1752 throw Exception('AtProto not initialized'); 1767 1753 } 1768 1754 1769 - final parameters = <String, dynamic>{ 1770 - 'actor': actor, 1771 - 'limit': limit, 1772 - }; 1755 + final parameters = <String, dynamic>{'actor': actor, 'limit': limit}; 1773 1756 1774 1757 if (cursor != null) { 1775 1758 parameters['cursor'] = cursor;
+1 -3
lib/src/core/network/atproto/data/repositories/notification_repository_impl.dart
··· 42 42 throw Exception('AtProto not initialized'); 43 43 } 44 44 45 - final parameters = <String, dynamic>{ 46 - 'limit': limit.toString(), 47 - }; 45 + final parameters = <String, dynamic>{'limit': limit.toString()}; 48 46 if (cursor != null && cursor.isNotEmpty) { 49 47 parameters['cursor'] = cursor; 50 48 }
+1 -3
lib/src/core/network/atproto/data/repositories/repo_repository_impl.dart
··· 280 280 'service: $modServiceDid', 281 281 ); 282 282 283 - final headers = { 284 - 'atproto-proxy': modServiceDid, 285 - }; 283 + final headers = {'atproto-proxy': modServiceDid}; 286 284 287 285 try { 288 286 final response = await atproto.post(
+1 -3
lib/src/core/network/atproto/data/repositories/sound_repository_impl.dart
··· 106 106 throw Exception('AtProto not initialized'); 107 107 } 108 108 109 - final parameters = <String, dynamic>{ 110 - 'limit': limit, 111 - }; 109 + final parameters = <String, dynamic>{'limit': limit}; 112 110 if (cursor != null) { 113 111 parameters['cursor'] = cursor; 114 112 }
+1 -4
lib/src/core/network/atproto/data/repositories/story_repository.dart
··· 23 23 Future< 24 24 ({String? cursor, Map<ProfileViewBasic, List<StoryView>> storiesByAuthor}) 25 25 > 26 - getStoriesTimeline({ 27 - int limit = 30, 28 - String? cursor, 29 - }); 26 + getStoriesTimeline({int limit = 30, String? cursor}); 30 27 31 28 /// Gets story views for a specified list of stories (by AT-URI). 32 29 ///
+1 -4
lib/src/core/network/atproto/data/repositories/story_repository_impl.dart
··· 46 46 Future< 47 47 ({String? cursor, Map<ProfileViewBasic, List<StoryView>> storiesByAuthor}) 48 48 > 49 - getStoriesTimeline({ 50 - int limit = 30, 51 - String? cursor, 52 - }) { 49 + getStoriesTimeline({int limit = 30, String? cursor}) { 53 50 return _client.executeWithRetry(() async { 54 51 if (!_client.authRepository.isAuthenticated) { 55 52 throw Exception('Not authenticated');
+1 -3
lib/src/core/network/messages/data/models/message_models.dart
··· 38 38 @freezed 39 39 abstract class SenderView with _$SenderView { 40 40 @JsonSerializable(explicitToJson: true) 41 - const factory SenderView({ 42 - required String did, 43 - }) = _SenderView; 41 + const factory SenderView({required String did}) = _SenderView; 44 42 const SenderView._(); 45 43 46 44 factory SenderView.fromJson(Map<String, dynamic> json) =>
+10 -22
lib/src/core/network/messages/data/repository/messages_repository_xrpc.dart
··· 27 27 ) async { 28 28 try { 29 29 final token = await _serviceAuthHelper.getServiceToken(nsid); 30 - final url = Uri.parse('$_baseUrl/xrpc/$nsid').replace( 31 - queryParameters: params.isEmpty ? null : params, 32 - ); 30 + final url = Uri.parse( 31 + '$_baseUrl/xrpc/$nsid', 32 + ).replace(queryParameters: params.isEmpty ? null : params); 33 33 34 34 final response = await http.get( 35 35 url, ··· 115 115 116 116 @override 117 117 Future<ConvoView> getConversation(String convoId) async { 118 - final data = await _callQuery( 119 - 'so.sprk.chat.getConvo', 120 - {'convoId': convoId}, 121 - ); 118 + final data = await _callQuery('so.sprk.chat.getConvo', { 119 + 'convoId': convoId, 120 + }); 122 121 123 122 return ConvoView.fromJson(data['convo'] as Map<String, dynamic>); 124 123 } ··· 127 126 Future<ConvoView> getConvoForMembers(List<String> members) async { 128 127 // Build URL with repeated members parameters 129 128 // Need to manually construct query string for repeated params 130 - final baseUri = Uri.parse( 131 - '$_baseUrl/xrpc/so.sprk.chat.getConvoForMembers', 132 - ); 129 + final baseUri = Uri.parse('$_baseUrl/xrpc/so.sprk.chat.getConvoForMembers'); 133 130 final queryParts = members 134 131 .map((m) => 'members=${Uri.encodeComponent(m)}') 135 132 .join('&'); ··· 141 138 142 139 final response = await http.get( 143 140 url, 144 - headers: { 145 - 'Accept': 'application/json', 146 - 'Authorization': 'Bearer $token', 147 - }, 141 + headers: {'Accept': 'application/json', 'Authorization': 'Bearer $token'}, 148 142 ); 149 143 150 144 if (response.statusCode == 200) { ··· 230 224 'value': value, 231 225 }; 232 226 233 - final data = await _callProcedure( 234 - 'so.sprk.chat.removeReaction', 235 - body, 236 - ); 227 + final data = await _callProcedure('so.sprk.chat.removeReaction', body); 237 228 238 229 return MessageView.fromJson(data); 239 230 } 240 231 241 232 @override 242 233 Future<ConvoView> updateRead(String convoId, String messageId) async { 243 - final body = <String, dynamic>{ 244 - 'convoId': convoId, 245 - 'messageId': messageId, 246 - }; 234 + final body = <String, dynamic>{'convoId': convoId, 'messageId': messageId}; 247 235 248 236 final data = await _callProcedure('so.sprk.chat.updateRead', body); 249 237
+10 -21
lib/src/core/pro_image_editor/story_image_editor_configs.dart
··· 69 69 ], 70 70 widgets: MainEditorWidgets( 71 71 removeLayerArea: 72 - ( 73 - removeAreaKey, 74 - editor, 75 - rebuildStream, 76 - isLayerBeingTransformed, 77 - ) => VideoEditorRemoveArea( 78 - removeAreaKey: removeAreaKey, 79 - editor: editor, 80 - rebuildStream: rebuildStream, 81 - isLayerBeingTransformed: isLayerBeingTransformed, 82 - ), 72 + (removeAreaKey, editor, rebuildStream, isLayerBeingTransformed) => 73 + VideoEditorRemoveArea( 74 + removeAreaKey: removeAreaKey, 75 + editor: editor, 76 + rebuildStream: rebuildStream, 77 + isLayerBeingTransformed: isLayerBeingTransformed, 78 + ), 83 79 appBar: (editor, rebuildStream) => null, 84 80 bottomBar: (editor, rebuildStream, key) => ReactiveWidget( 85 81 key: key, ··· 242 238 ), 243 239 blurEditor: BlurEditorConfigs( 244 240 maxBlur: 25, 245 - style: const BlurEditorStyle( 246 - background: AppColors.greyBlack, 247 - ), 241 + style: const BlurEditorStyle(background: AppColors.greyBlack), 248 242 widgets: BlurEditorWidgets( 249 243 appBar: (blurEditor, rebuildStream) => null, 250 244 bottomBar: (editorState, rebuildStream) { ··· 279 273 setLayer: setLayer, 280 274 scrollController: scrollController, 281 275 ), 282 - style: const StickerEditorStyle( 283 - showDragHandle: false, 284 - ), 276 + style: const StickerEditorStyle(showDragHandle: false), 285 277 ), 286 278 i18n: const I18n( 287 279 paintEditor: I18nPaintEditor( 288 280 changeOpacity: 'Opacity', 289 281 lineWidth: 'Thickness', 290 282 ), 291 - textEditor: I18nTextEditor( 292 - backgroundMode: 'Mode', 293 - textAlign: 'Align', 294 - ), 283 + textEditor: I18nTextEditor(backgroundMode: 'Mode', textAlign: 'Align'), 295 284 ), 296 285 ); 297 286 }
+11 -29
lib/src/core/pro_image_editor/ui/story_image_editor_page.dart
··· 22 22 /// - NO crop/rotate tools (to maintain aspect ratio) 23 23 /// - Custom UI matching the app's design language 24 24 class StoryImageEditorPage extends StatefulWidget { 25 - const StoryImageEditorPage({ 26 - required this.imageFile, 27 - super.key, 28 - }); 25 + const StoryImageEditorPage({required this.imageFile, super.key}); 29 26 30 27 /// The source image file to edit. 31 28 final File imageFile; ··· 88 85 // Initialize editor config 89 86 _configs = StoryImageEditorConfigs.build( 90 87 useMaterialDesign: _useMaterialDesign, 91 - imagePreviewBuilder: () => Image.file( 92 - _croppedImageFile!, 93 - fit: BoxFit.cover, 94 - ), 88 + imagePreviewBuilder: () => 89 + Image.file(_croppedImageFile!, fit: BoxFit.cover), 95 90 ); 96 91 97 92 if (mounted) { ··· 117 112 await file.writeAsBytes(bytes, flush: true); 118 113 119 114 if (mounted) { 120 - Navigator.of(context).pop( 121 - XFile( 122 - file.path, 123 - mimeType: 'image/png', 124 - name: filename, 125 - ), 126 - ); 115 + Navigator.of( 116 + context, 117 + ).pop(XFile(file.path, mimeType: 'image/png', name: filename)); 127 118 } 128 119 } 129 120 ··· 297 288 await file.writeAsBytes(bytes, flush: true); 298 289 299 290 if (mounted) { 300 - Navigator.of(context).pop( 301 - XFile( 302 - file.path, 303 - mimeType: 'image/png', 304 - name: filename, 305 - ), 306 - ); 291 + Navigator.of( 292 + context, 293 + ).pop(XFile(file.path, mimeType: 'image/png', name: filename)); 307 294 } 308 295 } 309 296 ··· 357 344 'layers': [layer.toMap()], 358 345 }, 359 346 ], 360 - 'imgSize': { 361 - 'width': storySize.width, 362 - 'height': storySize.height, 363 - }, 347 + 'imgSize': {'width': storySize.width, 'height': storySize.height}, 364 348 'lastRenderedImgSize': { 365 349 'width': storySize.width, 366 350 'height': storySize.height, ··· 395 379 if (!_isInitialized) { 396 380 return const Scaffold( 397 381 backgroundColor: Colors.black, 398 - body: Center( 399 - child: CircularProgressIndicator(color: Colors.white), 400 - ), 382 + body: Center(child: CircularProgressIndicator(color: Colors.white)), 401 383 ); 402 384 } 403 385
+1 -4
lib/src/core/pro_image_editor/ui/widgets/story_editor_bottom_section.dart
··· 7 7 /// 8 8 /// Contains the toolbar with editing tools. 9 9 class StoryEditorBottomSection extends StatelessWidget { 10 - const StoryEditorBottomSection({ 11 - required this.editor, 12 - super.key, 13 - }); 10 + const StoryEditorBottomSection({required this.editor, super.key}); 14 11 15 12 final ProImageEditorState editor; 16 13
+1 -5
lib/src/core/pro_image_editor/ui/widgets/story_editor_toolbar.dart
··· 87 87 child: Column( 88 88 mainAxisAlignment: MainAxisAlignment.center, 89 89 children: [ 90 - Icon( 91 - icon, 92 - color: AppColors.greyWhite, 93 - size: 26, 94 - ), 90 + Icon(icon, color: AppColors.greyWhite, size: 26), 95 91 const SizedBox(height: 4), 96 92 Text( 97 93 label,
+1 -4
lib/src/core/pro_video_editor/models/video_editor_result.dart
··· 4 4 /// Result returned from the video editor containing the edited video 5 5 /// and optional metadata about audio used. 6 6 class VideoEditorResult { 7 - const VideoEditorResult({ 8 - required this.video, 9 - this.soundRef, 10 - }); 7 + const VideoEditorResult({required this.video, this.soundRef}); 11 8 12 9 /// The edited video file. 13 10 final XFile video;
+2 -8
lib/src/core/pro_video_editor/pro_video_editor_repository_impl.dart
··· 60 60 await file.writeAsBytes(bytes, flush: true); 61 61 if (ctx.mounted) { 62 62 Navigator.of(ctx).pop( 63 - XFile( 64 - file.path, 65 - mimeType: 'image/jpeg', 66 - name: filename, 67 - ), 63 + XFile(file.path, mimeType: 'image/jpeg', name: filename), 68 64 ); 69 65 } 70 66 }, ··· 81 77 EditorVideo video, 82 78 ) async { 83 79 return Navigator.of(context).push<VideoEditorResult?>( 84 - MaterialPageRoute( 85 - builder: (_) => VideoEditorGroundedPage(video: video), 86 - ), 80 + MaterialPageRoute(builder: (_) => VideoEditorGroundedPage(video: video)), 87 81 ); 88 82 } 89 83
+3 -10
lib/src/core/pro_video_editor/services/audio_helper_service.dart
··· 8 8 class AudioHelperService { 9 9 /// Creates an instance of [AudioHelperService] for the 10 10 /// given [videoController]. 11 - AudioHelperService({ 12 - required this.videoController, 13 - }); 11 + AudioHelperService({required this.videoController}); 14 12 15 13 /// The internal audio player used to handle audio playback. 16 14 final _audioPlayer = AudioPlayer(); ··· 32 30 Future<void> initialize() { 33 31 return _audioPlayer.setAudioContext( 34 32 AudioContext( 35 - android: const AudioContextAndroid( 36 - audioFocus: AndroidAudioFocus.none, 37 - ), 33 + android: const AudioContextAndroid(audioFocus: AndroidAudioFocus.none), 38 34 iOS: AudioContextIOS( 39 35 options: const { 40 36 AVAudioSessionOptions.mixWithOthers, ··· 125 121 126 122 /// Mutes all audio (both original and custom). 127 123 Future<void> muteAll() async { 128 - await Future.wait([ 129 - setVolume(0), 130 - videoController.setVolume(0), 131 - ]); 124 + await Future.wait([setVolume(0), videoController.setVolume(0)]); 132 125 } 133 126 134 127 /// Restores audio based on current balance.
+5 -15
lib/src/core/pro_video_editor/ui/video_editor_grounded_page.dart
··· 57 57 final VideoEditorConfigs _videoConfigs = const VideoEditorConfigs( 58 58 enableTrimBar: false, 59 59 playTimeSmoothingDuration: Duration(milliseconds: 600), 60 - widgets: VideoEditorWidgets( 61 - headerToolbar: SizedBox.shrink(), 62 - ), 60 + widgets: VideoEditorWidgets(headerToolbar: SizedBox.shrink()), 63 61 ); 64 62 65 63 /// Indicates whether a seek operation is in progress. ··· 156 154 157 155 if (updateClipThumbnails) { 158 156 _configs.clipsEditor.clips.first = _configs.clipsEditor.clips.first 159 - .copyWith( 160 - thumbnails: temporaryThumbnails, 161 - ); 157 + .copyWith(thumbnails: temporaryThumbnails); 162 158 } 163 159 164 160 if (!mounted) return; ··· 219 215 220 216 // Update clip duration and thumbnails after first frame 221 217 _configs.clipsEditor.clips.first = _configs.clipsEditor.clips.first 222 - .copyWith( 223 - duration: _videoMetadata.duration, 224 - ); 218 + .copyWith(duration: _videoMetadata.duration); 225 219 if (!widget.storyMode) { 226 220 WidgetsBinding.instance.addPostFrameCallback((_) { 227 221 _generateThumbnails(); ··· 298 292 title: audio.title, 299 293 subtitle: audio.author.handle, 300 294 duration: const Duration(seconds: 9), 301 - image: EditorImage( 302 - networkUrl: audio.coverArt.toString(), 303 - ), 304 - audio: EditorAudio( 305 - networkUrl: audio.audio?.toString(), 306 - ), 295 + image: EditorImage(networkUrl: audio.coverArt.toString()), 296 + audio: EditorAudio(networkUrl: audio.audio?.toString()), 307 297 ), 308 298 ), 309 299 );
+1 -5
lib/src/core/pro_video_editor/ui/widgets/audio/audio_edit_controls_section.dart
··· 163 163 color: color.withAlpha(80), 164 164 borderRadius: BorderRadius.circular(8), 165 165 ), 166 - child: Icon( 167 - _configs.icons.audioTrackDefaultIcon, 168 - color: color, 169 - size: 24, 170 - ), 166 + child: Icon(_configs.icons.audioTrackDefaultIcon, color: color, size: 24), 171 167 ); 172 168 } 173 169
+5 -19
lib/src/core/pro_video_editor/ui/widgets/common/build_stickers.dart
··· 147 147 } 148 148 149 149 class _SheetHeader extends StatelessWidget { 150 - const _SheetHeader({ 151 - required this.title, 152 - required this.onClose, 153 - }); 150 + const _SheetHeader({required this.title, required this.onClose}); 154 151 155 152 final String title; 156 153 final VoidCallback onClose; ··· 281 278 itemCount: _itemCount(categoryIndex), 282 279 itemBuilder: (context, index) { 283 280 final url = _stickerUrl(categoryIndex: categoryIndex, index: index); 284 - return _StickerTile( 285 - url: url, 286 - onTap: () => onPickSticker(url), 287 - ); 281 + return _StickerTile(url: url, onTap: () => onPickSticker(url)); 288 282 }, 289 283 ), 290 284 ); ··· 295 289 return max(12, 12 + offset % 12); 296 290 } 297 291 298 - String _stickerUrl({ 299 - required int categoryIndex, 300 - required int index, 301 - }) { 292 + String _stickerUrl({required int categoryIndex, required int index}) { 302 293 final offset = categoryIndex * 20; 303 294 final id = offset + (index + 3) * 3; 304 295 return 'https://picsum.photos/id/$id/800'; ··· 306 297 } 307 298 308 299 class _StickerTile extends StatelessWidget { 309 - const _StickerTile({ 310 - required this.url, 311 - required this.onTap, 312 - }); 300 + const _StickerTile({required this.url, required this.onTap}); 313 301 314 302 final String url; 315 303 final VoidCallback onTap; ··· 347 335 } 348 336 349 337 class _StickerContent extends StatelessWidget { 350 - const _StickerContent({ 351 - required this.url, 352 - }); 338 + const _StickerContent({required this.url}); 353 339 354 340 final String url; 355 341
+12 -27
lib/src/core/pro_video_editor/ui/widgets/common/video_editor_configs_builder.dart
··· 72 72 initialMuted: true, 73 73 enableTrimBar: false, 74 74 playTimeSmoothingDuration: Duration(milliseconds: 300), 75 - widgets: VideoEditorWidgets( 76 - headerToolbar: SizedBox.shrink(), 77 - ), 75 + widgets: VideoEditorWidgets(headerToolbar: SizedBox.shrink()), 78 76 ), 79 77 }) { 80 78 final tools = storyMode ? _storyModeTools : _fullTools; ··· 96 94 tools: tools, 97 95 widgets: MainEditorWidgets( 98 96 removeLayerArea: 99 - ( 100 - removeAreaKey, 101 - editor, 102 - rebuildStream, 103 - isLayerBeingTransformed, 104 - ) => VideoEditorRemoveArea( 105 - removeAreaKey: removeAreaKey, 106 - editor: editor, 107 - rebuildStream: rebuildStream, 108 - isLayerBeingTransformed: isLayerBeingTransformed, 109 - ), 97 + (removeAreaKey, editor, rebuildStream, isLayerBeingTransformed) => 98 + VideoEditorRemoveArea( 99 + removeAreaKey: removeAreaKey, 100 + editor: editor, 101 + rebuildStream: rebuildStream, 102 + isLayerBeingTransformed: isLayerBeingTransformed, 103 + ), 110 104 appBar: (editor, rebuildStream) => null, 111 105 bottomBar: (editor, rebuildStream, key) => ReactiveWidget( 112 106 key: key, ··· 339 333 ), 340 334 blurEditor: BlurEditorConfigs( 341 335 maxBlur: 25, 342 - style: const BlurEditorStyle( 343 - background: AppColors.greyBlack, 344 - ), 336 + style: const BlurEditorStyle(background: AppColors.greyBlack), 345 337 widgets: BlurEditorWidgets( 346 338 appBar: (blurEditor, rebuildStream) => null, 347 339 bottomBar: (editorState, rebuildStream) { ··· 376 368 setLayer: setLayer, 377 369 scrollController: scrollController, 378 370 ), 379 - style: const StickerEditorStyle( 380 - showDragHandle: false, 381 - ), 371 + style: const StickerEditorStyle(showDragHandle: false), 382 372 ), 383 373 i18n: const I18n( 384 374 paintEditor: I18nPaintEditor( 385 375 changeOpacity: 'Opacity', 386 376 lineWidth: 'Thickness', 387 377 ), 388 - textEditor: I18nTextEditor( 389 - backgroundMode: 'Mode', 390 - textAlign: 'Align', 391 - ), 378 + textEditor: I18nTextEditor(backgroundMode: 'Mode', textAlign: 'Align'), 392 379 ), 393 380 // audioEditor: const AudioEditorConfigs( 394 381 // // Audio selection is now handled by the custom bottom sheet ··· 397 384 // audioTracks: const [], 398 385 // ), 399 386 clipsEditor: ClipsEditorConfigs( 400 - style: const ClipsEditorStyle( 401 - reversedClipsList: true, 402 - ), 387 + style: const ClipsEditorStyle(reversedClipsList: true), 403 388 widgets: ClipsEditorWidgets( 404 389 appBar: (editorState, rebuildStream) => null, 405 390 bottomBar: (editorState, rebuildStream) {
+1 -4
lib/src/core/pro_video_editor/ui/widgets/common/video_progress_alert.dart
··· 15 15 Widget build(BuildContext context) { 16 16 return Stack( 17 17 children: [ 18 - const ModalBarrier( 19 - dismissible: false, 20 - color: Colors.black54, 21 - ), 18 + const ModalBarrier(dismissible: false, color: Colors.black54), 22 19 Center( 23 20 child: Theme( 24 21 data: Theme.of(context),
+37 -43
lib/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart
··· 139 139 } 140 140 141 141 List<Widget> _buildAspectRatioButtons() { 142 - return List.generate( 143 - cropRotateEditorConfigs.aspectRatios.length, 144 - (index) { 145 - final item = cropRotateEditorConfigs.aspectRatios[index]; 146 - final isSelected = widget.editor.activeAspectRatio == item.value; 142 + return List.generate(cropRotateEditorConfigs.aspectRatios.length, (index) { 143 + final item = cropRotateEditorConfigs.aspectRatios[index]; 144 + final isSelected = widget.editor.activeAspectRatio == item.value; 147 145 148 - return Padding( 149 - padding: const EdgeInsets.symmetric(horizontal: 10), 150 - child: TextButton( 151 - onPressed: () => widget.editor.updateAspectRatio(item.value ?? -1), 152 - style: TextButton.styleFrom( 153 - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2), 154 - maximumSize: const Size( 155 - double.infinity, 156 - kBottomNavigationBarHeight, 157 - ), 146 + return Padding( 147 + padding: const EdgeInsets.symmetric(horizontal: 10), 148 + child: TextButton( 149 + onPressed: () => widget.editor.updateAspectRatio(item.value ?? -1), 150 + style: TextButton.styleFrom( 151 + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2), 152 + maximumSize: const Size( 153 + double.infinity, 154 + kBottomNavigationBarHeight, 158 155 ), 159 - child: Column( 160 - mainAxisSize: MainAxisSize.min, 161 - children: <Widget>[ 162 - SizedBox( 163 - height: 28, 164 - child: FittedBox( 165 - child: AspectRatioButton( 166 - aspectRatio: item.value, 167 - isSelected: isSelected, 168 - ), 156 + ), 157 + child: Column( 158 + mainAxisSize: MainAxisSize.min, 159 + children: <Widget>[ 160 + SizedBox( 161 + height: 28, 162 + child: FittedBox( 163 + child: AspectRatioButton( 164 + aspectRatio: item.value, 165 + isSelected: isSelected, 169 166 ), 170 167 ), 171 - const SizedBox(height: 5), 172 - Text( 173 - item.text, 174 - style: TextStyle( 175 - fontSize: 10, 176 - color: isSelected 177 - ? widget.selectedRatioColor 178 - : _foreGroundColorAccent, 179 - ), 168 + ), 169 + const SizedBox(height: 5), 170 + Text( 171 + item.text, 172 + style: TextStyle( 173 + fontSize: 10, 174 + color: isSelected 175 + ? widget.selectedRatioColor 176 + : _foreGroundColorAccent, 180 177 ), 181 - ], 182 - ), 178 + ), 179 + ], 183 180 ), 184 - ); 185 - }, 186 - ); 181 + ), 182 + ); 183 + }); 187 184 } 188 185 189 186 Widget _buildIconTextButton({ ··· 204 201 const SizedBox(height: 5), 205 202 Text( 206 203 label, 207 - style: TextStyle( 208 - fontSize: 10, 209 - color: _foreGroundColorAccent, 210 - ), 204 + style: TextStyle(fontSize: 10, color: _foreGroundColorAccent), 211 205 ), 212 206 ], 213 207 ),
+1 -4
lib/src/core/pro_video_editor/ui/widgets/filter/filter_editor_bar.dart
··· 78 78 duration: const Duration(milliseconds: 200), 79 79 transitionBuilder: (child, animation) => FadeTransition( 80 80 opacity: animation, 81 - child: SizeTransition( 82 - sizeFactor: animation, 83 - child: child, 84 - ), 81 + child: SizeTransition(sizeFactor: animation, child: child), 85 82 ), 86 83 child: widget.editor.selectedFilter.filters.isNotEmpty 87 84 ? StatefulBuilder(
+1 -4
lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_header.dart
··· 29 29 CircleIconButton( 30 30 onPressed: onNext, 31 31 backgroundColor: AppColors.primary500, 32 - icon: const Icon( 33 - Icons.arrow_forward, 34 - size: 22, 35 - ), 32 + icon: const Icon(Icons.arrow_forward, size: 22), 36 33 iconColor: AppColors.greyWhite, 37 34 semanticLabel: 'Done', 38 35 ),
+2 -10
lib/src/core/pro_video_editor/ui/widgets/layout/video_toolbar.dart
··· 56 56 label: 'Crop', 57 57 onPressed: onCrop, 58 58 ), 59 - _ToolButton( 60 - icon: Icons.tune, 61 - label: 'Tune', 62 - onPressed: onTune, 63 - ), 59 + _ToolButton(icon: Icons.tune, label: 'Tune', onPressed: onTune), 64 60 _ToolButton( 65 61 icon: Icons.filter, 66 62 label: 'Filter', ··· 117 113 child: Column( 118 114 mainAxisAlignment: MainAxisAlignment.center, 119 115 children: [ 120 - Icon( 121 - icon, 122 - color: AppColors.greyWhite, 123 - size: 24, 124 - ), 116 + Icon(icon, color: AppColors.greyWhite, size: 24), 125 117 const SizedBox(height: 4), 126 118 Text( 127 119 label,
+13 -22
lib/src/core/pro_video_editor/ui/widgets/paint/paint_editor_bar.dart
··· 173 173 } 174 174 175 175 List<Widget> _buildPaintTools() { 176 - return List.generate( 177 - widget.editor.tools.length, 178 - (index) { 179 - final item = widget.editor.tools[index]; 180 - final isActive = widget.editor.paintMode == item.mode; 176 + return List.generate(widget.editor.tools.length, (index) { 177 + final item = widget.editor.tools[index]; 178 + final isActive = widget.editor.paintMode == item.mode; 181 179 182 - return _buildIconTextButton( 183 - icon: item.icon, 184 - label: item.label, 185 - onPressed: () { 186 - widget.editor.setMode(item.mode); 187 - }, 188 - isActive: isActive, 189 - ); 190 - }, 191 - ); 180 + return _buildIconTextButton( 181 + icon: item.icon, 182 + label: item.label, 183 + onPressed: () { 184 + widget.editor.setMode(item.mode); 185 + }, 186 + isActive: isActive, 187 + ); 188 + }); 192 189 } 193 190 194 191 Widget _buildIconTextButton({ ··· 215 212 children: <Widget>[ 216 213 Icon(icon, color: color), 217 214 const SizedBox(height: 5), 218 - Text( 219 - label, 220 - style: TextStyle( 221 - fontSize: 10, 222 - color: labelColor, 223 - ), 224 - ), 215 + Text(label, style: TextStyle(fontSize: 10, color: labelColor)), 225 216 ], 226 217 ), 227 218 );
+20 -26
lib/src/core/pro_video_editor/ui/widgets/text/text_editor_bar.dart
··· 145 145 return []; 146 146 } 147 147 148 - return List.generate( 149 - textEditorConfigs.customTextStyles!.length, 150 - (index) { 151 - final item = textEditorConfigs.customTextStyles![index]; 152 - final selected = widget.editor.selectedTextStyle; 153 - final isSelected = selected.hashCode == item.hashCode; 148 + return List.generate(textEditorConfigs.customTextStyles!.length, (index) { 149 + final item = textEditorConfigs.customTextStyles![index]; 150 + final selected = widget.editor.selectedTextStyle; 151 + final isSelected = selected.hashCode == item.hashCode; 154 152 155 - return Padding( 156 - padding: const EdgeInsets.symmetric(horizontal: 10), 157 - child: IconButton( 158 - onPressed: () => widget.editor.setTextStyle(item), 159 - icon: Text( 160 - 'Aa', 161 - style: item.copyWith( 162 - color: isSelected ? Colors.black : Colors.white, 163 - ), 164 - ), 165 - style: IconButton.styleFrom( 166 - backgroundColor: isSelected ? Colors.white : Colors.black38, 167 - foregroundColor: isSelected ? Colors.black : Colors.white, 153 + return Padding( 154 + padding: const EdgeInsets.symmetric(horizontal: 10), 155 + child: IconButton( 156 + onPressed: () => widget.editor.setTextStyle(item), 157 + icon: Text( 158 + 'Aa', 159 + style: item.copyWith( 160 + color: isSelected ? Colors.black : Colors.white, 168 161 ), 169 162 ), 170 - ); 171 - }, 172 - ); 163 + style: IconButton.styleFrom( 164 + backgroundColor: isSelected ? Colors.white : Colors.black38, 165 + foregroundColor: isSelected ? Colors.black : Colors.white, 166 + ), 167 + ), 168 + ); 169 + }); 173 170 } 174 171 175 172 Widget _buildIconTextButton({ ··· 190 187 const SizedBox(height: 5), 191 188 Text( 192 189 label, 193 - style: TextStyle( 194 - fontSize: 10, 195 - color: _foreGroundColorAccent, 196 - ), 190 + style: TextStyle(fontSize: 10, color: _foreGroundColorAccent), 197 191 ), 198 192 ], 199 193 ),
+1 -4
lib/src/core/pro_video_editor/ui/widgets/text/text_editor_font_scale_sheet.dart
··· 61 61 child: Column( 62 62 mainAxisSize: MainAxisSize.min, 63 63 crossAxisAlignment: CrossAxisAlignment.stretch, 64 - children: [ 65 - _buildHeader(), 66 - _buildBody(), 67 - ], 64 + children: [_buildHeader(), _buildBody()], 68 65 ), 69 66 ), 70 67 ),
+5 -16
lib/src/core/routing/app_router.dart
··· 65 65 AutoRoute(page: SearchRoute.page, path: 'search'), 66 66 AutoRoute(page: MessagesRoute.page, path: 'messages'), 67 67 AutoRoute(page: NotificationsRoute.page, path: 'notifications'), 68 - AutoRoute( 69 - page: UserProfileRoute.page, 70 - path: 'profile', 71 - ), 68 + AutoRoute(page: UserProfileRoute.page, path: 'profile'), 72 69 ], 73 70 ), 74 71 ··· 107 104 108 105 // Deep linking routes or routes that will be pushed on top of everything 109 106 AutoRoute(page: StandalonePostRoute.page, path: '/post/:postUri'), 110 - AutoRoute( 111 - page: ProfileRoute.page, 112 - path: '/profile/:did', 113 - ), 107 + AutoRoute(page: ProfileRoute.page, path: '/profile/:did'), 114 108 AutoRoute( 115 109 page: StandaloneProfileFeedRoute.page, 116 110 path: '/profile/:did/feed', ··· 119 113 page: StandaloneRepostsFeedRoute.page, 120 114 path: '/profile/:did/reposts', 121 115 ), 122 - AutoRoute( 123 - page: StandaloneLikesFeedRoute.page, 124 - path: '/profile/:did/likes', 125 - ), 116 + AutoRoute(page: StandaloneLikesFeedRoute.page, path: '/profile/:did/likes'), 126 117 AutoRoute(page: UserListRoute.page, path: '/profile/:did/users'), 127 118 AutoRoute(page: VideoReviewRoute.page, path: '/video-review'), 128 119 AutoRoute(page: ImageReviewRoute.page, path: '/image-review'), ··· 181 172 ) { 182 173 return ModalBottomSheetRoute( 183 174 settings: page, 184 - builder: (context) => Padding( 185 - padding: const EdgeInsets.only(top: 12), 186 - child: child, 187 - ), 175 + builder: (context) => 176 + Padding(padding: const EdgeInsets.only(top: 12), child: child), 188 177 isScrollControlled: true, 189 178 useSafeArea: true, 190 179 shape: const RoundedRectangleBorder(
+2 -8
lib/src/core/storage/preferences/default_preferences.dart
··· 10 10 final labelerDid = modServiceDid; 11 11 // Default feeds: timeline, forYou, latest 12 12 final defaultFeeds = [ 13 - SavedFeed( 14 - type: 'timeline', 15 - value: 'following', 16 - pinned: true, 17 - ), 13 + SavedFeed(type: 'timeline', value: 'following', pinned: true), 18 14 SavedFeed( 19 15 type: 'feed', 20 16 value: ··· 74 70 ]; 75 71 76 72 // Default labelers 77 - final defaultLabelers = [ 78 - LabelerPrefItem(did: labelerDid), 79 - ]; 73 + final defaultLabelers = [LabelerPrefItem(did: labelerDid)]; 80 74 81 75 return Preferences( 82 76 preferences: [
+2 -6
lib/src/core/ui/widgets/report_dialog.dart
··· 19 19 misleading('Misleading'), 20 20 ruleViolations('Rule Violations'), 21 21 selfHarm('Self-Harm'), 22 - other('Other') 23 - ; 22 + other('Other'); 24 23 25 24 const ReportCategory(this.displayName); 26 25 final String displayName; ··· 553 552 height: 16, 554 553 child: CircularProgressIndicator(strokeWidth: 2), 555 554 ) 556 - : LongButton( 557 - label: 'Submit', 558 - onPressed: _submitReport, 559 - ), 555 + : LongButton(label: 'Submit', onPressed: _submitReport), 560 556 ], 561 557 ); 562 558 }
+1 -5
lib/src/core/utils/label_utils.dart
··· 72 72 } 73 73 74 74 static bool _isAdultOnlyLabel(String label) { 75 - const adultOnlyLabels = { 76 - 'porn', 77 - 'sexual', 78 - 'nsfl', 79 - }; 75 + const adultOnlyLabels = {'porn', 'sexual', 'nsfl'}; 80 76 return adultOnlyLabels.contains(label); 81 77 } 82 78
+1 -2
lib/src/core/utils/logging/log_level.dart
··· 6 6 warning(3, 'WARNING'), 7 7 error(4, 'ERROR'), 8 8 fatal(5, 'FATAL'), 9 - nothing(6, 'NOTHING') 10 - ; 9 + nothing(6, 'NOTHING'); 11 10 12 11 final int value; 13 12 final String name;
+1 -4
lib/src/features/auth/providers/auth_providers.dart
··· 141 141 return result; 142 142 } catch (e, stackTrace) { 143 143 _logger.e('OAuth completion error', error: e, stackTrace: stackTrace); 144 - state = state.copyWith( 145 - isLoading: false, 146 - error: 'Login failed: $e', 147 - ); 144 + state = state.copyWith(isLoading: false, error: 'Login failed: $e'); 148 145 return LoginResult.failed('Login failed: $e'); 149 146 } 150 147 }
+2 -3
lib/src/features/comments/providers/comments_page_state.dart
··· 5 5 6 6 @freezed 7 7 abstract class CommentsPageState with _$CommentsPageState { 8 - const factory CommentsPageState({ 9 - required ThreadViewPost thread, 10 - }) = _CommentsPageState; 8 + const factory CommentsPageState({required ThreadViewPost thread}) = 9 + _CommentsPageState; 11 10 }
+3 -7
lib/src/features/comments/ui/pages/comments_page.dart
··· 256 256 padding: EdgeInsets.zero, 257 257 constraints: const BoxConstraints(), 258 258 onPressed: _closeComments, 259 - icon: Icon( 260 - FluentIcons.dismiss_24_regular, 261 - color: textColor, 262 - ), 259 + icon: Icon(FluentIcons.dismiss_24_regular, color: textColor), 263 260 ), 264 261 ], 265 262 ), ··· 267 264 ), 268 265 if (hasCrossposts) 269 266 _CrosspostCommentsBanner( 270 - onTap: () => context.router.push( 271 - CrosspostCommentsRoute(postUri: _postUri), 272 - ), 267 + onTap: () => 268 + context.router.push(CrosspostCommentsRoute(postUri: _postUri)), 273 269 ), 274 270 Expanded( 275 271 child: asyncState.when(
+1 -5
lib/src/features/comments/ui/widgets/comment_item.dart
··· 52 52 'app.bsky', 53 53 ); 54 54 context.router.push( 55 - ProfileRoute( 56 - did: author.did, 57 - initialProfile: author, 58 - bsky: isBskyPost, 59 - ), 55 + ProfileRoute(did: author.did, initialProfile: author, bsky: isBskyPost), 60 56 ); 61 57 } 62 58
+1 -4
lib/src/features/comments/ui/widgets/emoji_picker.dart
··· 60 60 } 61 61 62 62 class _EmojiItem extends StatelessWidget { 63 - const _EmojiItem({ 64 - required this.emoji, 65 - required this.onEmojiSelected, 66 - }); 63 + const _EmojiItem({required this.emoji, required this.onEmojiSelected}); 67 64 68 65 final String emoji; 69 66 final Function(String) onEmojiSelected;
+2 -6
lib/src/features/feed/providers/feed_action_controller.dart
··· 7 7 /// like advancing to the next post without needing to pass callbacks through 8 8 /// the widget tree. 9 9 class FeedActionController { 10 - FeedActionController({ 11 - required this.onAdvanceAndRemove, 12 - }); 10 + FeedActionController({required this.onAdvanceAndRemove}); 13 11 14 12 /// Called after an action that should remove the current post and advance 15 13 /// to the next one (e.g., blocking a user, deleting a post). ··· 43 41 FeedActionControllerNotifier, 44 42 FeedActionController?, 45 43 Feed 46 - >( 47 - (ref, feed) => FeedActionControllerNotifier(), 48 - ); 44 + >((ref, feed) => FeedActionControllerNotifier());
+23 -33
lib/src/features/feed/providers/feed_provider.dart
··· 170 170 171 171 for (final newLabel in allLabels) { 172 172 final uri = AtUri.parse(newLabel.uri); 173 - extraInfo.update( 174 - uri, 175 - (value) { 176 - final existingLabels = value.postLabels; 173 + extraInfo.update(uri, (value) { 174 + final existingLabels = value.postLabels; 177 175 178 - // if new label in existing labels, 179 - //check if it should replace existing one 180 - if (existingLabels.any((label) => label.val == newLabel.val)) { 181 - final existingLabel = existingLabels.firstWhere( 182 - (label) => label.val == newLabel.val, 183 - ); 176 + // if new label in existing labels, 177 + //check if it should replace existing one 178 + if (existingLabels.any((label) => label.val == newLabel.val)) { 179 + final existingLabel = existingLabels.firstWhere( 180 + (label) => label.val == newLabel.val, 181 + ); 184 182 185 - // if new label says that existing one is negated or expired, 186 - // replace the existing one 187 - if (((newLabel.ver ?? 0) > (existingLabel.ver ?? 0) && 188 - newLabel.isNeg) || 189 - existingLabel.exp != null && 190 - existingLabel.exp!.isBefore(DateTime.now())) { 191 - existingLabels.remove(existingLabel); 192 - return ( 193 - postLabels: [...existingLabels, newLabel], 194 - ); 195 - } else { 196 - // if the new label is the same as the existing one, do nothing 197 - return value; 198 - } 183 + // if new label says that existing one is negated or expired, 184 + // replace the existing one 185 + if (((newLabel.ver ?? 0) > (existingLabel.ver ?? 0) && 186 + newLabel.isNeg) || 187 + existingLabel.exp != null && 188 + existingLabel.exp!.isBefore(DateTime.now())) { 189 + existingLabels.remove(existingLabel); 190 + return (postLabels: [...existingLabels, newLabel]); 199 191 } else { 200 - // if the new label is not in the existing labels, add it 201 - return ( 202 - postLabels: [...existingLabels, newLabel], 203 - ); 192 + // if the new label is the same as the existing one, do nothing 193 + return value; 204 194 } 205 - }, 206 - ifAbsent: () => ( 207 - postLabels: [newLabel], 208 - ), 209 - ); 195 + } else { 196 + // if the new label is not in the existing labels, add it 197 + return (postLabels: [...existingLabels, newLabel]); 198 + } 199 + }, ifAbsent: () => (postLabels: [newLabel])); 210 200 } 211 201 212 202 final filteredPosts = await _filterHiddenPosts(
+3 -11
lib/src/features/feed/ui/pages/feed_page.dart
··· 48 48 feedActionControllerProvider(widget.feed).notifier, 49 49 ); 50 50 _actionControllerNotifier!.setController( 51 - FeedActionController( 52 - onAdvanceAndRemove: scrollToNextAndRemovePrevious, 53 - ), 51 + FeedActionController(onAdvanceAndRemove: scrollToNextAndRemovePrevious), 54 52 ); 55 53 }); 56 54 } ··· 201 199 if (shouldBeActive) { 202 200 return Stack( 203 201 children: [ 204 - FeedPostWidget( 205 - index: index, 206 - feed: widget.feed, 207 - ), 202 + FeedPostWidget(index: index, feed: widget.feed), 208 203 const Positioned( 209 204 bottom: 10, 210 205 left: 10, ··· 234 229 ); 235 230 } else { 236 231 if (shouldBeActive) { 237 - return FeedPostWidget( 238 - index: index, 239 - feed: widget.feed, 240 - ); 232 + return FeedPostWidget(index: index, feed: widget.feed); 241 233 } else { 242 234 // SizedBox to maintain scroll position but hide content 243 235 return const DecoratedBox(
+5 -21
lib/src/features/feed/ui/pages/standalone_post_page.dart
··· 215 215 _videoPlayerKey.currentState?.pauseVideo(); 216 216 final isBskyPost = postData.uri.collection 217 217 .toString() 218 - .startsWith( 219 - 'app.bsky', 220 - ); 218 + .startsWith('app.bsky'); 221 219 context.router.push( 222 220 ProfileRoute( 223 221 did: postData.author.did, ··· 250 248 child: Column( 251 249 mainAxisAlignment: MainAxisAlignment.center, 252 250 children: [ 253 - const Icon( 254 - Icons.error, 255 - color: Colors.white, 256 - size: 48, 257 - ), 251 + const Icon(Icons.error, color: Colors.white, size: 48), 258 252 const SizedBox(height: 16), 259 253 Text( 260 254 'Error loading post: ${snapshot.error}', ··· 273 267 body: Stack( 274 268 children: [ 275 269 content, 276 - const Positioned( 277 - top: 0, 278 - left: 0, 279 - child: AppOverlayBackButton(), 280 - ), 270 + const Positioned(top: 0, left: 0, child: AppOverlayBackButton()), 281 271 ], 282 272 ), 283 273 bottomNavigationBar: postData == null ··· 301 291 } 302 292 303 293 class _CommentBar extends StatelessWidget { 304 - const _CommentBar({ 305 - required this.bottomPadding, 306 - required this.onTap, 307 - }); 294 + const _CommentBar({required this.bottomPadding, required this.onTap}); 308 295 309 296 final double bottomPadding; 310 297 final VoidCallback onTap; ··· 348 335 ), 349 336 child: const Text( 350 337 'Add comment...', 351 - style: TextStyle( 352 - color: Colors.white54, 353 - fontSize: 14, 354 - ), 338 + style: TextStyle(color: Colors.white54, fontSize: 14), 355 339 ), 356 340 ), 357 341 ),
+3 -10
lib/src/features/feed/ui/widgets/action_buttons/share_panel.dart
··· 52 52 ShareParams(uri: Uri.parse(widget.shareUrl)), 53 53 ); 54 54 } catch (e, st) { 55 - logger.e( 56 - 'Failed to open native share sheet', 57 - error: e, 58 - stackTrace: st, 59 - ); 55 + logger.e('Failed to open native share sheet', error: e, stackTrace: st); 60 56 } 61 57 } 62 58 ··· 201 197 child: child, 202 198 ); 203 199 } 204 - final scaleAnimation = 205 - Tween<double>( 206 - begin: 0.7, 207 - end: 1, 208 - ).animate( 200 + final scaleAnimation = Tween<double>(begin: 0.7, end: 1) 201 + .animate( 209 202 CurvedAnimation( 210 203 parent: animation, 211 204 curve: Curves.easeOutBack,
+4 -14
lib/src/features/feed/ui/widgets/action_buttons/side_action_bar.dart
··· 275 275 isScrollControlled: true, 276 276 backgroundColor: Colors.transparent, 277 277 builder: (context) { 278 - return SharePanel( 279 - shareUrl: shareUrl, 280 - atUri: originalAtUri, 281 - ); 278 + return SharePanel(shareUrl: shareUrl, atUri: originalAtUri); 282 279 }, 283 280 ); 284 281 } ··· 347 344 } 348 345 349 346 if (widget.feed != null) { 350 - final controller = ref.read( 351 - feedActionControllerProvider(widget.feed!), 352 - ); 347 + final controller = ref.read(feedActionControllerProvider(widget.feed!)); 353 348 controller?.onAdvanceAndRemove(); 354 349 } else { 355 350 final profileUri = AtUri.parse('at://${currentPost.author.did}'); ··· 396 391 397 392 try { 398 393 final graphRepository = GetIt.instance<SprkRepository>().graph; 399 - await graphRepository.toggleBlock( 400 - author.did, 401 - author.viewer?.blocking, 402 - ); 394 + await graphRepository.toggleBlock(author.did, author.viewer?.blocking); 403 395 404 396 // If blocking and we have a feed, use the action controller to advance 405 397 if (!wasBlocked && widget.feed != null) { 406 - final controller = ref.read( 407 - feedActionControllerProvider(widget.feed!), 408 - ); 398 + final controller = ref.read(feedActionControllerProvider(widget.feed!)); 409 399 controller?.onAdvanceAndRemove(); 410 400 } 411 401 } catch (_) {}
+2 -6
lib/src/features/feed/ui/widgets/feed/cacheable_page_view.dart
··· 200 200 void debugFillProperties(DiagnosticPropertiesBuilder description) { 201 201 super.debugFillProperties(description); 202 202 description 203 - ..add( 204 - EnumProperty<Axis>('scrollDirection', widget.scrollDirection), 205 - ) 206 - ..add( 207 - FlagProperty('reverse', value: widget.reverse, ifTrue: 'reversed'), 208 - ) 203 + ..add(EnumProperty<Axis>('scrollDirection', widget.scrollDirection)) 204 + ..add(FlagProperty('reverse', value: widget.reverse, ifTrue: 'reversed')) 209 205 ..add( 210 206 DiagnosticsProperty<PageController>( 211 207 'controller',
+2 -6
lib/src/features/feed/ui/widgets/feed/feeds_bar.dart
··· 190 190 selectedTagId: settings.activeFeed.config.id, 191 191 onLeadingPressed: () => _showCreateMenu(context), 192 192 onTagTap: (tagId) { 193 - final feed = pinnedFeeds.firstWhere( 194 - (f) => f.config.id == tagId, 195 - ); 193 + final feed = pinnedFeeds.firstWhere((f) => f.config.id == tagId); 196 194 197 195 if (settings.activeFeed == feed) { 198 196 ref.read(feedRefreshTriggerProvider(feed).notifier).trigger(); ··· 205 203 } 206 204 }, 207 205 onLongPress: (tagData) { 208 - final feed = pinnedFeeds.firstWhere( 209 - (f) => f.config.id == tagData.id, 210 - ); 206 + final feed = pinnedFeeds.firstWhere((f) => f.config.id == tagData.id); 211 207 _showFeedOptionsSheet(context, feed); 212 208 }, 213 209 );
+7 -10
lib/src/features/feed/ui/widgets/images/image_carousel.dart
··· 119 119 } 120 120 return const SizedBox.shrink(); 121 121 }, 122 - errorBuilder: (context, error, stackTrace) => const Center( 123 - child: Icon(FluentIcons.error_circle_24_regular), 124 - ), 122 + errorBuilder: (context, error, stackTrace) => 123 + const Center(child: Icon(FluentIcons.error_circle_24_regular)), 125 124 ), 126 125 ); 127 126 } ··· 244 243 duration: const Duration(milliseconds: 200), 245 244 vsync: this, 246 245 ); 247 - _scrollAnimation = Tween<double>(begin: 0, end: 0).animate( 248 - CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic), 249 - ); 246 + _scrollAnimation = Tween<double>( 247 + begin: 0, 248 + end: 0, 249 + ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic)); 250 250 _previousIndex = widget.currentIndex; 251 251 _targetScrollOffset = _calculateScrollOffset(widget.currentIndex); 252 252 _currentScrollOffset = _targetScrollOffset; ··· 318 318 _currentScrollOffset = currentVisualPosition; 319 319 320 320 _scrollAnimation = 321 - Tween<double>( 322 - begin: currentVisualPosition, 323 - end: newOffset, 324 - ).animate( 321 + Tween<double>(begin: currentVisualPosition, end: newOffset).animate( 325 322 CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic), 326 323 ); 327 324 _targetScrollOffset = newOffset;
+2 -8
lib/src/features/feed/ui/widgets/post/feed_post_widget.dart
··· 18 18 import 'package:spark/src/features/settings/providers/preferences_provider.dart'; 19 19 20 20 class FeedPostWidget extends ConsumerStatefulWidget { 21 - const FeedPostWidget({ 22 - required this.index, 23 - required this.feed, 24 - super.key, 25 - }); 21 + const FeedPostWidget({required this.index, required this.feed, super.key}); 26 22 27 23 final int index; 28 24 final Feed feed; ··· 311 307 _videoPlayerKey.currentState?.pauseVideo(); 312 308 final isBskyPost = currentPost.uri.collection 313 309 .toString() 314 - .startsWith( 315 - 'app.bsky', 316 - ); 310 + .startsWith('app.bsky'); 317 311 context.router.push( 318 312 ProfileRoute( 319 313 did: currentPost.author.did,
+3 -7
lib/src/features/feed/ui/widgets/videos/video_player.dart
··· 66 66 duration: const Duration(milliseconds: 300), 67 67 vsync: this, 68 68 ); 69 - _bounceAnimation = 70 - Tween<double>( 71 - begin: 1, 72 - end: 1.3, 73 - ).animate( 74 - CurvedAnimation(parent: _bounceController, curve: Curves.elasticOut), 75 - ); 69 + _bounceAnimation = Tween<double>(begin: 1, end: 1.3).animate( 70 + CurvedAnimation(parent: _bounceController, curve: Curves.elasticOut), 71 + ); 76 72 initVideoPlayer(); 77 73 GetIt.I<LogService>() 78 74 .getLogger('PostVideoPlayer')
+2 -3
lib/src/features/home/providers/navigation_state.dart
··· 4 4 5 5 @freezed 6 6 abstract class NavigationState with _$NavigationState { 7 - const factory NavigationState({ 8 - @Default(0) int currentIndex, 9 - }) = _NavigationState; 7 + const factory NavigationState({@Default(0) int currentIndex}) = 8 + _NavigationState; 10 9 11 10 factory NavigationState.initial() => const NavigationState(); 12 11 }
+2 -7
lib/src/features/messages/ui/pages/new_chat_search_page.dart
··· 66 66 controller: _searchController, 67 67 decoration: InputDecoration( 68 68 hintText: 'Search users', 69 - prefixIcon: const Icon( 70 - FluentIcons.search_24_regular, 71 - ), 69 + prefixIcon: const Icon(FluentIcons.search_24_regular), 72 70 suffixIcon: _searchController.text.isNotEmpty 73 71 ? IconButton( 74 72 iconSize: 20, ··· 212 210 final theme = Theme.of(context); 213 211 final colorScheme = theme.colorScheme; 214 212 return Center( 215 - child: Text( 216 - state.error!, 217 - style: TextStyle(color: colorScheme.error), 218 - ), 213 + child: Text(state.error!, style: TextStyle(color: colorScheme.error)), 219 214 ); 220 215 } 221 216 if (state.query.isEmpty) {
+4 -16
lib/src/features/notifications/providers/notification_provider.dart
··· 18 18 bool _isLoading = false; 19 19 20 20 @override 21 - NotificationState build({ 22 - bool? priority, 23 - List<String>? reasons, 24 - }) { 21 + NotificationState build({bool? priority, List<String>? reasons}) { 25 22 _notificationRepository = GetIt.instance<SprkRepository>().notification; 26 23 _logger = GetIt.instance<LogService>().getLogger('NotificationNotifier'); 27 24 ··· 30 27 loadNotifications(priority: priority, reasons: reasons); 31 28 }); 32 29 33 - return const NotificationState( 34 - notifications: [], 35 - isLoading: true, 36 - ); 30 + return const NotificationState(notifications: [], isLoading: true); 37 31 } 38 32 39 33 /// Load initial notifications or refresh ··· 85 79 } 86 80 87 81 /// Load more notifications (pagination) 88 - Future<void> loadMore({ 89 - bool? priority, 90 - List<String>? reasons, 91 - }) async { 82 + Future<void> loadMore({bool? priority, List<String>? reasons}) async { 92 83 if (_isLoading || state.isLoadingMore || !state.hasMore) { 93 84 return; 94 85 } ··· 170 161 } 171 162 172 163 /// Refresh notifications 173 - Future<void> refresh({ 174 - bool? priority, 175 - List<String>? reasons, 176 - }) async { 164 + Future<void> refresh({bool? priority, List<String>? reasons}) async { 177 165 await loadNotifications( 178 166 priority: priority, 179 167 reasons: reasons,
+6 -18
lib/src/features/notifications/ui/widgets/notification_item.dart
··· 144 144 // Navigate based on notification type 145 145 if (notification.reason == 'follow') { 146 146 // Navigate to profile (use first author for grouped follows) 147 - context.router.push( 148 - ProfileRoute(did: notification.author.did), 149 - ); 147 + context.router.push(ProfileRoute(did: notification.author.did)); 150 148 } else if (notification.reason == 'reply') { 151 149 // Reply notification - navigate to root post with reply highlighted 152 150 final replyUri = notification.uri.toString(); ··· 160 158 ); 161 159 } else { 162 160 // Fallback to standalone post 163 - context.router.push( 164 - StandalonePostRoute(postUri: replyUri), 165 - ); 161 + context.router.push(StandalonePostRoute(postUri: replyUri)); 166 162 } 167 163 } else if (notification.reason == 'like' && _isReplySubject()) { 168 164 // Like on a reply - get root post URI from embedded subject or fetch it ··· 183 179 ); 184 180 } else if (context.mounted) { 185 181 // Fallback to standalone post showing the reply 186 - context.router.push( 187 - StandalonePostRoute(postUri: replyUri), 188 - ); 182 + context.router.push(StandalonePostRoute(postUri: replyUri)); 189 183 } 190 184 } else if (notification.reasonSubject != null) { 191 185 // Navigate to the post/thread 192 186 final reasonSubjectStr = notification.reasonSubject!.toString(); 193 - context.router.push( 194 - StandalonePostRoute(postUri: reasonSubjectStr), 195 - ); 187 + context.router.push(StandalonePostRoute(postUri: reasonSubjectStr)); 196 188 } else { 197 189 final collectionStr = notification.uri.collection.toString(); 198 190 if (collectionStr.startsWith('so.sprk.feed.post') || 199 191 collectionStr.startsWith('app.bsky.feed.post')) { 200 192 // Navigate to the post 201 193 final uriStr = notification.uri.toString(); 202 - context.router.push( 203 - StandalonePostRoute(postUri: uriStr), 204 - ); 194 + context.router.push(StandalonePostRoute(postUri: uriStr)); 205 195 return; 206 196 } 207 197 // Fallback to author profile 208 - context.router.push( 209 - ProfileRoute(did: notification.author.did), 210 - ); 198 + context.router.push(ProfileRoute(did: notification.author.did)); 211 199 } 212 200 } 213 201
+8 -31
lib/src/features/notifications/ui/widgets/notifications_list.dart
··· 4 4 import 'package:spark/src/features/notifications/ui/widgets/notification_item.dart'; 5 5 6 6 class NotificationsList extends ConsumerStatefulWidget { 7 - const NotificationsList({ 8 - this.priority, 9 - this.reasons, 10 - super.key, 11 - }); 7 + const NotificationsList({this.priority, this.reasons, super.key}); 12 8 13 9 final bool? priority; 14 10 final List<String>? reasons; ··· 45 41 reasons: widget.reasons, 46 42 ).notifier, 47 43 ) 48 - .loadMore( 49 - priority: widget.priority, 50 - reasons: widget.reasons, 51 - ); 44 + .loadMore(priority: widget.priority, reasons: widget.reasons); 52 45 } 53 46 } 54 47 55 48 @override 56 49 Widget build(BuildContext context) { 57 50 final notificationState = ref.watch( 58 - notificationProvider( 59 - priority: widget.priority, 60 - reasons: widget.reasons, 61 - ), 51 + notificationProvider(priority: widget.priority, reasons: widget.reasons), 62 52 ); 63 53 64 54 final isLoading = notificationState.isLoading; ··· 66 56 final hasError = notificationState.hasError; 67 57 68 58 if (isLoading && isEmpty) { 69 - return const Center( 70 - child: CircularProgressIndicator(), 71 - ); 59 + return const Center(child: CircularProgressIndicator()); 72 60 } 73 61 74 62 if (hasError && isEmpty) { ··· 84 72 reasons: widget.reasons, 85 73 ).notifier, 86 74 ) 87 - .refresh( 88 - priority: widget.priority, 89 - reasons: widget.reasons, 90 - ); 75 + .refresh(priority: widget.priority, reasons: widget.reasons); 91 76 }, 92 77 child: SingleChildScrollView( 93 78 physics: const AlwaysScrollableScrollPhysics(), ··· 158 143 reasons: widget.reasons, 159 144 ).notifier, 160 145 ) 161 - .refresh( 162 - priority: widget.priority, 163 - reasons: widget.reasons, 164 - ); 146 + .refresh(priority: widget.priority, reasons: widget.reasons); 165 147 }, 166 148 child: SingleChildScrollView( 167 149 physics: const AlwaysScrollableScrollPhysics(), ··· 212 194 reasons: widget.reasons, 213 195 ).notifier, 214 196 ) 215 - .refresh( 216 - priority: widget.priority, 217 - reasons: widget.reasons, 218 - ); 197 + .refresh(priority: widget.priority, reasons: widget.reasons); 219 198 }, 220 199 child: ListView.builder( 221 200 controller: _scrollController, ··· 228 207 // Loading more indicator 229 208 return const Padding( 230 209 padding: EdgeInsets.all(16), 231 - child: Center( 232 - child: CircularProgressIndicator(), 233 - ), 210 + child: Center(child: CircularProgressIndicator()), 234 211 ); 235 212 } 236 213
+3 -14
lib/src/features/posting/ui/pages/media_picker_page.dart
··· 442 442 return Center( 443 443 child: Padding( 444 444 padding: const EdgeInsets.all(24), 445 - child: Text( 446 - _errorMessage!, 447 - textAlign: TextAlign.center, 448 - ), 445 + child: Text(_errorMessage!, textAlign: TextAlign.center), 449 446 ), 450 447 ); 451 448 } ··· 533 530 Container( 534 531 color: Colors.black.withAlpha(140), 535 532 alignment: Alignment.center, 536 - child: const Icon( 537 - Icons.block, 538 - color: Colors.white, 539 - size: 22, 540 - ), 533 + child: const Icon(Icons.block, color: Colors.white, size: 22), 541 534 ), 542 535 ], 543 536 ), ··· 593 586 ); 594 587 } 595 588 596 - return Image.memory( 597 - bytes, 598 - fit: BoxFit.cover, 599 - gaplessPlayback: true, 600 - ); 589 + return Image.memory(bytes, fit: BoxFit.cover, gaplessPlayback: true); 601 590 }, 602 591 ); 603 592 }
+16 -25
lib/src/features/posting/ui/pages/recording_page.dart
··· 170 170 171 171 try { 172 172 await context.router.push( 173 - ImageReviewRoute( 174 - imageFiles: photos, 175 - storyMode: widget.storyMode, 176 - ), 173 + ImageReviewRoute(imageFiles: photos, storyMode: widget.storyMode), 177 174 ); 178 175 179 176 if (!mounted) return; ··· 192 189 setState(() { 193 190 _isProcessing = false; 194 191 }); 195 - ScaffoldMessenger.of(context).showSnackBar( 196 - SnackBar(content: Text('Error: $e')), 197 - ); 192 + ScaffoldMessenger.of( 193 + context, 194 + ).showSnackBar(SnackBar(content: Text('Error: $e'))); 198 195 } 199 196 } 200 197 } ··· 264 261 setState(() { 265 262 _isProcessing = false; 266 263 }); 267 - ScaffoldMessenger.of(context).showSnackBar( 268 - SnackBar(content: Text('Error: $e')), 269 - ); 264 + ScaffoldMessenger.of( 265 + context, 266 + ).showSnackBar(SnackBar(content: Text('Error: $e'))); 270 267 } 271 268 } 272 269 } ··· 367 364 stackTrace: stackTrace, 368 365 ); 369 366 if (mounted) { 370 - ScaffoldMessenger.of(context).showSnackBar( 371 - SnackBar(content: Text('Failed to post story: $e')), 372 - ); 367 + ScaffoldMessenger.of( 368 + context, 369 + ).showSnackBar(SnackBar(content: Text('Failed to post story: $e'))); 373 370 } 374 371 } 375 372 // If posting failed or was cancelled, reset state ··· 399 396 setState(() { 400 397 _isProcessing = false; 401 398 }); 402 - ScaffoldMessenger.of(context).showSnackBar( 403 - SnackBar(content: Text('Error: $e')), 404 - ); 399 + ScaffoldMessenger.of( 400 + context, 401 + ).showSnackBar(SnackBar(content: Text('Error: $e'))); 405 402 } 406 403 } 407 404 } ··· 467 464 if (!cameraState.isInitialized || cameraState.controller == null) { 468 465 return const Scaffold( 469 466 backgroundColor: Colors.black, 470 - body: Center( 471 - child: CircularProgressIndicator(color: Colors.white), 472 - ), 467 + body: Center(child: CircularProgressIndicator(color: Colors.white)), 473 468 ); 474 469 } 475 470 ··· 477 472 if (_isExiting) { 478 473 return const Scaffold( 479 474 backgroundColor: Colors.black, 480 - body: Center( 481 - child: CircularProgressIndicator(color: Colors.white), 482 - ), 475 + body: Center(child: CircularProgressIndicator(color: Colors.white)), 483 476 ); 484 477 } 485 478 ··· 533 526 }, 534 527 loading: () => const Scaffold( 535 528 backgroundColor: Colors.black, 536 - body: Center( 537 - child: CircularProgressIndicator(color: Colors.white), 538 - ), 529 + body: Center(child: CircularProgressIndicator(color: Colors.white)), 539 530 ), 540 531 error: (error, stack) => Scaffold( 541 532 backgroundColor: Colors.black,
+6 -12
lib/src/features/posting/ui/pages/story_post_page.dart
··· 15 15 /// Shows a loading indicator while uploading and posting. 16 16 @RoutePage() 17 17 class StoryPostPage extends ConsumerStatefulWidget { 18 - const StoryPostPage({ 19 - this.imageFile, 20 - this.videoPath, 21 - super.key, 22 - }) : assert( 23 - imageFile != null || videoPath != null, 24 - 'Either imageFile or videoPath must be provided', 25 - ); 18 + const StoryPostPage({this.imageFile, this.videoPath, super.key}) 19 + : assert( 20 + imageFile != null || videoPath != null, 21 + 'Either imageFile or videoPath must be provided', 22 + ); 26 23 27 24 final XFile? imageFile; 28 25 final String? videoPath; ··· 216 213 const SizedBox(height: 10), 217 214 const Text( 218 215 'Keep this screen open. This usually takes a few seconds.', 219 - style: TextStyle( 220 - color: Color(0xFF94A3B8), 221 - fontSize: 12, 222 - ), 216 + style: TextStyle(color: Color(0xFF94A3B8), fontSize: 12), 223 217 textAlign: TextAlign.center, 224 218 ), 225 219 ],
+1 -3
lib/src/features/profile/providers/blocks_provider.dart
··· 30 30 /// Fetch missing profile data using ActorRepository (Spark-first with 31 31 /// Bluesky fallback) 32 32 /// Only fetches profiles that are actually incomplete (missing key fields) 33 - Future<void> _fetchAndMergeProfiles( 34 - List<ProfileView> profiles, 35 - ) async { 33 + Future<void> _fetchAndMergeProfiles(List<ProfileView> profiles) async { 36 34 // Check if profile is incomplete - need to check multiple fields 37 35 // Profile is incomplete if it's missing displayName, description, or avatar 38 36 // AND has a valid handle (if handle missing, it's likely deleted account)
+2 -8
lib/src/features/profile/providers/profile_likes_provider.dart
··· 25 25 Future<ProfileFeedState> build(String actor, bool bsky) async { 26 26 _actor = actor; 27 27 try { 28 - final result = await _loadLikes( 29 - actor: actor, 30 - cursor: null, 31 - ); 28 + final result = await _loadLikes(actor: actor, cursor: null); 32 29 return result; 33 30 } catch (e, stackTrace) { 34 31 _logger.e( ··· 156 153 157 154 Future<void> refresh() async { 158 155 try { 159 - final result = await _loadLikes( 160 - actor: _actor, 161 - cursor: null, 162 - ); 156 + final result = await _loadLikes(actor: _actor, cursor: null); 163 157 state = AsyncValue.data(result); 164 158 } catch (e) { 165 159 _logger.e('Error refreshing likes: $e');
+2 -8
lib/src/features/profile/providers/profile_reposts_provider.dart
··· 25 25 Future<ProfileFeedState> build(String actor, bool bsky) async { 26 26 _actor = actor; 27 27 try { 28 - final result = await _loadReposts( 29 - actor: actor, 30 - cursor: null, 31 - ); 28 + final result = await _loadReposts(actor: actor, cursor: null); 32 29 return result; 33 30 } catch (e, stackTrace) { 34 31 _logger.e( ··· 156 153 157 154 Future<void> refresh() async { 158 155 try { 159 - final result = await _loadReposts( 160 - actor: _actor, 161 - cursor: null, 162 - ); 156 + final result = await _loadReposts(actor: _actor, cursor: null); 163 157 state = AsyncValue.data(result); 164 158 } catch (e) { 165 159 _logger.e('Error refreshing reposts: $e');
+1 -3
lib/src/features/profile/providers/user_list_provider.dart
··· 81 81 /// Fetch missing profile data using ActorRepository (Spark-first with 82 82 /// Bluesky fallback) 83 83 /// Only fetches profiles that are actually incomplete (missing key fields) 84 - Future<void> _fetchAndMergeProfiles( 85 - List<ProfileView> profiles, 86 - ) async { 84 + Future<void> _fetchAndMergeProfiles(List<ProfileView> profiles) async { 87 85 // Check if profile is incomplete - need to check multiple fields 88 86 // Profile is incomplete if it's missing displayName, description, or avatar 89 87 // AND has a valid handle (if handle missing, it's likely a deleted account)
+1 -3
lib/src/features/profile/ui/pages/blocks_page.dart
··· 51 51 leading: const AppLeadingButton(tooltip: 'Back'), 52 52 title: const Text('Blocked Users'), 53 53 ), 54 - body: const Center( 55 - child: Text('Please log in to view blocked users'), 56 - ), 54 + body: const Center(child: Text('Please log in to view blocked users')), 57 55 ); 58 56 } 59 57
+6 -22
lib/src/features/profile/ui/pages/profile_page.dart
··· 122 122 ); 123 123 case 2: 124 124 // Third tab - likes (only shown for current user) 125 - tabWidget = ProfileLikesTab( 126 - profileUri: profileUri, 127 - bsky: widget.bsky, 128 - ); 125 + tabWidget = ProfileLikesTab(profileUri: profileUri, bsky: widget.bsky); 129 126 default: 130 127 // Fallback to first tab 131 128 tabWidget = ProfileGridTab(profileUri: profileUri, bsky: widget.bsky); ··· 169 166 170 167 Future<void> _handleAddStory(BuildContext context) async { 171 168 context.router.push( 172 - RecordingRoute( 173 - storyMode: true, 174 - captureMode: CaptureMode.hybrid, 175 - ), 169 + RecordingRoute(storyMode: true, captureMode: CaptureMode.hybrid), 176 170 ); 177 171 } 178 172 ··· 340 334 splashColor: Colors.transparent, 341 335 highlightColor: Colors.transparent, 342 336 onPressed: () => _showCreateMenu(context), 343 - icon: AppIcons.addPostFilled( 344 - size: 28, 345 - ), 337 + icon: AppIcons.addPostFilled(size: 28), 346 338 ), 347 339 ) 348 340 : null, ··· 387 379 final confirmed = await showDialog<bool>( 388 380 context: context, 389 381 builder: (context) => AlertDialog( 390 - title: Text( 391 - wasBlocked ? 'Unblock User' : 'Block User', 392 - ), 382 + title: Text(wasBlocked ? 'Unblock User' : 'Block User'), 393 383 content: Text( 394 384 wasBlocked 395 385 ? 'Are you sure you want to unblock this user?' ··· 417 407 try { 418 408 await notifier.toggleBlock(); 419 409 } catch (e) { 420 - _logger.e( 421 - 'Error blocking/unblocking profile', 422 - error: e, 423 - ); 410 + _logger.e('Error blocking/unblocking profile', error: e); 424 411 } 425 412 }, 426 413 isBlocked: isBlocking(profile.viewer), ··· 483 470 constraints: const BoxConstraints(), 484 471 splashColor: Colors.transparent, 485 472 highlightColor: Colors.transparent, 486 - icon: AppIcons.moreHoriz( 487 - color: colorScheme.onSurface, 488 - size: 28, 489 - ), 473 + icon: AppIcons.moreHoriz(color: colorScheme.onSurface, size: 28), 490 474 ), 491 475 ], 492 476 tabsWidget: ProfileTabBar(
+4 -16
lib/src/features/profile/ui/pages/standalone_likes_feed_page.dart
··· 65 65 }); 66 66 } 67 67 68 - final likesState = ref.watch( 69 - profileLikesProvider(widget.did, widget.bsky), 70 - ); 68 + final likesState = ref.watch(profileLikesProvider(widget.did, widget.bsky)); 71 69 final bottomPadding = MediaQuery.of(context).padding.bottom; 72 70 73 71 return Scaffold( ··· 173 171 ), 174 172 ), 175 173 // Back button overlay 176 - const Positioned( 177 - top: 0, 178 - left: 0, 179 - child: AppOverlayBackButton(), 180 - ), 174 + const Positioned(top: 0, left: 0, child: AppOverlayBackButton()), 181 175 ], 182 176 ), 183 177 bottomNavigationBar: _CommentBar( ··· 204 198 } 205 199 206 200 class _CommentBar extends StatelessWidget { 207 - const _CommentBar({ 208 - required this.bottomPadding, 209 - required this.onTap, 210 - }); 201 + const _CommentBar({required this.bottomPadding, required this.onTap}); 211 202 212 203 final double bottomPadding; 213 204 final VoidCallback onTap; ··· 251 242 ), 252 243 child: const Text( 253 244 'Add comment...', 254 - style: TextStyle( 255 - color: Colors.white54, 256 - fontSize: 14, 257 - ), 245 + style: TextStyle(color: Colors.white54, fontSize: 14), 258 246 ), 259 247 ), 260 248 ),
+3 -13
lib/src/features/profile/ui/pages/standalone_profile_feed_page.dart
··· 177 177 ), 178 178 ), 179 179 // Back button overlay 180 - const Positioned( 181 - top: 0, 182 - left: 0, 183 - child: AppOverlayBackButton(), 184 - ), 180 + const Positioned(top: 0, left: 0, child: AppOverlayBackButton()), 185 181 ], 186 182 ), 187 183 bottomNavigationBar: _CommentBar( ··· 208 204 } 209 205 210 206 class _CommentBar extends StatelessWidget { 211 - const _CommentBar({ 212 - required this.bottomPadding, 213 - required this.onTap, 214 - }); 207 + const _CommentBar({required this.bottomPadding, required this.onTap}); 215 208 216 209 final double bottomPadding; 217 210 final VoidCallback onTap; ··· 255 248 ), 256 249 child: const Text( 257 250 'Add comment...', 258 - style: TextStyle( 259 - color: Colors.white54, 260 - fontSize: 14, 261 - ), 251 + style: TextStyle(color: Colors.white54, fontSize: 14), 262 252 ), 263 253 ), 264 254 ),
+3 -13
lib/src/features/profile/ui/pages/standalone_reposts_feed_page.dart
··· 173 173 ), 174 174 ), 175 175 // Back button overlay 176 - const Positioned( 177 - top: 0, 178 - left: 0, 179 - child: AppOverlayBackButton(), 180 - ), 176 + const Positioned(top: 0, left: 0, child: AppOverlayBackButton()), 181 177 ], 182 178 ), 183 179 bottomNavigationBar: _CommentBar( ··· 204 200 } 205 201 206 202 class _CommentBar extends StatelessWidget { 207 - const _CommentBar({ 208 - required this.bottomPadding, 209 - required this.onTap, 210 - }); 203 + const _CommentBar({required this.bottomPadding, required this.onTap}); 211 204 212 205 final double bottomPadding; 213 206 final VoidCallback onTap; ··· 251 244 ), 252 245 child: const Text( 253 246 'Add comment...', 254 - style: TextStyle( 255 - color: Colors.white54, 256 - fontSize: 14, 257 - ), 247 + style: TextStyle(color: Colors.white54, fontSize: 14), 258 248 ), 259 249 ), 260 250 ),
+1 -3
lib/src/features/profile/ui/widgets/blocks_list_view.dart
··· 23 23 @override 24 24 Widget build(BuildContext context, WidgetRef ref) { 25 25 if (users.isEmpty) { 26 - return const Center( 27 - child: Text('No blocked users.'), 28 - ); 26 + return const Center(child: Text('No blocked users.')); 29 27 } 30 28 31 29 return ListView.builder(
+1 -3
lib/src/features/profile/ui/widgets/early_supporter_sheet.dart
··· 67 67 fontWeight: FontWeight.bold, 68 68 ), 69 69 ), 70 - TextSpan( 71 - text: 'Thanks to them for supporting us.', 72 - ), 70 + TextSpan(text: 'Thanks to them for supporting us.'), 73 71 ], 74 72 ), 75 73 textAlign: TextAlign.center,
+1 -3
lib/src/features/profile/ui/widgets/profile_feed_post_widget.dart
··· 285 285 // Check if post is from Bluesky 286 286 final isBskyPost = post.uri.collection 287 287 .toString() 288 - .startsWith( 289 - 'app.bsky', 290 - ); 288 + .startsWith('app.bsky'); 291 289 // Otherwise, navigate to the new profile 292 290 context.router.push( 293 291 ProfileRoute(
+1 -3
lib/src/features/profile/ui/widgets/profile_grid_tab.dart
··· 39 39 ), 40 40 ); 41 41 } else { 42 - context.router.push( 43 - StandalonePostRoute(postUri: postUri.toString()), 44 - ); 42 + context.router.push(StandalonePostRoute(postUri: postUri.toString())); 45 43 } 46 44 }); 47 45 }
+13 -16
lib/src/features/profile/ui/widgets/profile_grid_widget.dart
··· 73 73 mainAxisSpacing: 5, 74 74 childAspectRatio: 9 / 16, 75 75 ), 76 - delegate: SliverChildBuilderDelegate( 77 - (context, index) { 78 - final postUri = filteredUris[index]; 79 - final postView = state.postViews[postUri]; 80 - final postSource = state.postSources[postUri]; 76 + delegate: SliverChildBuilderDelegate((context, index) { 77 + final postUri = filteredUris[index]; 78 + final postView = state.postViews[postUri]; 79 + final postSource = state.postSources[postUri]; 81 80 82 - if (postView == null) { 83 - return const SizedBox.shrink(); 84 - } 81 + if (postView == null) { 82 + return const SizedBox.shrink(); 83 + } 85 84 86 - return ProfileGridTile( 87 - postView: postView, 88 - postSource: postSource, 89 - onTap: () => onPostTap(context, ref, postUri), 90 - ); 91 - }, 92 - childCount: filteredUris.length, 93 - ), 85 + return ProfileGridTile( 86 + postView: postView, 87 + postSource: postSource, 88 + onTap: () => onPostTap(context, ref, postUri), 89 + ); 90 + }, childCount: filteredUris.length), 94 91 ), 95 92 ), 96 93 // Bottom padding for tab bar
+14 -19
lib/src/features/profile/ui/widgets/profile_likes_tab.dart
··· 40 40 ), 41 41 ); 42 42 } else { 43 - context.router.push( 44 - StandalonePostRoute(postUri: postUri.toString()), 45 - ); 43 + context.router.push(StandalonePostRoute(postUri: postUri.toString())); 46 44 } 47 45 }); 48 46 } ··· 115 113 mainAxisSpacing: 5, 116 114 childAspectRatio: 9 / 16, 117 115 ), 118 - delegate: SliverChildBuilderDelegate( 119 - (context, index) { 120 - final postUri = filteredUris[index]; 121 - final postView = state.postViews[postUri]; 122 - final postSource = state.postSources[postUri]; 116 + delegate: SliverChildBuilderDelegate((context, index) { 117 + final postUri = filteredUris[index]; 118 + final postView = state.postViews[postUri]; 119 + final postSource = state.postSources[postUri]; 123 120 124 - if (postView == null) { 125 - return const SizedBox.shrink(); 126 - } 121 + if (postView == null) { 122 + return const SizedBox.shrink(); 123 + } 127 124 128 - return ProfileGridTile( 129 - postView: postView, 130 - postSource: postSource, 131 - onTap: () => onPostTap(context, ref, postUri), 132 - ); 133 - }, 134 - childCount: filteredUris.length, 135 - ), 125 + return ProfileGridTile( 126 + postView: postView, 127 + postSource: postSource, 128 + onTap: () => onPostTap(context, ref, postUri), 129 + ); 130 + }, childCount: filteredUris.length), 136 131 ), 137 132 ), 138 133 // Bottom padding for tab bar
+14 -19
lib/src/features/profile/ui/widgets/profile_reposts_tab.dart
··· 40 40 ), 41 41 ); 42 42 } else { 43 - context.router.push( 44 - StandalonePostRoute(postUri: postUri.toString()), 45 - ); 43 + context.router.push(StandalonePostRoute(postUri: postUri.toString())); 46 44 } 47 45 }); 48 46 } ··· 115 113 mainAxisSpacing: 5, 116 114 childAspectRatio: 9 / 16, 117 115 ), 118 - delegate: SliverChildBuilderDelegate( 119 - (context, index) { 120 - final postUri = filteredUris[index]; 121 - final postView = state.postViews[postUri]; 122 - final postSource = state.postSources[postUri]; 116 + delegate: SliverChildBuilderDelegate((context, index) { 117 + final postUri = filteredUris[index]; 118 + final postView = state.postViews[postUri]; 119 + final postSource = state.postSources[postUri]; 123 120 124 - if (postView == null) { 125 - return const SizedBox.shrink(); 126 - } 121 + if (postView == null) { 122 + return const SizedBox.shrink(); 123 + } 127 124 128 - return ProfileGridTile( 129 - postView: postView, 130 - postSource: postSource, 131 - onTap: () => onPostTap(context, ref, postUri), 132 - ); 133 - }, 134 - childCount: filteredUris.length, 135 - ), 125 + return ProfileGridTile( 126 + postView: postView, 127 + postSource: postSource, 128 + onTap: () => onPostTap(context, ref, postUri), 129 + ); 130 + }, childCount: filteredUris.length), 136 131 ), 137 132 ), 138 133 // Bottom padding for tab bar
+27 -32
lib/src/features/search/ui/pages/post_results.dart
··· 175 175 mainAxisSpacing: 12, 176 176 childAspectRatio: 0.45, // Adjust this to control card height 177 177 ), 178 - delegate: SliverChildBuilderDelegate( 179 - (context, index) { 180 - if (index >= state.searchResults.length) { 181 - return const Center(child: CircularProgressIndicator()); 182 - } 178 + delegate: SliverChildBuilderDelegate((context, index) { 179 + if (index >= state.searchResults.length) { 180 + return const Center(child: CircularProgressIndicator()); 181 + } 183 182 184 - final post = state.searchResults[index]; 185 - final preferences = ref 186 - .read(userPreferencesProvider) 187 - .asData 188 - ?.value; 189 - final labels = post.labels ?? []; 190 - final shouldBlur = 191 - preferences != null && 192 - labels.isNotEmpty && 193 - LabelUtils.shouldBlurContent(preferences, labels); 183 + final post = state.searchResults[index]; 184 + final preferences = ref 185 + .read(userPreferencesProvider) 186 + .asData 187 + ?.value; 188 + final labels = post.labels ?? []; 189 + final shouldBlur = 190 + preferences != null && 191 + labels.isNotEmpty && 192 + LabelUtils.shouldBlurContent(preferences, labels); 194 193 195 - return PostTile( 196 - thumbnailUrl: post.thumbnailUrl, 197 - likes: post.likeCount ?? 0, 198 - seen: false, 199 - nsfwBlur: shouldBlur, 200 - onTap: () { 201 - context.router.push( 202 - StandalonePostRoute(postUri: post.uri.toString()), 203 - ); 204 - }, 205 - ); 206 - }, 207 - childCount: state.searchResults.length, 208 - ), 194 + return PostTile( 195 + thumbnailUrl: post.thumbnailUrl, 196 + likes: post.likeCount ?? 0, 197 + seen: false, 198 + nsfwBlur: shouldBlur, 199 + onTap: () { 200 + context.router.push( 201 + StandalonePostRoute(postUri: post.uri.toString()), 202 + ); 203 + }, 204 + ); 205 + }, childCount: state.searchResults.length), 209 206 ), 210 207 ), 211 208 ··· 219 216 ), 220 217 221 218 // Bottom padding 222 - const SliverToBoxAdapter( 223 - child: SizedBox(height: 24), 224 - ), 219 + const SliverToBoxAdapter(child: SizedBox(height: 24)), 225 220 ], 226 221 ); 227 222 }
+5 -20
lib/src/features/search/ui/pages/search_page.dart
··· 90 90 } 91 91 92 92 void _onSuggestionSelected(ProfileViewBasic actor) { 93 - context.router.push( 94 - ProfileRoute(did: actor.did, initialProfile: actor), 95 - ); 93 + context.router.push(ProfileRoute(did: actor.did, initialProfile: actor)); 96 94 } 97 95 98 96 void _onSubmitted(String _) { ··· 118 116 hintText: 'Search users, posts...', 119 117 onSubmitted: _onSubmitted, 120 118 textInputAction: TextInputAction.search, 121 - leadingWidgets: const [ 122 - Icon( 123 - FluentIcons.search_24_regular, 124 - size: 20, 125 - ), 126 - ], 119 + leadingWidgets: const [Icon(FluentIcons.search_24_regular, size: 20)], 127 120 actionWidgets: _searchController.text.isNotEmpty 128 121 ? [ 129 122 GestureDetector( 130 123 onTap: _searchController.clear, 131 - child: const Icon( 132 - FluentIcons.dismiss_24_regular, 133 - size: 20, 134 - ), 124 + child: const Icon(FluentIcons.dismiss_24_regular, size: 20), 135 125 ), 136 126 ] 137 127 : null, ··· 144 134 ], 145 135 ), 146 136 contentWidget: const TabBarView( 147 - children: [ 148 - PostResults(), 149 - UserResults(), 150 - ], 137 + children: [PostResults(), UserResults()], 151 138 ), 152 139 emptyStateWidget: hasQuery 153 140 ? _ActorTypeaheadSuggestions( ··· 166 153 SliverToBoxAdapter(child: SuggestedFeedsList()), 167 154 SliverFillRemaining( 168 155 hasScrollBody: false, 169 - child: Center( 170 - child: Text('Discover new content'), 171 - ), 156 + child: Center(child: Text('Discover new content')), 172 157 ), 173 158 ], 174 159 ),
+10 -35
lib/src/features/settings/providers/settings_provider.dart
··· 86 86 Future<void> _updatePreferences(Preferences preferences) async { 87 87 await ref 88 88 .read(userPreferencesProvider.notifier) 89 - .updatePreferences( 90 - preferences, 91 - ); 89 + .updatePreferences(preferences); 92 90 } 93 91 94 92 /// Loads the last active feed from local storage ··· 145 143 } 146 144 147 145 // Return temporary default state that will be replaced by loadSettings() 148 - return SettingsState( 149 - activeFeed: defaultFeed, 150 - ); 146 + return SettingsState(activeFeed: defaultFeed); 151 147 } 152 148 153 149 /// Loads all settings from the preferences provider ··· 304 300 likedFeeds.add(updatedFeed); 305 301 } 306 302 307 - state = state.copyWith( 308 - feeds: updatedFeeds, 309 - likedFeeds: likedFeeds, 310 - ); 303 + state = state.copyWith(feeds: updatedFeeds, likedFeeds: likedFeeds); 311 304 312 305 await _updateFeedsInPreferences(updatedFeeds); 313 306 } ··· 337 330 .where((f) => f.config.id != updatedFeed.config.id) 338 331 .toList(); 339 332 340 - state = state.copyWith( 341 - feeds: updatedFeeds, 342 - likedFeeds: likedFeeds, 343 - ); 333 + state = state.copyWith(feeds: updatedFeeds, likedFeeds: likedFeeds); 344 334 345 335 await _updateFeedsInPreferences(updatedFeeds); 346 336 } ··· 478 468 return feeds.firstWhere((feed) => feed.config.id == activeSavedFeed!.id); 479 469 } catch (e) { 480 470 // Fallback to creating feed without view if not found 481 - return Feed( 482 - type: activeSavedFeed.type, 483 - config: activeSavedFeed, 484 - ); 471 + return Feed(type: activeSavedFeed.type, config: activeSavedFeed); 485 472 } 486 473 } 487 474 ··· 511 498 preferences.preferences 512 499 .where((pref) => !pref.isLabelersPref(pref)) 513 500 .toList() 514 - ..add( 515 - Preference.labelersPref(labelers: updatedLabelers), 516 - ); 501 + ..add(Preference.labelersPref(labelers: updatedLabelers)); 517 502 518 503 await _updatePreferences( 519 504 Preferences(preferences: updatedPreferencesList), ··· 571 556 preferences.preferences 572 557 .where((pref) => !pref.isLabelersPref(pref)) 573 558 .toList() 574 - ..add( 575 - Preference.labelersPref(labelers: updatedLabelers), 576 - ); 559 + ..add(Preference.labelersPref(labelers: updatedLabelers)); 577 560 578 561 await _updatePreferences( 579 562 Preferences(preferences: updatedPreferencesList), ··· 615 598 preferences.preferences 616 599 .where((pref) => !pref.isLabelersPref(pref)) 617 600 .toList() 618 - ..add( 619 - Preference.labelersPref(labelers: updatedLabelers), 620 - ); 601 + ..add(Preference.labelersPref(labelers: updatedLabelers)); 621 602 622 603 await _updatePreferences( 623 604 Preferences(preferences: updatedPreferencesList), ··· 656 637 preferences.preferences 657 638 .where((pref) => !pref.isLabelersPref(pref)) 658 639 .toList() 659 - ..add( 660 - Preference.labelersPref(labelers: updatedLabelers), 661 - ); 640 + ..add(Preference.labelersPref(labelers: updatedLabelers)); 662 641 await _updatePreferences( 663 642 Preferences(preferences: updatedPreferencesList), 664 643 ); ··· 911 890 } 912 891 913 892 bool _isAdultOnlyLabel(String label) { 914 - const adultOnlyLabels = { 915 - 'porn', 916 - 'sexual', 917 - 'nsfl', 918 - }; 893 + const adultOnlyLabels = {'porn', 'sexual', 'nsfl'}; 919 894 return adultOnlyLabels.contains(label); 920 895 } 921 896
+1 -4
lib/src/features/settings/ui/pages/feed_list_page.dart
··· 55 55 ), 56 56 style: TextButton.styleFrom( 57 57 foregroundColor: colorScheme.primary, 58 - padding: const EdgeInsets.symmetric( 59 - horizontal: 12, 60 - vertical: 8, 61 - ), 58 + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), 62 59 ), 63 60 ), 64 61 ],
+2 -9
lib/src/features/settings/ui/pages/labeler_label_settings_page.dart
··· 19 19 class LabelerLabelSettingsPage extends ConsumerStatefulWidget { 20 20 final String did; 21 21 22 - const LabelerLabelSettingsPage({ 23 - required this.did, 24 - super.key, 25 - }); 22 + const LabelerLabelSettingsPage({required this.did, super.key}); 26 23 27 24 @override 28 25 ConsumerState<LabelerLabelSettingsPage> createState() => ··· 248 245 } 249 246 250 247 bool _isAdultOnlyLabel(String label) { 251 - const adultOnlyLabels = { 252 - 'porn', 253 - 'sexual', 254 - 'nsfl', 255 - }; 248 + const adultOnlyLabels = {'porn', 'sexual', 'nsfl'}; 256 249 return adultOnlyLabels.contains(label); 257 250 } 258 251
+59 -64
lib/src/features/settings/ui/pages/labeler_management_page.dart
··· 77 77 78 78 for (final did in dids) { 79 79 try { 80 - final profile = profiles.firstWhere( 81 - (p) => p.did == did, 82 - ); 80 + final profile = profiles.firstWhere((p) => p.did == did); 83 81 profileMap[did] = profile; 84 82 } catch (_) { 85 83 profileMap[did] = null; ··· 306 304 ) 307 305 else 308 306 SliverList( 309 - delegate: SliverChildBuilderDelegate( 310 - (context, index) { 311 - final did = _labelerDids[index]; 312 - final profile = _labelerProfiles[did]; 313 - final isDefault = did == _defaultModServiceDid; 307 + delegate: SliverChildBuilderDelegate((context, index) { 308 + final did = _labelerDids[index]; 309 + final profile = _labelerProfiles[did]; 310 + final isDefault = did == _defaultModServiceDid; 314 311 315 - return Padding( 316 - padding: const EdgeInsets.symmetric( 317 - horizontal: 16, 318 - vertical: 4, 319 - ), 320 - child: Card( 321 - child: InkWell( 322 - onTap: () { 323 - context.router.push( 324 - LabelerLabelSettingsRoute(did: did), 325 - ); 326 - }, 327 - borderRadius: BorderRadius.circular(12), 328 - child: Padding( 329 - padding: const EdgeInsets.symmetric( 330 - horizontal: 8, 331 - vertical: 4, 332 - ), 333 - child: Row( 334 - children: [ 335 - Expanded( 336 - child: profile != null 337 - ? _buildProfileCardWithoutBorder( 338 - profile: profile, 339 - colorScheme: colorScheme, 340 - isDefault: isDefault, 341 - ) 342 - : _buildFallbackLabelerCard( 343 - did, 344 - colorScheme, 345 - isDefault: isDefault, 346 - ), 312 + return Padding( 313 + padding: const EdgeInsets.symmetric( 314 + horizontal: 16, 315 + vertical: 4, 316 + ), 317 + child: Card( 318 + child: InkWell( 319 + onTap: () { 320 + context.router.push( 321 + LabelerLabelSettingsRoute(did: did), 322 + ); 323 + }, 324 + borderRadius: BorderRadius.circular(12), 325 + child: Padding( 326 + padding: const EdgeInsets.symmetric( 327 + horizontal: 8, 328 + vertical: 4, 329 + ), 330 + child: Row( 331 + children: [ 332 + Expanded( 333 + child: profile != null 334 + ? _buildProfileCardWithoutBorder( 335 + profile: profile, 336 + colorScheme: colorScheme, 337 + isDefault: isDefault, 338 + ) 339 + : _buildFallbackLabelerCard( 340 + did, 341 + colorScheme, 342 + isDefault: isDefault, 343 + ), 344 + ), 345 + IconButton( 346 + padding: EdgeInsets.zero, 347 + constraints: const BoxConstraints(), 348 + icon: AppIcons.gear( 349 + color: colorScheme.onSurface, 347 350 ), 351 + onPressed: () { 352 + context.router.push( 353 + LabelerLabelSettingsRoute(did: did), 354 + ); 355 + }, 356 + tooltip: 'Label settings', 357 + ), 358 + if (!isDefault) 348 359 IconButton( 349 360 padding: EdgeInsets.zero, 350 361 constraints: const BoxConstraints(), 351 - icon: AppIcons.gear( 352 - color: colorScheme.onSurface, 353 - ), 354 - onPressed: () { 355 - context.router.push( 356 - LabelerLabelSettingsRoute(did: did), 357 - ); 358 - }, 359 - tooltip: 'Label settings', 362 + icon: const Icon(Icons.delete_outline), 363 + color: colorScheme.error, 364 + onPressed: () => _removeLabeler(did), 365 + tooltip: 'Remove labeler', 360 366 ), 361 - if (!isDefault) 362 - IconButton( 363 - padding: EdgeInsets.zero, 364 - constraints: const BoxConstraints(), 365 - icon: const Icon(Icons.delete_outline), 366 - color: colorScheme.error, 367 - onPressed: () => _removeLabeler(did), 368 - tooltip: 'Remove labeler', 369 - ), 370 - ], 371 - ), 367 + ], 372 368 ), 373 369 ), 374 370 ), 375 - ); 376 - }, 377 - childCount: _labelerDids.length, 378 - ), 371 + ), 372 + ); 373 + }, childCount: _labelerDids.length), 379 374 ), 380 375 ], 381 376 ),
+3 -9
lib/src/features/settings/ui/pages/settings_page.dart
··· 25 25 showDialog( 26 26 context: context, 27 27 barrierDismissible: false, 28 - builder: (context) => const Center( 29 - child: CircularProgressIndicator(), 30 - ), 28 + builder: (context) => const Center(child: CircularProgressIndicator()), 31 29 ); 32 30 33 31 // Call logout on the auth provider ··· 56 54 showDialog( 57 55 context: context, 58 56 barrierDismissible: false, 59 - builder: (context) => const Center( 60 - child: CircularProgressIndicator(), 61 - ), 57 + builder: (context) => const Center(child: CircularProgressIndicator()), 62 58 ); 63 59 64 60 final authRepository = GetIt.instance<AuthRepository>(); ··· 199 195 backgroundColor: Theme.of(context).colorScheme.surface, 200 196 elevation: 0, 201 197 leading: const AppLeadingButton(), 202 - title: const Text( 203 - 'Settings', 204 - ), 198 + title: const Text('Settings'), 205 199 centerTitle: true, 206 200 ), 207 201 body: ListView(
+1 -4
lib/src/features/sound/ui/pages/sound_page.dart
··· 169 169 } 170 170 171 171 class _SoundPostTile extends StatelessWidget { 172 - const _SoundPostTile({ 173 - required this.post, 174 - required this.onTap, 175 - }); 172 + const _SoundPostTile({required this.post, required this.onTap}); 176 173 177 174 final PostView post; 178 175 final VoidCallback onTap;
+3 -12
lib/src/features/sound/ui/widgets/sound_header_card.dart
··· 72 72 // Title with music icon 73 73 Row( 74 74 children: [ 75 - AppIcons.music( 76 - size: 16, 77 - color: colorScheme.primary, 78 - ), 75 + AppIcons.music(size: 16, color: colorScheme.primary), 79 76 const SizedBox(width: 8), 80 77 Expanded( 81 78 child: Text( ··· 144 141 } 145 142 146 143 class _CoverArtWithPlayer extends StatefulWidget { 147 - const _CoverArtWithPlayer({ 148 - required this.coverArtUrl, 149 - this.audioUrl, 150 - }); 144 + const _CoverArtWithPlayer({required this.coverArtUrl, this.audioUrl}); 151 145 152 146 final String coverArtUrl; 153 147 final String? audioUrl; ··· 286 280 ), 287 281 errorWidget: (context, url, error) => ColoredBox( 288 282 color: AppColors.grey700, 289 - child: AppIcons.music( 290 - size: 32, 291 - color: AppColors.grey400, 292 - ), 283 + child: AppIcons.music(size: 32, color: AppColors.grey400), 293 284 ), 294 285 ), 295 286 ),
+1 -5
lib/src/features/stories/providers/stories_by_author.dart
··· 8 8 FutureOr< 9 9 ({Map<ProfileViewBasic, List<StoryView>> storiesByAuthor, String? cursor}) 10 10 > 11 - storiesByAuthor( 12 - Ref ref, { 13 - int limit = 30, 14 - String? cursor, 15 - }) async { 11 + storiesByAuthor(Ref ref, {int limit = 30, String? cursor}) async { 16 12 final storyRepository = GetIt.instance<StoryRepository>(); 17 13 final result = await storyRepository.getStoriesTimeline( 18 14 limit: limit,
+1 -4
lib/src/features/stories/ui/pages/author_stories_page.dart
··· 72 72 void _initializeProgressControllers() { 73 73 _progressControllers = List.generate( 74 74 widget.stories.length, 75 - (_) => AnimationController( 76 - duration: _defaultStoryDuration, 77 - vsync: this, 78 - ), 75 + (_) => AnimationController(duration: _defaultStoryDuration, vsync: this), 79 76 ); 80 77 _storyLoadingStates = List<bool>.filled(widget.stories.length, true); 81 78 }
+1 -3
lib/src/features/stories/ui/pages/story_manager_page.dart
··· 66 66 final autoDeletePref = ref.watch(storyAutoDeletePrefProvider); 67 67 68 68 return Scaffold( 69 - appBar: AppBar( 70 - title: const Text('Story Manager'), 71 - ), 69 + appBar: AppBar(title: const Text('Story Manager')), 72 70 body: asyncState.when( 73 71 data: (data) { 74 72 return RefreshIndicator(
+1 -4
lib/src/features/stories/ui/widgets/stories_list.dart
··· 20 20 21 21 void _openStoryRecorder(BuildContext context) { 22 22 context.router.push( 23 - RecordingRoute( 24 - storyMode: true, 25 - captureMode: CaptureMode.hybrid, 26 - ), 23 + RecordingRoute(storyMode: true, captureMode: CaptureMode.hybrid), 27 24 ); 28 25 } 29 26
+14 -22
pubspec.lock
··· 1073 1073 dependency: transitive 1074 1074 description: 1075 1075 name: matcher 1076 - sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" 1076 + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 1077 1077 url: "https://pub.dev" 1078 1078 source: hosted 1079 - version: "0.12.18" 1079 + version: "0.12.19" 1080 1080 material_color_utilities: 1081 1081 dependency: transitive 1082 1082 description: ··· 1313 1313 dependency: "direct main" 1314 1314 description: 1315 1315 name: posthog_flutter 1316 - sha256: "04f95017238189fdc57146591a5a665398165dc7add86304345bbc2a7175194f" 1316 + sha256: "470eb55c5265e61bdcac7d720fa9de1306746a6086fe59dc06d2254d794705ed" 1317 1317 url: "https://pub.dev" 1318 1318 source: hosted 1319 - version: "5.19.0" 1319 + version: "5.20.0" 1320 1320 pro_image_editor: 1321 1321 dependency: "direct main" 1322 1322 description: ··· 1663 1663 dependency: transitive 1664 1664 description: 1665 1665 name: test 1666 - sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a" 1666 + sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7" 1667 1667 url: "https://pub.dev" 1668 1668 source: hosted 1669 - version: "1.29.0" 1669 + version: "1.30.0" 1670 1670 test_api: 1671 1671 dependency: transitive 1672 1672 description: 1673 1673 name: test_api 1674 - sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" 1674 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" 1675 1675 url: "https://pub.dev" 1676 1676 source: hosted 1677 - version: "0.7.9" 1677 + version: "0.7.10" 1678 1678 test_core: 1679 1679 dependency: transitive 1680 1680 description: 1681 1681 name: test_core 1682 - sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943" 1682 + sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" 1683 1683 url: "https://pub.dev" 1684 1684 source: hosted 1685 - version: "0.6.15" 1685 + version: "0.6.16" 1686 1686 typed_data: 1687 1687 dependency: transitive 1688 1688 description: ··· 1795 1795 url: "https://pub.dev" 1796 1796 source: hosted 1797 1797 version: "2.2.0" 1798 - very_good_analysis: 1799 - dependency: "direct dev" 1800 - description: 1801 - name: very_good_analysis 1802 - sha256: d1cb1d66a5aae2c702d68caca6c8347306d35e728fd94555fa21fa0448a972e0 1803 - url: "https://pub.dev" 1804 - source: hosted 1805 - version: "10.2.0" 1806 1798 video_player: 1807 1799 dependency: "direct main" 1808 1800 description: ··· 1863 1855 dependency: transitive 1864 1856 description: 1865 1857 name: wakelock_plus 1866 - sha256: "9296d40c9adbedaba95d1e704f4e0b434be446e2792948d0e4aa977048104228" 1858 + sha256: e4e125b7c1a2f0e491e5452afdc0e25ab77b2d2775a7caa231fcc1c1f2162c47 1867 1859 url: "https://pub.dev" 1868 1860 source: hosted 1869 - version: "1.4.0" 1861 + version: "1.5.0" 1870 1862 wakelock_plus_platform_interface: 1871 1863 dependency: transitive 1872 1864 description: 1873 1865 name: wakelock_plus_platform_interface 1874 - sha256: "036deb14cd62f558ca3b73006d52ce049fabcdcb2eddfe0bf0fe4e8a943b5cf2" 1866 + sha256: "24b84143787220a403491c2e5de0877fbbb87baf3f0b18a2a988973863db4b03" 1875 1867 url: "https://pub.dev" 1876 1868 source: hosted 1877 - version: "1.3.0" 1869 + version: "1.4.0" 1878 1870 watcher: 1879 1871 dependency: transitive 1880 1872 description:
-1
pubspec.yaml
··· 80 80 sdk: flutter 81 81 json_serializable: ^6.11.2 82 82 riverpod_generator: ^4.0.3 83 - very_good_analysis: ^10.2.0 84 83 85 84 flutter_launcher_icons: 86 85 android: true