Merge pull request #671 from Udhay-Adithya/add-feature-onboarding-screen

feat: add onboarding screen (#651)
This commit is contained in:
Ankit Mahato
2025-04-01 07:17:16 +05:30
committed by GitHub
11 changed files with 289 additions and 2 deletions

1
assets/api_server.json Normal file

File diff suppressed because one or more lines are too long

1
assets/files.json Normal file

File diff suppressed because one or more lines are too long

1
assets/generate.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -138,7 +138,25 @@ class DashApp extends ConsumerWidget {
!kIsLinux && !kIsMobile
? const App()
: context.isMediumWindow
? const MobileDashboard()
? (kIsMobile
? FutureBuilder<bool>(
future: getOnboardingStatusFromSharedPrefs(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
debugPrint(
"showOnboarding: ${snapshot.data.toString()}");
final showOnboarding =
snapshot.data ?? false;
return showOnboarding
? const MobileDashboard()
: const OnboardingScreen();
}
return const Center(
child: CircularProgressIndicator());
},
)
: const MobileDashboard())
: const Dashboard(),
if (kIsWindows)
SizedBox(

View File

@ -13,6 +13,9 @@ const kAssetIntroMd = "assets/intro.md";
const kAssetSendingLottie = "assets/sending.json";
const kAssetSavingLottie = "assets/saving.json";
const kAssetSavedLottie = "assets/completed.json";
const kAssetGenerateCodeLottie = "assets/generate.json";
const kAssetApiServerLottie = "assets/api_server.json";
const kAssetFolderLottie = "assets/files.json";
final kIsMacOS = !kIsWeb && Platform.isMacOS;
final kIsWindows = !kIsWeb && Platform.isWindows;

View File

@ -1 +1,2 @@
export 'dashboard.dart';
export 'onboarding_screen.dart';

View File

@ -0,0 +1,170 @@
import 'package:apidash/consts.dart';
import 'package:apidash/screens/mobile/widgets/onboarding_slide.dart';
import 'package:apidash/screens/screens.dart';
import 'package:apidash/services/services.dart';
import 'package:apidash_design_system/apidash_design_system.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
class OnboardingScreen extends StatefulWidget {
const OnboardingScreen({super.key});
@override
State<OnboardingScreen> createState() => _OnboardingScreenState();
}
class _OnboardingScreenState extends State<OnboardingScreen> {
int currentPageIndex = 0;
final CarouselSliderController _carouselController =
CarouselSliderController();
void _onNextPressed() {
if (currentPageIndex < 2) {
_carouselController.nextPage(
duration: const Duration(milliseconds: 600),
curve: Curves.ease,
);
} else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => MobileDashboard(),
),
(route) => false,
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.surface,
actions: [
TextButton(
onPressed: () async {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => MobileDashboard(),
),
(route) => false,
);
await setOnboardingStatusToSharedPrefs(
isOnboardingComplete: true,
);
},
child: const Text(
'Skip',
),
),
],
),
body: Container(
color: Theme.of(context).colorScheme.surface,
child: Column(
children: [
Expanded(
child: CarouselSlider(
carouselController: _carouselController,
options: CarouselOptions(
height: MediaQuery.of(context).size.height * 0.75,
viewportFraction: 1.0,
enableInfiniteScroll: false,
onPageChanged: (index, reason) {
setState(() {
currentPageIndex = index;
});
},
),
items: [
OnboardingSlide(
context: context,
assetPath: kAssetApiServerLottie,
assetSize: context.width * 0.75,
title: "Test APIs with Ease",
description:
"Send requests, preview responses, and test APIs with ease. REST and GraphQL support included!",
),
OnboardingSlide(
context: context,
assetPath: kAssetFolderLottie,
assetSize: context.width * 0.55,
title: "Organize & Save Requests",
description:
"Save and organize API requests into collections for quick access and better workflow.",
),
OnboardingSlide(
context: context,
assetPath: kAssetGenerateCodeLottie,
assetSize: context.width * 0.65,
title: "Generate Code Instantly",
description:
"Integrate APIs using well tested code generators for JavaScript, Python, Dart, Kotlin & others.",
),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 36.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(3, (index) {
bool isSelected = currentPageIndex == index;
return GestureDetector(
onTap: () {
_carouselController.animateToPage(index);
},
child: AnimatedContainer(
width: isSelected ? 40 : 18,
height: 7,
margin: const EdgeInsets.symmetric(horizontal: 3),
decoration: BoxDecoration(
color: isSelected
? Theme.of(context).colorScheme.primary
: Theme.of(context)
.colorScheme
.secondaryContainer,
borderRadius: BorderRadius.circular(9),
),
duration: const Duration(milliseconds: 300),
),
);
}),
),
),
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: IconButton(
onPressed: () async {
_onNextPressed();
if (currentPageIndex == 2) {
await setOnboardingStatusToSharedPrefs(
isOnboardingComplete: true,
);
}
},
icon: const Icon(
Icons.arrow_forward_rounded,
size: 30,
),
style: IconButton.styleFrom(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(35),
),
),
),
),
],
),
const SizedBox(height: 60),
],
),
),
);
}
}

View File

@ -0,0 +1,70 @@
import 'package:apidash_design_system/apidash_design_system.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
class OnboardingSlide extends StatelessWidget {
final BuildContext context;
final String assetPath;
final double assetSize;
final String title;
final String description;
const OnboardingSlide({
required this.context,
required this.assetPath,
required this.assetSize,
required this.title,
required this.description,
super.key,
});
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(top: 75.0),
child: Center(
child: Lottie.asset(
assetPath,
renderCache: RenderCache.drawingCommands,
width: assetSize,
fit: BoxFit.cover,
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 16,
),
child: Text(
description,
textAlign: TextAlign.center,
style: kTextStyleButton.copyWith(fontSize: 16),
),
),
const SizedBox(
height: 70,
)
],
),
],
);
}
}

View File

@ -3,6 +3,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../models/models.dart';
const String kSharedPrefSettingsKey = 'apidash-settings';
const String kSharedPrefOnboardingKey = 'apidash-onboarding-status';
Future<SettingsModel?> getSettingsFromSharedPrefs() async {
final prefs = await SharedPreferences.getInstance();
@ -22,7 +23,19 @@ Future<void> setSettingsToSharedPrefs(SettingsModel settingsModel) async {
await prefs.setString(kSharedPrefSettingsKey, settingsModel.toString());
}
Future<void> setOnboardingStatusToSharedPrefs(
{required bool isOnboardingComplete}) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(kSharedPrefOnboardingKey, isOnboardingComplete);
}
Future<bool> getOnboardingStatusFromSharedPrefs() async {
final prefs = await SharedPreferences.getInstance();
final onboardingStatus = prefs.getBool(kSharedPrefOnboardingKey) ?? false;
return onboardingStatus;
}
Future<void> clearSharedPrefs() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(kSharedPrefSettingsKey);
await prefs.clear();
}

View File

@ -175,6 +175,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.9.4"
carousel_slider:
dependency: "direct main"
description:
name: carousel_slider
sha256: "7b006ec356205054af5beaef62e2221160ea36b90fb70a35e4deacd49d0349ae"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
characters:
dependency: transitive
description:

View File

@ -68,6 +68,7 @@ dependencies:
git:
url: https://github.com/google/flutter-desktop-embedding.git
path: plugins/window_size
carousel_slider: ^5.0.0
dependency_overrides:
extended_text_field: ^16.0.0