From 4eb651891c807954c963fa5b97264915a518d6a2 Mon Sep 17 00:00:00 2001 From: Jordan Nnabugwu Date: Mon, 22 Dec 2025 10:54:13 -0800 Subject: [PATCH 1/4] feat: added the imports page to the onboarding flow --- .../onboarding_integrations_wrapper.dart | 72 +++++++++++++++++++ app/lib/pages/onboarding/wrapper.dart | 69 ++++++++++++------ app/lib/pages/settings/settings_drawer.dart | 2 +- 3 files changed, 120 insertions(+), 23 deletions(-) create mode 100644 app/lib/pages/onboarding/integrations/onboarding_integrations_wrapper.dart diff --git a/app/lib/pages/onboarding/integrations/onboarding_integrations_wrapper.dart b/app/lib/pages/onboarding/integrations/onboarding_integrations_wrapper.dart new file mode 100644 index 0000000000..4122213ca9 --- /dev/null +++ b/app/lib/pages/onboarding/integrations/onboarding_integrations_wrapper.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:omi/pages/settings/integrations_page.dart'; + +class OnboardingIntegrationsWrapper extends StatelessWidget { + final VoidCallback goNext; + final VoidCallback onSkip; + final VoidCallback goBack; + + const OnboardingIntegrationsWrapper({ + super.key, + required this.goNext, + required this.onSkip, + required this.goBack, + }); + + @override + Widget build(BuildContext context) { + return IntegrationsPage( + hideAppBar: false, + onBackPressed: goBack, + bottomWidget: Padding( + padding: const EdgeInsets.only(top: 8), + child: Row( + children: [ + Expanded( + child: TextButton( + onPressed: onSkip, + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + 'Skip', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + flex: 2, + child: ElevatedButton( + onPressed: goNext, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: Colors.black, + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: const Text( + 'Continue', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + ), + ); + } +} + diff --git a/app/lib/pages/onboarding/wrapper.dart b/app/lib/pages/onboarding/wrapper.dart index a75c1147db..f1e9de0b07 100644 --- a/app/lib/pages/onboarding/wrapper.dart +++ b/app/lib/pages/onboarding/wrapper.dart @@ -16,6 +16,7 @@ import 'package:omi/pages/onboarding/speech_profile_widget.dart'; import 'package:omi/pages/onboarding/user_review_page.dart'; import 'package:omi/pages/onboarding/welcome/page.dart'; import 'package:omi/pages/onboarding/device_onboarding/device_onboarding_wrapper.dart'; +import 'package:omi/pages/onboarding/integrations/onboarding_integrations_wrapper.dart'; import 'package:omi/providers/home_provider.dart'; import 'package:omi/providers/onboarding_provider.dart'; import 'package:omi/services/auth_service.dart'; @@ -39,13 +40,14 @@ class _OnboardingWrapperState extends State with TickerProvid static const int kNamePage = 1; static const int kPrimaryLanguagePage = 2; static const int kPermissionsPage = 3; - static const int kUserReviewPage = 4; - static const int kWelcomePage = 5; - static const int kFindDevicesPage = 6; - static const int kSpeechProfilePage = 7; // Now always the last index + static const int kImportPage = 4; + static const int kUserReviewPage = 5; + static const int kWelcomePage = 6; + static const int kFindDevicesPage = 7; + static const int kSpeechProfilePage = 8; // Now always the last index // Special index values used in comparisons - static const List kHiddenHeaderPages = [-1, 0, 1, 2, 3, 4, 5, 6, 7]; + static const List kHiddenHeaderPages = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]; TabController? _controller; late AnimationController _backgroundAnimationController; @@ -55,7 +57,7 @@ class _OnboardingWrapperState extends State with TickerProvid @override void initState() { - _controller = TabController(length: 8, vsync: this); + _controller = TabController(length: 9, vsync: this); _controller!.addListener(() { setState(() {}); // Update background image when page changes @@ -132,6 +134,9 @@ class _OnboardingWrapperState extends State with TickerProvid case kPermissionsPage: newImage = Assets.images.onboardingBg3.path; break; + case kImportPage: + newImage = Assets.images.onboardingBg3.path; + break; case kUserReviewPage: newImage = Assets.images.onboardingBg6.path; break; @@ -175,6 +180,8 @@ class _OnboardingWrapperState extends State with TickerProvid return Assets.images.onboardingBg4.path; case kPermissionsPage: return Assets.images.onboardingBg3.path; + case kImportPage: + return Assets.images.onboardingBg3.path; case kUserReviewPage: return Assets.images.onboardingBg6.path; default: @@ -223,10 +230,24 @@ class _OnboardingWrapperState extends State with TickerProvid }), PermissionsWidget( goNext: () { - _goNext(); // Go to User Review page + _goNext(); // Go to Integrations page MixpanelManager().onboardingStepCompleted('Permissions'); }, ), + OnboardingIntegrationsWrapper( + goNext: () { + _goNext(); // Go to User Review page + MixpanelManager().onboardingStepCompleted('Integrations'); + }, + onSkip: () { + _goNext(); // Go to User Review page + MixpanelManager().onboardingStepCompleted('Integrations Skipped'); + }, + goBack: () { + // Go back to Permissions page + _controller!.animateTo(kPermissionsPage); + }, + ), UserReviewPage( goNext: () { _goNext(); // Go to Welcome page @@ -313,7 +334,9 @@ class _OnboardingWrapperState extends State with TickerProvid pages[kAuthPage], ], ) - : _controller!.index == kNamePage || + : _controller!.index == kImportPage + ? pages[_controller!.index] + : _controller!.index == kNamePage || _controller!.index == kPrimaryLanguagePage || _controller!.index == kPermissionsPage || _controller!.index == kUserReviewPage || @@ -348,9 +371,9 @@ class _OnboardingWrapperState extends State with TickerProvid child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( - 7, + 8, (index) { - int pageIndex = index + 1; // Name=1, Lang=2, ..., Speech=7 + int pageIndex = index + 1; // Name=1, Lang=2, ..., Speech=8 return Container( margin: const EdgeInsets.symmetric(horizontal: 4.0), width: pageIndex == _controller!.index ? 12.0 : 8.0, @@ -403,16 +426,18 @@ class _OnboardingWrapperState extends State with TickerProvid shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: [ - Consumer( - builder: (context, onboardingProvider, child) { - return DeviceAnimationWidget( - animatedBackground: _controller!.index != -1 && onboardingProvider.isConnected, - isConnected: onboardingProvider.isConnected, - deviceName: onboardingProvider.deviceName, - ); - }, - ), - const SizedBox(height: 24), + // Hide device animation for integrations page + if (_controller!.index != kImportPage) + Consumer( + builder: (context, onboardingProvider, child) { + return DeviceAnimationWidget( + animatedBackground: _controller!.index != -1 && onboardingProvider.isConnected, + isConnected: onboardingProvider.isConnected, + deviceName: onboardingProvider.deviceName, + ); + }, + ), + if (_controller!.index != kImportPage) const SizedBox(height: 24), kHiddenHeaderPages.contains(_controller?.index) ? const SizedBox.shrink() : Padding( @@ -473,9 +498,9 @@ class _OnboardingWrapperState extends State with TickerProvid child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( - 6, + 8, (index) { - int pageIndex = index + 1; // Name=1, Lang=2, ..., Speech=6 + int pageIndex = index + 1; // Name=1, Lang=2, ..., Speech=8 return Container( margin: const EdgeInsets.symmetric(horizontal: 4.0), width: pageIndex == _controller!.index ? 12.0 : 8.0, diff --git a/app/lib/pages/settings/settings_drawer.dart b/app/lib/pages/settings/settings_drawer.dart index 5439b0a0f8..113933cabd 100644 --- a/app/lib/pages/settings/settings_drawer.dart +++ b/app/lib/pages/settings/settings_drawer.dart @@ -360,7 +360,7 @@ class _SettingsDrawerState extends State { const Divider(height: 1, color: Color(0xFF3C3C43)), _buildSettingsItem( title: 'Offline Sync', - icon: const FaIcon(FontAwesomeIcons.solidCloud, color: Color(0xFF8E8E93), size: 20), + icon: const FaIcon(FontAwesomeIcons.cloud, color: Color(0xFF8E8E93), size: 20), onTap: () { Navigator.pop(context); Navigator.of(context).push( From 5e365eac309b11a7f7eaddf0c0f0e75b871495f7 Mon Sep 17 00:00:00 2001 From: Jordan Nnabugwu Date: Mon, 22 Dec 2025 10:57:39 -0800 Subject: [PATCH 2/4] revert back --- app/lib/pages/settings/settings_drawer.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/pages/settings/settings_drawer.dart b/app/lib/pages/settings/settings_drawer.dart index 113933cabd..5439b0a0f8 100644 --- a/app/lib/pages/settings/settings_drawer.dart +++ b/app/lib/pages/settings/settings_drawer.dart @@ -360,7 +360,7 @@ class _SettingsDrawerState extends State { const Divider(height: 1, color: Color(0xFF3C3C43)), _buildSettingsItem( title: 'Offline Sync', - icon: const FaIcon(FontAwesomeIcons.cloud, color: Color(0xFF8E8E93), size: 20), + icon: const FaIcon(FontAwesomeIcons.solidCloud, color: Color(0xFF8E8E93), size: 20), onTap: () { Navigator.pop(context); Navigator.of(context).push( From 531babbda4729fbf05ec569f254be648106a7a61 Mon Sep 17 00:00:00 2001 From: Jordan Nnabugwu Date: Mon, 22 Dec 2025 11:30:28 -0800 Subject: [PATCH 3/4] feat: made pr updates --- .../onboarding_integrations_wrapper.dart | 1 - app/lib/pages/settings/integrations_page.dart | 52 ++++++++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/app/lib/pages/onboarding/integrations/onboarding_integrations_wrapper.dart b/app/lib/pages/onboarding/integrations/onboarding_integrations_wrapper.dart index 4122213ca9..91813ca7ad 100644 --- a/app/lib/pages/onboarding/integrations/onboarding_integrations_wrapper.dart +++ b/app/lib/pages/onboarding/integrations/onboarding_integrations_wrapper.dart @@ -16,7 +16,6 @@ class OnboardingIntegrationsWrapper extends StatelessWidget { @override Widget build(BuildContext context) { return IntegrationsPage( - hideAppBar: false, onBackPressed: goBack, bottomWidget: Padding( padding: const EdgeInsets.only(top: 8), diff --git a/app/lib/pages/settings/integrations_page.dart b/app/lib/pages/settings/integrations_page.dart index d40d1371ad..a685a32257 100644 --- a/app/lib/pages/settings/integrations_page.dart +++ b/app/lib/pages/settings/integrations_page.dart @@ -115,7 +115,16 @@ extension IntegrationAppExtension on IntegrationApp { } class IntegrationsPage extends StatefulWidget { - const IntegrationsPage({super.key}); + final bool hideAppBar; + final VoidCallback? onBackPressed; + final Widget? bottomWidget; + + const IntegrationsPage({ + super.key, + this.hideAppBar = false, + this.onBackPressed, + this.bottomWidget, + }); @override State createState() => _IntegrationsPageState(); @@ -598,23 +607,25 @@ class _IntegrationsPageState extends State with WidgetsBinding return Scaffold( backgroundColor: const Color(0xFF000000), - appBar: AppBar( - backgroundColor: const Color(0xFF000000), - elevation: 0, - leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () => Navigator.pop(context), - ), - title: const Text( - 'Chat Tools', - style: TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.w600, - ), - ), - centerTitle: true, - ), + appBar: widget.hideAppBar + ? null + : AppBar( + backgroundColor: const Color(0xFF000000), + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: widget.onBackPressed ?? () => Navigator.pop(context), + ), + title: const Text( + 'Chat Tools', + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + centerTitle: true, + ), body: SafeArea( child: Padding( padding: const EdgeInsets.all(20), @@ -660,6 +671,11 @@ class _IntegrationsPageState extends State with WidgetsBinding ], ), ), + // Optional bottom widget (for onboarding buttons) + if (widget.bottomWidget != null) ...[ + const SizedBox(height: 24), + widget.bottomWidget!, + ], ], ), ), From 1632f914dbc0787dd16d7cdcb014ced6568b1cb1 Mon Sep 17 00:00:00 2001 From: Jordan Nnabugwu Date: Mon, 22 Dec 2025 11:41:31 -0800 Subject: [PATCH 4/4] Update app/lib/pages/onboarding/wrapper.dart Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- app/lib/pages/onboarding/wrapper.dart | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/app/lib/pages/onboarding/wrapper.dart b/app/lib/pages/onboarding/wrapper.dart index f1e9de0b07..f839951b25 100644 --- a/app/lib/pages/onboarding/wrapper.dart +++ b/app/lib/pages/onboarding/wrapper.dart @@ -426,18 +426,16 @@ class _OnboardingWrapperState extends State with TickerProvid shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: [ - // Hide device animation for integrations page - if (_controller!.index != kImportPage) - Consumer( - builder: (context, onboardingProvider, child) { - return DeviceAnimationWidget( - animatedBackground: _controller!.index != -1 && onboardingProvider.isConnected, - isConnected: onboardingProvider.isConnected, - deviceName: onboardingProvider.deviceName, - ); - }, - ), - if (_controller!.index != kImportPage) const SizedBox(height: 24), + Consumer( + builder: (context, onboardingProvider, child) { + return DeviceAnimationWidget( + animatedBackground: _controller!.index != -1 && onboardingProvider.isConnected, + isConnected: onboardingProvider.isConnected, + deviceName: onboardingProvider.deviceName, + ); + }, + ), + const SizedBox(height: 24), kHiddenHeaderPages.contains(_controller?.index) ? const SizedBox.shrink() : Padding(