From cbb3f7f1dcf1a5e565f7ddbe51429eb9e9e36527 Mon Sep 17 00:00:00 2001 From: jake sciotto Date: Thu, 27 Aug 2026 13:27:59 -0600 Subject: [PATCH 1/4] feat(surveys): add optional intro screen before the first question Adds the leading mirror of the confirmation message: an optional intro screen shown before question 1, configured via the new displayIntroScreen / introScreenHeader / introScreenDescription / introScreenDescriptionContentType / introScreenButtonText appearance fields. Rendered by the Dart survey UI; the native bridges forward the new fields (requires posthog-ios >= 3.70.0 and posthog-android >= 3.59.0). Advancing past it records no response and sends no survey event. Generated-By: PostHog Code Task-Id: 50ceca51-4b90-4b94-8e72-e01bce50073c --- .changeset/survey-intro-screen.md | 5 + posthog_flutter/android/build.gradle | 2 +- .../flutter/PostHogDisplaySurveyExt.kt | 5 + .../darwin/posthog_flutter.podspec | 4 +- .../PostHogDisplaySurvey+Dict.swift | 11 ++ .../models/posthog_display_survey.dart | 10 ++ .../posthog_display_survey_appearance.dart | 10 ++ .../src/surveys/models/survey_appearance.dart | 12 ++ .../src/surveys/widgets/intro_message.dart | 59 ++++++++++ .../surveys/widgets/survey_bottom_sheet.dart | 24 +++- .../test/survey_bottom_sheet_test.dart | 106 ++++++++++++++++++ posthog_flutter/test/surveys_test.dart | 44 ++++++++ 12 files changed, 285 insertions(+), 7 deletions(-) create mode 100644 .changeset/survey-intro-screen.md create mode 100644 posthog_flutter/lib/src/surveys/widgets/intro_message.dart create mode 100644 posthog_flutter/test/survey_bottom_sheet_test.dart diff --git a/.changeset/survey-intro-screen.md b/.changeset/survey-intro-screen.md new file mode 100644 index 00000000..72adb1a9 --- /dev/null +++ b/.changeset/survey-intro-screen.md @@ -0,0 +1,5 @@ +--- +"posthog_flutter": minor +--- + +Surveys can now display an optional intro screen before the first question, configured via the new `displayIntroScreen`, `introScreenHeader`, `introScreenDescription`, `introScreenDescriptionContentType`, and `introScreenButtonText` appearance fields. The intro is rendered by the Dart survey UI as the leading mirror of the confirmation message: advancing past it records no response and sends no survey event, while closing the survey from the intro still sends the normal `survey dismissed` event. Requires posthog-ios >= 3.70.0 and posthog-android >= 3.59.0, which forward the new appearance fields over the bridge. diff --git a/posthog_flutter/android/build.gradle b/posthog_flutter/android/build.gradle index 8674e166..ca79394b 100644 --- a/posthog_flutter/android/build.gradle +++ b/posthog_flutter/android/build.gradle @@ -64,7 +64,7 @@ android { dependencies { testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'org.mockito:mockito-core:5.0.0' - implementation 'com.posthog:posthog-android:[3.60.7,4.0.0)' + implementation 'com.posthog:posthog-android:[3.61.0,4.0.0)' } testOptions { diff --git a/posthog_flutter/android/src/main/kotlin/com/posthog/flutter/PostHogDisplaySurveyExt.kt b/posthog_flutter/android/src/main/kotlin/com/posthog/flutter/PostHogDisplaySurveyExt.kt index b8fcb15c..10efea8d 100644 --- a/posthog_flutter/android/src/main/kotlin/com/posthog/flutter/PostHogDisplaySurveyExt.kt +++ b/posthog_flutter/android/src/main/kotlin/com/posthog/flutter/PostHogDisplaySurveyExt.kt @@ -81,6 +81,11 @@ fun PostHogDisplaySurvey.toMap(): Map { "thankYouMessageDescription" to app.thankYouMessageDescription, "thankYouMessageDescriptionContentType" to app.thankYouMessageDescriptionContentType?.value, "thankYouMessageCloseButtonText" to app.thankYouMessageCloseButtonText, + "displayIntroScreen" to app.displayIntroScreen, + "introScreenHeader" to app.introScreenHeader, + "introScreenDescription" to app.introScreenDescription, + "introScreenDescriptionContentType" to app.introScreenDescriptionContentType?.value, + "introScreenButtonText" to app.introScreenButtonText, ) } diff --git a/posthog_flutter/darwin/posthog_flutter.podspec b/posthog_flutter/darwin/posthog_flutter.podspec index 69e3dd0e..19fc5ef4 100644 --- a/posthog_flutter/darwin/posthog_flutter.podspec +++ b/posthog_flutter/darwin/posthog_flutter.podspec @@ -21,8 +21,8 @@ Postog flutter plugin s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' - # ~> Version 3.69.0 up to, but not including, 4.0.0 - s.dependency 'PostHog', '>= 3.69.0', '< 4.0.0' + # ~> Version 3.70.0 up to, but not including, 4.0.0 + s.dependency 'PostHog', '>= 3.70.0', '< 4.0.0' s.ios.deployment_target = '13.0' # PH iOS SDK 3.0.0 requires >= 10.15 diff --git a/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PostHogDisplaySurvey+Dict.swift b/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PostHogDisplaySurvey+Dict.swift index c9912d62..a710dc32 100644 --- a/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PostHogDisplaySurvey+Dict.swift +++ b/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PostHogDisplaySurvey+Dict.swift @@ -101,6 +101,17 @@ if let thankYouMessageCloseButtonText = appearance.thankYouMessageCloseButtonText { appearanceDict["thankYouMessageCloseButtonText"] = thankYouMessageCloseButtonText } + appearanceDict["displayIntroScreen"] = appearance.displayIntroScreen + if let introScreenHeader = appearance.introScreenHeader { + appearanceDict["introScreenHeader"] = introScreenHeader + } + if let introScreenDescription = appearance.introScreenDescription { + appearanceDict["introScreenDescription"] = introScreenDescription + appearanceDict["introScreenDescriptionContentType"] = appearance.introScreenDescriptionContentType?.rawValue + } + if let introScreenButtonText = appearance.introScreenButtonText { + appearanceDict["introScreenButtonText"] = introScreenButtonText + } dict["appearance"] = appearanceDict } diff --git a/posthog_flutter/lib/src/surveys/models/posthog_display_survey.dart b/posthog_flutter/lib/src/surveys/models/posthog_display_survey.dart index 82dbff38..984dc0e5 100644 --- a/posthog_flutter/lib/src/surveys/models/posthog_display_survey.dart +++ b/posthog_flutter/lib/src/surveys/models/posthog_display_survey.dart @@ -94,6 +94,11 @@ class PostHogDisplaySurvey { final thankYouMessageDescriptionContentType = PostHogDisplaySurveyTextContentType.fromInt(thankYouContentTypeRaw); + final introContentTypeRaw = + a['introScreenDescriptionContentType'] as int? ?? 1; + final introScreenDescriptionContentType = + PostHogDisplaySurveyTextContentType.fromInt(introContentTypeRaw); + appearance = PostHogDisplaySurveyAppearance( fontFamily: a['fontFamily'] as String?, backgroundColor: a['backgroundColor'] as String?, @@ -115,6 +120,11 @@ class PostHogDisplaySurvey { thankYouMessageDescriptionContentType, thankYouMessageCloseButtonText: a['thankYouMessageCloseButtonText'] as String?, + displayIntroScreen: a['displayIntroScreen'] as bool? ?? false, + introScreenHeader: a['introScreenHeader'] as String?, + introScreenDescription: a['introScreenDescription'] as String?, + introScreenDescriptionContentType: introScreenDescriptionContentType, + introScreenButtonText: a['introScreenButtonText'] as String?, ); } diff --git a/posthog_flutter/lib/src/surveys/models/posthog_display_survey_appearance.dart b/posthog_flutter/lib/src/surveys/models/posthog_display_survey_appearance.dart index 00c6e531..8cf2d565 100644 --- a/posthog_flutter/lib/src/surveys/models/posthog_display_survey_appearance.dart +++ b/posthog_flutter/lib/src/surveys/models/posthog_display_survey_appearance.dart @@ -23,6 +23,11 @@ class PostHogDisplaySurveyAppearance { this.thankYouMessageDescription, this.thankYouMessageDescriptionContentType, this.thankYouMessageCloseButtonText, + this.displayIntroScreen = false, + this.introScreenHeader, + this.introScreenDescription, + this.introScreenDescriptionContentType, + this.introScreenButtonText, }); final String? fontFamily; @@ -44,4 +49,9 @@ class PostHogDisplaySurveyAppearance { final PostHogDisplaySurveyTextContentType? thankYouMessageDescriptionContentType; final String? thankYouMessageCloseButtonText; + final bool displayIntroScreen; + final String? introScreenHeader; + final String? introScreenDescription; + final PostHogDisplaySurveyTextContentType? introScreenDescriptionContentType; + final String? introScreenButtonText; } diff --git a/posthog_flutter/lib/src/surveys/models/survey_appearance.dart b/posthog_flutter/lib/src/surveys/models/survey_appearance.dart index aa3f9143..ac3696d5 100644 --- a/posthog_flutter/lib/src/surveys/models/survey_appearance.dart +++ b/posthog_flutter/lib/src/surveys/models/survey_appearance.dart @@ -22,6 +22,10 @@ class SurveyAppearance { this.thankYouMessageHeader = 'Thank you for your feedback!', this.thankYouMessageDescription, this.thankYouMessageCloseButtonText = 'Close', + this.displayIntroScreen = false, + this.introScreenHeader, + this.introScreenDescription, + this.introScreenButtonText = 'Get started', this.borderColor = const Color(0xFFBDBDBD), this.inputBackgroundColor = Colors.white, this.inputTextColor = Colors.black, @@ -45,6 +49,10 @@ class SurveyAppearance { final String thankYouMessageHeader; final String? thankYouMessageDescription; final String thankYouMessageCloseButtonText; + final bool displayIntroScreen; + final String? introScreenHeader; + final String? introScreenDescription; + final String introScreenButtonText; final Color borderColor; final Color inputBackgroundColor; final Color inputTextColor; @@ -101,6 +109,10 @@ class SurveyAppearance { thankYouMessageDescription: appearance?.thankYouMessageDescription, thankYouMessageCloseButtonText: appearance?.thankYouMessageCloseButtonText ?? 'Close', + displayIntroScreen: appearance?.displayIntroScreen ?? false, + introScreenHeader: appearance?.introScreenHeader, + introScreenDescription: appearance?.introScreenDescription, + introScreenButtonText: appearance?.introScreenButtonText ?? 'Get started', borderColor: _colorFromHex(appearance?.borderColor) ?? const Color(0xFFBDBDBD), inputBackgroundColor: inputBackgroundColor, diff --git a/posthog_flutter/lib/src/surveys/widgets/intro_message.dart b/posthog_flutter/lib/src/surveys/widgets/intro_message.dart new file mode 100644 index 00000000..db64633f --- /dev/null +++ b/posthog_flutter/lib/src/surveys/widgets/intro_message.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import '../models/posthog_display_survey_text_content_type.dart'; +import '../models/survey_appearance.dart'; +import 'survey_button.dart'; + +/// Intro screen shown before the first question — the leading mirror of +/// [ConfirmationMessage]. Advancing records no response and sends no survey +/// event; it is a pure UI transition handled by the bottom sheet. +class IntroMessage extends StatelessWidget { + const IntroMessage({ + super.key, + required this.onStart, + this.appearance = SurveyAppearance.defaultAppearance, + this.introScreenDescriptionContentType, + }); + + final VoidCallback onStart; + final SurveyAppearance appearance; + final PostHogDisplaySurveyTextContentType? introScreenDescriptionContentType; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (appearance.introScreenHeader?.isNotEmpty == true) + Text( + appearance.introScreenHeader!, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: appearance.descriptionTextColor, + ), + textAlign: TextAlign.center, + ), + if (appearance.introScreenDescription?.isNotEmpty == true && + introScreenDescriptionContentType == + PostHogDisplaySurveyTextContentType.text) ...[ + const SizedBox(height: 16), + Text( + appearance.introScreenDescription!, + style: TextStyle( + fontSize: 16, + color: appearance.descriptionTextColor, + ), + textAlign: TextAlign.center, + ), + ], + const SizedBox(height: 20), + SurveyButton( + onPressed: onStart, + text: appearance.introScreenButtonText, + appearance: appearance, + ), + ], + ); + } +} diff --git a/posthog_flutter/lib/src/surveys/widgets/survey_bottom_sheet.dart b/posthog_flutter/lib/src/surveys/widgets/survey_bottom_sheet.dart index 1e59bdf3..3c3d215b 100644 --- a/posthog_flutter/lib/src/surveys/widgets/survey_bottom_sheet.dart +++ b/posthog_flutter/lib/src/surveys/widgets/survey_bottom_sheet.dart @@ -16,6 +16,7 @@ import 'rating_question.dart'; import 'choice_question.dart'; import 'confirmation_message.dart'; import 'survey_icon.dart'; +import 'intro_message.dart'; /// A bottom sheet that displays a survey to the user. class SurveyBottomSheet extends StatefulWidget { @@ -41,6 +42,10 @@ class SurveyBottomSheet extends StatefulWidget { class _SurveyBottomSheetState extends State { int _currentIndex = 0; bool _isCompleted = false; + // Advancing past the intro is a pure UI transition: no response is recorded + // and no survey event is sent. The X button keeps closing the survey. + late bool _showingIntroScreen = + widget.survey.appearance?.displayIntroScreen ?? false; @override void initState() { @@ -216,9 +221,7 @@ class _SurveyBottomSheetState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - if (!_isCompleted) - _buildQuestion(context) - else + if (_isCompleted) ConfirmationMessage( onClose: _handleClose, appearance: widget.appearance, @@ -227,7 +230,20 @@ class _SurveyBottomSheetState extends State { .appearance ?.thankYouMessageDescriptionContentType ?? PostHogDisplaySurveyTextContentType.text, - ), + ) + else if (_showingIntroScreen) + IntroMessage( + onStart: () => + setState(() => _showingIntroScreen = false), + appearance: widget.appearance, + introScreenDescriptionContentType: widget + .survey + .appearance + ?.introScreenDescriptionContentType ?? + PostHogDisplaySurveyTextContentType.text, + ) + else + _buildQuestion(context), ], ), ), diff --git a/posthog_flutter/test/survey_bottom_sheet_test.dart b/posthog_flutter/test/survey_bottom_sheet_test.dart new file mode 100644 index 00000000..3505cb20 --- /dev/null +++ b/posthog_flutter/test/survey_bottom_sheet_test.dart @@ -0,0 +1,106 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:posthog_flutter/src/surveys/models/posthog_display_survey.dart'; +import 'package:posthog_flutter/src/surveys/models/survey_appearance.dart'; +import 'package:posthog_flutter/src/surveys/models/survey_callbacks.dart'; +import 'package:posthog_flutter/src/surveys/widgets/survey_bottom_sheet.dart'; + +void main() { + PostHogDisplaySurvey surveyWithIntro({required bool displayIntroScreen}) { + return PostHogDisplaySurvey.fromDict({ + 'id': 'survey-1', + 'name': 'Test survey', + 'questions': [ + { + 'type': 'open', + 'question': 'What can we do better?', + 'isOptional': false, + }, + ], + 'appearance': { + 'displayIntroScreen': displayIntroScreen, + 'introScreenHeader': 'Welcome!', + 'introScreenDescription': 'Two quick questions.', + 'introScreenDescriptionContentType': 1, + 'introScreenButtonText': 'Get started', + }, + }); + } + + // Presents the sheet the same way SurveyService does in production. + Future pumpSurveySheet( + WidgetTester tester, + PostHogDisplaySurvey survey, + List callbackLog, + ) async { + await tester + .pumpWidget(const MaterialApp(home: Scaffold(body: SizedBox()))); + final context = tester.element(find.byType(Scaffold)); + unawaited(showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + builder: (context) => SurveyBottomSheet( + survey: survey, + appearance: SurveyAppearance.fromPostHog(survey.appearance), + onShown: (_) => callbackLog.add('shown'), + onResponse: (_, index, __) async { + callbackLog.add('response:$index'); + return const PostHogSurveyNextQuestion( + questionIndex: 0, + isSurveyCompleted: true, + ); + }, + onClosed: (_) => callbackLog.add('closed'), + ), + )); + await tester.pumpAndSettle(); + } + + group('SurveyBottomSheet intro screen', () { + testWidgets( + 'shows the intro before the first question and advances without callbacks', + (tester) async { + final callbackLog = []; + await pumpSurveySheet( + tester, surveyWithIntro(displayIntroScreen: true), callbackLog); + + // Intro visible, question not yet rendered + expect(find.text('Welcome!'), findsOneWidget); + expect(find.text('Two quick questions.'), findsOneWidget); + expect(find.text('What can we do better?'), findsNothing); + + await tester.tap(find.text('Get started')); + await tester.pumpAndSettle(); + + // Question 1 visible; advancing recorded no response and closed nothing + expect(find.text('What can we do better?'), findsOneWidget); + expect(find.text('Welcome!'), findsNothing); + expect(callbackLog, ['shown']); + }); + + testWidgets('does not show the intro when displayIntroScreen is off', + (tester) async { + final callbackLog = []; + await pumpSurveySheet( + tester, surveyWithIntro(displayIntroScreen: false), callbackLog); + + expect(find.text('Welcome!'), findsNothing); + expect(find.text('What can we do better?'), findsOneWidget); + }); + + testWidgets('closing from the intro screen still notifies onClosed', + (tester) async { + final callbackLog = []; + await pumpSurveySheet( + tester, surveyWithIntro(displayIntroScreen: true), callbackLog); + + await tester.tap(find.byIcon(Icons.close)); + await tester.pumpAndSettle(); + + expect(callbackLog, ['shown', 'closed']); + }); + }); +} diff --git a/posthog_flutter/test/surveys_test.dart b/posthog_flutter/test/surveys_test.dart index 8d1e4031..723cefa2 100644 --- a/posthog_flutter/test/surveys_test.dart +++ b/posthog_flutter/test/surveys_test.dart @@ -3,6 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:posthog_flutter/src/posthog_observer.dart'; import 'package:posthog_flutter/src/surveys/models/posthog_display_link_question.dart'; import 'package:posthog_flutter/src/surveys/models/posthog_display_survey.dart'; +import 'package:posthog_flutter/src/surveys/models/posthog_display_survey_text_content_type.dart'; import 'package:posthog_flutter/src/surveys/survey_service.dart'; import 'package:posthog_flutter/src/surveys/widgets/survey_bottom_sheet.dart'; @@ -160,4 +161,47 @@ void main() { expect(question.link, ''); }); }); + + group('PostHogDisplaySurvey.fromDict intro screen appearance', () { + Map surveyWithAppearance(Map appearance) { + return { + 'id': 'survey-1', + 'name': 'Test survey', + 'questions': [ + {'type': 'open', 'question': 'Feedback?', 'isOptional': false}, + ], + 'appearance': appearance, + }; + } + + test('parses the intro screen fields from the native payload', () { + final survey = PostHogDisplaySurvey.fromDict(surveyWithAppearance({ + 'displayIntroScreen': true, + 'introScreenHeader': 'Welcome!', + 'introScreenDescription': 'Two quick questions.', + 'introScreenDescriptionContentType': 0, + 'introScreenButtonText': 'Get started', + })); + + final appearance = survey.appearance!; + expect(appearance.displayIntroScreen, true); + expect(appearance.introScreenHeader, 'Welcome!'); + expect(appearance.introScreenDescription, 'Two quick questions.'); + expect(appearance.introScreenDescriptionContentType, + PostHogDisplaySurveyTextContentType.html); + expect(appearance.introScreenButtonText, 'Get started'); + }); + + test('absent intro screen keys default to disabled', () { + final survey = PostHogDisplaySurvey.fromDict(surveyWithAppearance({ + 'thankYouMessageHeader': 'Thanks!', + })); + + final appearance = survey.appearance!; + expect(appearance.displayIntroScreen, false); + expect(appearance.introScreenHeader, isNull); + expect(appearance.introScreenDescriptionContentType, + PostHogDisplaySurveyTextContentType.text); + }); + }); } From 698e5c3400f4c2d1f3a64cc892f95e1c4281d12b Mon Sep 17 00:00:00 2001 From: jake sciotto Date: Thu, 27 Aug 2026 13:28:02 -0600 Subject: [PATCH 2/4] fix(surveys): skip the intro screen when it has no copy Parity with the web renderer: the intro has no default header, so an intro with neither header nor description would draw an empty sheet with a lone button. Skips straight to question 1 instead. Generated-By: PostHog Code Task-Id: 50ceca51-4b90-4b94-8e72-e01bce50073c --- .../surveys/widgets/survey_bottom_sheet.dart | 7 +++++- .../test/survey_bottom_sheet_test.dart | 24 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/posthog_flutter/lib/src/surveys/widgets/survey_bottom_sheet.dart b/posthog_flutter/lib/src/surveys/widgets/survey_bottom_sheet.dart index 3c3d215b..dc1ce6c1 100644 --- a/posthog_flutter/lib/src/surveys/widgets/survey_bottom_sheet.dart +++ b/posthog_flutter/lib/src/surveys/widgets/survey_bottom_sheet.dart @@ -44,8 +44,13 @@ class _SurveyBottomSheetState extends State { bool _isCompleted = false; // Advancing past the intro is a pure UI transition: no response is recorded // and no survey event is sent. The X button keeps closing the survey. + // The intro has no default header, so an intro with no copy at all is + // skipped instead of drawing an empty sheet with a lone button. late bool _showingIntroScreen = - widget.survey.appearance?.displayIntroScreen ?? false; + (widget.survey.appearance?.displayIntroScreen ?? false) && + ((widget.survey.appearance?.introScreenHeader?.isNotEmpty ?? false) || + (widget.survey.appearance?.introScreenDescription?.isNotEmpty ?? + false)); @override void initState() { diff --git a/posthog_flutter/test/survey_bottom_sheet_test.dart b/posthog_flutter/test/survey_bottom_sheet_test.dart index 3505cb20..60c56f7e 100644 --- a/posthog_flutter/test/survey_bottom_sheet_test.dart +++ b/posthog_flutter/test/survey_bottom_sheet_test.dart @@ -8,7 +8,11 @@ import 'package:posthog_flutter/src/surveys/models/survey_callbacks.dart'; import 'package:posthog_flutter/src/surveys/widgets/survey_bottom_sheet.dart'; void main() { - PostHogDisplaySurvey surveyWithIntro({required bool displayIntroScreen}) { + PostHogDisplaySurvey surveyWithIntro({ + required bool displayIntroScreen, + String? header = 'Welcome!', + String? description = 'Two quick questions.', + }) { return PostHogDisplaySurvey.fromDict({ 'id': 'survey-1', 'name': 'Test survey', @@ -21,8 +25,8 @@ void main() { ], 'appearance': { 'displayIntroScreen': displayIntroScreen, - 'introScreenHeader': 'Welcome!', - 'introScreenDescription': 'Two quick questions.', + if (header != null) 'introScreenHeader': header, + if (description != null) 'introScreenDescription': description, 'introScreenDescriptionContentType': 1, 'introScreenButtonText': 'Get started', }, @@ -91,6 +95,20 @@ void main() { expect(find.text('What can we do better?'), findsOneWidget); }); + testWidgets( + 'skips the intro when it has neither a header nor a description', + (tester) async { + final callbackLog = []; + await pumpSurveySheet( + tester, + surveyWithIntro( + displayIntroScreen: true, header: null, description: null), + callbackLog); + + expect(find.text('Get started'), findsNothing); + expect(find.text('What can we do better?'), findsOneWidget); + }); + testWidgets('closing from the intro screen still notifies onClosed', (tester) async { final callbackLog = []; From fa8c6b42ccdb5370a49b13db002dd88d4a104093 Mon Sep 17 00:00:00 2001 From: jake sciotto Date: Thu, 27 Aug 2026 13:28:04 -0600 Subject: [PATCH 3/4] test(surveys): find the close button by IconButton on the intro screen The sheet now draws its close affordance with the custom-painted SurveyIcon rather than a Material Icons glyph, so find.byIcon no longer matches. Locate the button by IconButton, matching the existing survey tests. Generated-By: PostHog Desktop Task-Id: de8974e0-537f-4189-a799-66a7bc98cb10 --- posthog_flutter/test/survey_bottom_sheet_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog_flutter/test/survey_bottom_sheet_test.dart b/posthog_flutter/test/survey_bottom_sheet_test.dart index 60c56f7e..cffb0d9f 100644 --- a/posthog_flutter/test/survey_bottom_sheet_test.dart +++ b/posthog_flutter/test/survey_bottom_sheet_test.dart @@ -115,7 +115,7 @@ void main() { await pumpSurveySheet( tester, surveyWithIntro(displayIntroScreen: true), callbackLog); - await tester.tap(find.byIcon(Icons.close)); + await tester.tap(find.byType(IconButton)); await tester.pumpAndSettle(); expect(callbackLog, ['shown', 'closed']); From 47864ae7fc39cdd02f3120f36e7a2bdc82a09024 Mon Sep 17 00:00:00 2001 From: jake sciotto Date: Sun, 30 Aug 2026 18:36:33 -0400 Subject: [PATCH 4/4] fix(surveys): bump the spm posthog-ios pin to 3.70.0 the podspec was bumped to >= 3.70.0 for the intro screen appearance fields but Package.swift still allowed 3.69.x, so the spm build resolved an older posthog-ios and failed to compile PostHogDisplaySurvey+Dict.swift against the missing displayIntroScreen, introScreenHeader, introScreenDescription, introScreenDescriptionContentType and introScreenButtonText members. Generated-By: PostHog Desktop Task-Id: e5eeb291-dc63-454b-8b53-384816ef6f78 --- posthog_flutter/darwin/posthog_flutter/Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog_flutter/darwin/posthog_flutter/Package.swift b/posthog_flutter/darwin/posthog_flutter/Package.swift index 56e24a8f..e4a24d60 100644 --- a/posthog_flutter/darwin/posthog_flutter/Package.swift +++ b/posthog_flutter/darwin/posthog_flutter/Package.swift @@ -14,7 +14,7 @@ let package = Package( ], dependencies: [ .package(name: "FlutterFramework", path: "../FlutterFramework"), - .package(url: "https://github.com/PostHog/posthog-ios", "3.69.0" ..< "4.0.0"), + .package(url: "https://github.com/PostHog/posthog-ios", "3.70.0" ..< "4.0.0"), ], targets: [ .target(