Compare changes

Choose any two refs to compare.

Changed files
+412 -85
ios
Flutter
Runner.xcodeproj
xcshareddata
xcschemes
Runner.xcworkspace
lib
+21
lib/providers/comments_provider.dart
···
String? _cursor;
bool _hasMore = true;
+
// Collapsed thread state - stores URIs of collapsed comments
+
final Set<String> _collapsedComments = {};
+
// Current post being viewed
String? _postUri;
String? _postCid;
···
String get sort => _sort;
String? get timeframe => _timeframe;
ValueNotifier<DateTime?> get currentTimeNotifier => _currentTimeNotifier;
+
Set<String> get collapsedComments => _collapsedComments;
+
+
/// Toggle collapsed state for a comment thread
+
///
+
/// When collapsed, the comment's replies are hidden from view.
+
/// Long-pressing the same comment again will expand the thread.
+
void toggleCollapsed(String uri) {
+
if (_collapsedComments.contains(uri)) {
+
_collapsedComments.remove(uri);
+
} else {
+
_collapsedComments.add(uri);
+
}
+
notifyListeners();
+
}
+
+
/// Check if a specific comment is collapsed
+
bool isCollapsed(String uri) => _collapsedComments.contains(uri);
/// Start periodic time updates for "time ago" strings
///
···
_postUri = null;
_postCid = null;
_pendingRefresh = false;
+
_collapsedComments.clear();
notifyListeners();
}
+8
lib/screens/home/post_detail_screen.dart
···
currentTimeNotifier:
commentsProvider.currentTimeNotifier,
onCommentTap: _openReplyToComment,
+
collapsedComments: commentsProvider.collapsedComments,
+
onCollapseToggle: commentsProvider.toggleCollapsed,
);
},
childCount:
···
required this.comment,
required this.currentTimeNotifier,
this.onCommentTap,
+
this.collapsedComments = const {},
+
this.onCollapseToggle,
});
final ThreadViewComment comment;
final ValueNotifier<DateTime?> currentTimeNotifier;
final void Function(ThreadViewComment)? onCommentTap;
+
final Set<String> collapsedComments;
+
final void Function(String uri)? onCollapseToggle;
@override
Widget build(BuildContext context) {
···
currentTime: currentTime,
maxDepth: 6,
onCommentTap: onCommentTap,
+
collapsedComments: collapsedComments,
+
onCollapseToggle: onCollapseToggle,
);
},
);
+117 -67
lib/widgets/comment_card.dart
···
/// - Heart vote button with optimistic updates via VoteProvider
/// - Visual threading indicator based on nesting depth
/// - Tap-to-reply functionality via [onTap] callback
+
/// - Long-press to collapse thread via [onLongPress] callback
///
/// The [currentTime] parameter allows passing the current time for
/// time-ago calculations, enabling periodic updates and testing.
+
///
+
/// When [isCollapsed] is true, displays a badge showing [collapsedCount]
+
/// hidden replies on the threading indicator bar.
class CommentCard extends StatelessWidget {
const CommentCard({
required this.comment,
this.depth = 0,
this.currentTime,
this.onTap,
+
this.onLongPress,
+
this.isCollapsed = false,
+
this.collapsedCount = 0,
super.key,
});
···
/// Callback when the comment is tapped (for reply functionality)
final VoidCallback? onTap;
+
/// Callback when the comment is long-pressed (for collapse functionality)
+
final VoidCallback? onLongPress;
+
+
/// Whether this comment's thread is currently collapsed
+
final bool isCollapsed;
+
+
/// Number of replies hidden when collapsed
+
final int collapsedCount;
+
@override
Widget build(BuildContext context) {
// All comments get at least 1 threading line (depth + 1)
···
// the stroke width)
final borderLeftOffset = (threadingLineCount * 6.0) + 2.0;
-
return InkWell(
-
onTap: onTap,
-
child: Container(
-
decoration: const BoxDecoration(color: AppColors.background),
-
child: Stack(
-
children: [
-
// Threading indicators - vertical lines showing nesting ancestry
-
Positioned.fill(
-
child: CustomPaint(
-
painter: _CommentDepthPainter(depth: threadingLineCount),
+
return GestureDetector(
+
onLongPress: onLongPress != null
+
? () {
+
HapticFeedback.mediumImpact();
+
onLongPress!();
+
}
+
: null,
+
child: InkWell(
+
onTap: onTap,
+
child: Container(
+
decoration: const BoxDecoration(color: AppColors.background),
+
child: Stack(
+
children: [
+
// Threading indicators - vertical lines showing nesting ancestry
+
Positioned.fill(
+
child: CustomPaint(
+
painter: _CommentDepthPainter(depth: threadingLineCount),
+
),
),
-
),
-
// Bottom border (starts after threading lines, not overlapping them)
-
Positioned(
-
left: borderLeftOffset,
-
right: 0,
-
bottom: 0,
-
child: Container(height: 1, color: AppColors.border),
-
),
-
// Comment content with depth-based left padding
-
Padding(
-
padding: EdgeInsets.fromLTRB(leftPadding, 12, 16, 8),
-
child: Column(
-
crossAxisAlignment: CrossAxisAlignment.start,
-
children: [
-
// Author info row
-
Row(
-
children: [
-
// Author avatar
-
_buildAuthorAvatar(comment.author),
-
const SizedBox(width: 8),
-
Expanded(
-
child: Column(
-
crossAxisAlignment: CrossAxisAlignment.start,
-
children: [
-
// Author handle
-
Text(
-
'@${comment.author.handle}',
-
style: TextStyle(
-
color: AppColors.textPrimary.withValues(
-
alpha: 0.5,
+
// Collapsed count badge - positioned after threading lines
+
// to avoid overlap at any depth level
+
if (isCollapsed && collapsedCount > 0)
+
Positioned(
+
left: borderLeftOffset + 4,
+
bottom: 8,
+
child: Container(
+
padding: const EdgeInsets.symmetric(
+
horizontal: 6,
+
vertical: 2,
+
),
+
decoration: BoxDecoration(
+
color: AppColors.primary,
+
borderRadius: BorderRadius.circular(8),
+
),
+
child: Text(
+
'+$collapsedCount hidden',
+
style: const TextStyle(
+
color: AppColors.textPrimary,
+
fontSize: 10,
+
fontWeight: FontWeight.w500,
+
),
+
),
+
),
+
),
+
// Bottom border
+
// (starts after threading lines, not overlapping them)
+
Positioned(
+
left: borderLeftOffset,
+
right: 0,
+
bottom: 0,
+
child: Container(height: 1, color: AppColors.border),
+
),
+
// Comment content with depth-based left padding
+
Padding(
+
padding: EdgeInsets.fromLTRB(leftPadding, 12, 16, 8),
+
child: Column(
+
crossAxisAlignment: CrossAxisAlignment.start,
+
children: [
+
// Author info row
+
Row(
+
children: [
+
// Author avatar
+
_buildAuthorAvatar(comment.author),
+
const SizedBox(width: 8),
+
Expanded(
+
child: Column(
+
crossAxisAlignment: CrossAxisAlignment.start,
+
children: [
+
// Author handle
+
Text(
+
'@${comment.author.handle}',
+
style: TextStyle(
+
color: AppColors.textPrimary.withValues(
+
alpha: 0.5,
+
),
+
fontSize: 13,
+
fontWeight: FontWeight.w500,
),
-
fontSize: 13,
-
fontWeight: FontWeight.w500,
),
-
),
-
],
-
),
-
),
-
// Time ago
-
Text(
-
DateTimeUtils.formatTimeAgo(
-
comment.createdAt,
-
currentTime: currentTime,
+
],
+
),
),
-
style: TextStyle(
-
color: AppColors.textPrimary.withValues(alpha: 0.5),
-
fontSize: 12,
+
// Time ago
+
Text(
+
DateTimeUtils.formatTimeAgo(
+
comment.createdAt,
+
currentTime: currentTime,
+
),
+
style: TextStyle(
+
color: AppColors.textPrimary.withValues(alpha: 0.5),
+
fontSize: 12,
+
),
),
-
),
+
],
+
),
+
const SizedBox(height: 8),
+
+
// Comment content
+
if (comment.content.isNotEmpty) ...[
+
_buildCommentContent(comment),
+
const SizedBox(height: 8),
],
-
),
-
const SizedBox(height: 8),
-
// Comment content
-
if (comment.content.isNotEmpty) ...[
-
_buildCommentContent(comment),
-
const SizedBox(height: 8),
+
// Action buttons (just vote for now)
+
_buildActionButtons(context),
],
-
-
// Action buttons (just vote for now)
-
_buildActionButtons(context),
-
],
+
),
),
-
),
-
],
+
],
+
),
),
),
);
+77 -14
lib/widgets/comment_thread.dart
···
/// - Limits nesting depth to prevent excessive indentation
/// - Shows "Load more replies" button when hasMore is true
/// - Supports tap-to-reply via [onCommentTap] callback
+
/// - Supports long-press to collapse threads via [onCollapseToggle] callback
///
/// The [maxDepth] parameter controls how deeply nested comments can be
/// before they're rendered at the same level to prevent UI overflow.
+
///
+
/// When a comment is collapsed (via [collapsedComments]), its replies are
+
/// hidden with a smooth animation and a badge shows the hidden count.
class CommentThread extends StatelessWidget {
const CommentThread({
required this.thread,
···
this.currentTime,
this.onLoadMoreReplies,
this.onCommentTap,
+
this.collapsedComments = const {},
+
this.onCollapseToggle,
super.key,
});
···
/// Callback when a comment is tapped (for reply functionality)
final void Function(ThreadViewComment)? onCommentTap;
+
/// Set of collapsed comment URIs
+
final Set<String> collapsedComments;
+
+
/// Callback when a comment collapse state is toggled
+
final void Function(String uri)? onCollapseToggle;
+
+
/// Count all descendants recursively
+
static int countDescendants(ThreadViewComment thread) {
+
if (thread.replies == null || thread.replies!.isEmpty) {
+
return 0;
+
}
+
var count = thread.replies!.length;
+
for (final reply in thread.replies!) {
+
count += countDescendants(reply);
+
}
+
return count;
+
}
+
@override
Widget build(BuildContext context) {
// Calculate effective depth (flatten after maxDepth)
final effectiveDepth = depth > maxDepth ? maxDepth : depth;
+
// Check if this comment is collapsed
+
final isCollapsed = collapsedComments.contains(thread.comment.uri);
+
final collapsedCount = isCollapsed ? countDescendants(thread) : 0;
+
+
// Check if there are replies to render
+
final hasReplies = thread.replies != null && thread.replies!.isNotEmpty;
+
+
// Only build replies widget when NOT collapsed (optimization)
+
// When collapsed, AnimatedSwitcher shows SizedBox.shrink() so children
+
// are never mounted - no need to build them at all
+
final repliesWidget = hasReplies && !isCollapsed
+
? Column(
+
key: const ValueKey('replies'),
+
crossAxisAlignment: CrossAxisAlignment.start,
+
children: thread.replies!.map((reply) {
+
return CommentThread(
+
thread: reply,
+
depth: depth + 1,
+
maxDepth: maxDepth,
+
currentTime: currentTime,
+
onLoadMoreReplies: onLoadMoreReplies,
+
onCommentTap: onCommentTap,
+
collapsedComments: collapsedComments,
+
onCollapseToggle: onCollapseToggle,
+
);
+
}).toList(),
+
)
+
: null;
+
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
-
// Render the comment with tap handler
+
// Render the comment with tap and long-press handlers
CommentCard(
comment: thread.comment,
depth: effectiveDepth,
currentTime: currentTime,
onTap: onCommentTap != null ? () => onCommentTap!(thread) : null,
+
onLongPress: onCollapseToggle != null
+
? () => onCollapseToggle!(thread.comment.uri)
+
: null,
+
isCollapsed: isCollapsed,
+
collapsedCount: collapsedCount,
),
-
// Render replies recursively
-
if (thread.replies != null && thread.replies!.isNotEmpty)
-
...thread.replies!.map(
-
(reply) => CommentThread(
-
thread: reply,
-
depth: depth + 1,
-
maxDepth: maxDepth,
-
currentTime: currentTime,
-
onLoadMoreReplies: onLoadMoreReplies,
-
onCommentTap: onCommentTap,
-
),
+
// Render replies with animation
+
if (hasReplies)
+
AnimatedSwitcher(
+
duration: const Duration(milliseconds: 200),
+
switchInCurve: Curves.easeInOutCubicEmphasized,
+
switchOutCurve: Curves.easeInOutCubicEmphasized,
+
transitionBuilder: (Widget child, Animation<double> animation) {
+
return SizeTransition(
+
sizeFactor: animation,
+
axisAlignment: -1,
+
child: child,
+
);
+
},
+
child: isCollapsed
+
? const SizedBox.shrink(key: ValueKey('collapsed'))
+
: repliesWidget,
),
-
// Show "Load more replies" button if there are more
-
if (thread.hasMore) _buildLoadMoreButton(context),
+
// Show "Load more replies" button if there are more (and not collapsed)
+
if (thread.hasMore && !isCollapsed) _buildLoadMoreButton(context),
],
);
}
+1 -1
ios/Flutter/AppFrameworkInfo.plist
···
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
-
<string>12.0</string>
+
<string>13.0</string>
</dict>
</plist>
+68
ios/Podfile.lock
···
+
PODS:
+
- Flutter (1.0.0)
+
- flutter_secure_storage (6.0.0):
+
- Flutter
+
- flutter_web_auth_2 (3.0.0):
+
- Flutter
+
- path_provider_foundation (0.0.1):
+
- Flutter
+
- FlutterMacOS
+
- share_plus (0.0.1):
+
- Flutter
+
- shared_preferences_foundation (0.0.1):
+
- Flutter
+
- FlutterMacOS
+
- sqflite_darwin (0.0.4):
+
- Flutter
+
- FlutterMacOS
+
- url_launcher_ios (0.0.1):
+
- Flutter
+
- video_player_avfoundation (0.0.1):
+
- Flutter
+
- FlutterMacOS
+
+
DEPENDENCIES:
+
- Flutter (from `Flutter`)
+
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
+
- flutter_web_auth_2 (from `.symlinks/plugins/flutter_web_auth_2/ios`)
+
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
+
- share_plus (from `.symlinks/plugins/share_plus/ios`)
+
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
+
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
+
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
+
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
+
+
EXTERNAL SOURCES:
+
Flutter:
+
:path: Flutter
+
flutter_secure_storage:
+
:path: ".symlinks/plugins/flutter_secure_storage/ios"
+
flutter_web_auth_2:
+
:path: ".symlinks/plugins/flutter_web_auth_2/ios"
+
path_provider_foundation:
+
:path: ".symlinks/plugins/path_provider_foundation/darwin"
+
share_plus:
+
:path: ".symlinks/plugins/share_plus/ios"
+
shared_preferences_foundation:
+
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
+
sqflite_darwin:
+
:path: ".symlinks/plugins/sqflite_darwin/darwin"
+
url_launcher_ios:
+
:path: ".symlinks/plugins/url_launcher_ios/ios"
+
video_player_avfoundation:
+
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
+
+
SPEC CHECKSUMS:
+
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
+
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
+
flutter_web_auth_2: 5c8d9dcd7848b5a9efb086d24e7a9adcae979c80
+
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
+
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
+
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
+
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
+
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
+
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
+
+
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
+
+
COCOAPODS: 1.16.2
+115 -3
ios/Runner.xcodeproj/project.pbxproj
···
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+
16E67738C4AF07C35AA47470 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82EC9CF23352AC72F2003AAD /* Pods_RunnerTests.framework */; };
+
2220618238061C279E522B7E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2A2DB00FCDBEA05F362717D /* Pods_Runner.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
···
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+
24C909BB605D55AC18D4D709 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+
58C1A39422F3ADDA7073882C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
+
62C533E7959427EBD54BF4E0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
+
7404320A2A2665D2993CC4A9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+
82EC9CF23352AC72F2003AAD /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+
91248B6140D65FC329BE4089 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
···
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+
CAFF337A6DF135B15E2E5A82 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
+
D2A2DB00FCDBEA05F362717D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F2B3C8D12D0C8A5E00ABCDEF /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+
6A654D0E96DDFAB5016AAB44 /* Frameworks */ = {
+
isa = PBXFrameworksBuildPhase;
+
buildActionMask = 2147483647;
+
files = (
+
16E67738C4AF07C35AA47470 /* Pods_RunnerTests.framework in Frameworks */,
+
);
+
runOnlyForDeploymentPostprocessing = 0;
+
};
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+
2220618238061C279E522B7E /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
···
path = RunnerTests;
sourceTree = "<group>";
};
+
86A9EDA55647EB05647C404F /* Frameworks */ = {
+
isa = PBXGroup;
+
children = (
+
D2A2DB00FCDBEA05F362717D /* Pods_Runner.framework */,
+
82EC9CF23352AC72F2003AAD /* Pods_RunnerTests.framework */,
+
);
+
name = Frameworks;
+
sourceTree = "<group>";
+
};
+
8AC347B174FB51D9D1783044 /* Pods */ = {
+
isa = PBXGroup;
+
children = (
+
91248B6140D65FC329BE4089 /* Pods-Runner.debug.xcconfig */,
+
7404320A2A2665D2993CC4A9 /* Pods-Runner.release.xcconfig */,
+
62C533E7959427EBD54BF4E0 /* Pods-Runner.profile.xcconfig */,
+
24C909BB605D55AC18D4D709 /* Pods-RunnerTests.debug.xcconfig */,
+
58C1A39422F3ADDA7073882C /* Pods-RunnerTests.release.xcconfig */,
+
CAFF337A6DF135B15E2E5A82 /* Pods-RunnerTests.profile.xcconfig */,
+
);
+
name = Pods;
+
path = Pods;
+
sourceTree = "<group>";
+
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
···
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
+
8AC347B174FB51D9D1783044 /* Pods */,
+
86A9EDA55647EB05647C404F /* Frameworks */,
);
sourceTree = "<group>";
};
···
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
+
0D16B9D95FB392A9811278BE /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
+
6A654D0E96DDFAB5016AAB44 /* Frameworks */,
);
buildRules = (
);
···
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
+
5D065FE9468A69BB975A017A /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+
A11CDD673B8A553D9BF96957 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
···
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+
0D16B9D95FB392A9811278BE /* [CP] Check Pods Manifest.lock */ = {
+
isa = PBXShellScriptBuildPhase;
+
buildActionMask = 2147483647;
+
files = (
+
);
+
inputFileListPaths = (
+
);
+
inputPaths = (
+
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+
"${PODS_ROOT}/Manifest.lock",
+
);
+
name = "[CP] Check Pods Manifest.lock";
+
outputFileListPaths = (
+
);
+
outputPaths = (
+
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+
);
+
runOnlyForDeploymentPostprocessing = 0;
+
shellPath = /bin/sh;
+
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+
showEnvVarsInLog = 0;
+
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
···
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
+
5D065FE9468A69BB975A017A /* [CP] Check Pods Manifest.lock */ = {
+
isa = PBXShellScriptBuildPhase;
+
buildActionMask = 2147483647;
+
files = (
+
);
+
inputFileListPaths = (
+
);
+
inputPaths = (
+
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+
"${PODS_ROOT}/Manifest.lock",
+
);
+
name = "[CP] Check Pods Manifest.lock";
+
outputFileListPaths = (
+
);
+
outputPaths = (
+
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+
);
+
runOnlyForDeploymentPostprocessing = 0;
+
shellPath = /bin/sh;
+
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+
showEnvVarsInLog = 0;
+
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
···
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
+
A11CDD673B8A553D9BF96957 /* [CP] Embed Pods Frameworks */ = {
+
isa = PBXShellScriptBuildPhase;
+
buildActionMask = 2147483647;
+
files = (
+
);
+
inputFileListPaths = (
+
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+
);
+
name = "[CP] Embed Pods Frameworks";
+
outputFileListPaths = (
+
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+
);
+
runOnlyForDeploymentPostprocessing = 0;
+
shellPath = /bin/sh;
+
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+
showEnvVarsInLog = 0;
+
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
···
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
-
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
···
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
+
baseConfigurationReference = 24C909BB605D55AC18D4D709 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
···
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
+
baseConfigurationReference = 58C1A39422F3ADDA7073882C /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
···
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
+
baseConfigurationReference = CAFF337A6DF135B15E2E5A82 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
···
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
-
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
···
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
-
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
+2
ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
···
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
···
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
+3
ios/Runner.xcworkspace/contents.xcworkspacedata
···
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
+
<FileRef
+
location = "group:Pods/Pods.xcodeproj">
+
</FileRef>
</Workspace>