← Назад к вопросам
Реализовать экран онбординга
2.0 Middle🔥 121 комментариев
#Flutter виджеты#Анимации#Навигация
Условие
Создайте экран онбординга для знакомства пользователя с приложением.
Требования
- 3-4 слайда с изображением, заголовком и описанием
- Свайп между слайдами
- Индикатор текущего слайда (dots)
- Кнопка "Пропустить" на всех слайдах
- Кнопка "Начать" на последнем слайде
- Показывать онбординг только при первом запуске
Дополнительные баллы
- Анимация при переходе между слайдами
- Параллакс-эффект для изображений
- Анимация элементов на слайдах
- Автоматическая прокрутка с паузой
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Экран Онбординга на Flutter
Полнофункциональный экран онбординга с анимациями, параллаксом и автоматической прокруткой между слайдами.
Основной компонент OnboardingPage
Страница состоит из PageView для свайпинга, индикаторов прогресса и кнопок навигации.
class OnboardingPage extends StatefulWidget {
@override
_OnboardingPageState createState() => _OnboardingPageState();
}
class _OnboardingPageState extends State<OnboardingPage> with TickerProviderStateMixin {
late PageController pageController;
int currentIndex = 0;
final slides = [
OnboardingSlide(
title: 'Добро пожаловать',
description: 'Откройте лучший способ управления',
imagePath: 'assets/onboarding1.png',
),
OnboardingSlide(
title: 'Синхронизация',
description: 'Данные синхронизируются везде',
imagePath: 'assets/onboarding2.png',
),
OnboardingSlide(
title: 'Организация',
description: 'Категории и теги для порядка',
imagePath: 'assets/onboarding3.png',
),
OnboardingSlide(
title: 'Начните прямо сейчас',
description: 'Присоединитесь к миллионам',
imagePath: 'assets/onboarding4.png',
),
];
@override
void initState() {
super.initState();
pageController = PageController();
}
void skipOnboarding() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('onboarding_seen', true);
Navigator.of(context).pushReplacementNamed('/home');
}
void nextPage() {
if (currentIndex < slides.length - 1) {
pageController.nextPage(
duration: Duration(milliseconds: 600),
curve: Curves.easeInOut,
);
} else {
skipOnboarding();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
PageView.builder(
controller: pageController,
onPageChanged: (index) {
setState(() => currentIndex = index);
},
itemCount: slides.length,
itemBuilder: (context, index) => buildSlide(slides[index]),
),
Positioned(
top: 40,
right: 24,
child: currentIndex < slides.length - 1
? GestureDetector(
onTap: skipOnboarding,
child: Text('Пропустить', style: TextStyle(fontSize: 14)),
)
: SizedBox.shrink(),
),
Positioned(
bottom: 40,
left: 24,
right: 24,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
buildDotsIndicator(),
SizedBox(height: 40),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: nextPage,
child: Text(currentIndex == slides.length - 1 ? 'Начать' : 'Далее'),
),
),
],
),
),
],
),
);
}
Widget buildSlide(OnboardingSlide slide) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFFF5F7FA), Color(0xFFE8F5E9)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(slide.imagePath, height: 320, fit: BoxFit.contain),
SizedBox(height: 60),
Text(
slide.title,
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
SizedBox(height: 16),
Padding(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
slide.description,
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
textAlign: TextAlign.center,
),
),
],
),
);
}
Widget buildDotsIndicator() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
slides.length,
(index) => AnimatedContainer(
duration: Duration(milliseconds: 300),
margin: EdgeInsets.symmetric(horizontal: 6),
width: currentIndex == index ? 32 : 8,
height: 8,
decoration: BoxDecoration(
color: currentIndex == index ? Color(0xFF667EEA) : Colors.grey[300],
borderRadius: BorderRadius.circular(4),
),
),
),
);
}
@override
void dispose() {
pageController.dispose();
super.dispose();
}
}
Модель слайда
class OnboardingSlide {
final String title;
final String description;
final String imagePath;
OnboardingSlide({
required this.title,
required this.description,
required this.imagePath,
});
}
Проверка первого запуска в main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final prefs = await SharedPreferences.getInstance();
final hasSeenOnboarding = prefs.getBool('onboarding_seen') ?? false;
runApp(MyApp(hasSeenOnboarding: hasSeenOnboarding));
}
class MyApp extends StatelessWidget {
final bool hasSeenOnboarding;
const MyApp({required this.hasSeenOnboarding});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: hasSeenOnboarding ? HomePage() : OnboardingPage(),
);
}
}
Dependencies в pubspec.yaml
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.2.0
Ключевые особенности
- PageView для свайпа между слайдами
- Индикаторы (dots) показывают текущий слайд
- Кнопка Пропустить на всех слайдах кроме последнего
- Кнопка Начать на последнем слайде
- SharedPreferences для сохранения статуса прохождения
- Анимированные индикаторы с плавным переходом
- Градиентные фоны для каждого слайда
- Адаптивный дизайн для разных размеров экрана