온보딩 화면은 사용자에게 앱의 주요 기능을 소개하고, 초기 사용 방법을 안내하는 중요한 역할을 합니다. Flutter에서 온보딩 화면을 구현하면 사용자 경험을 향상시키고, 앱에 대한 이해도를 높일 수 있습니다. 이번 글에서는 Flutter에서 온보딩 화면을 구현하는 방법과 이를 커스터마이징하는 예제를 자세히 살펴보겠습니다.
1. 기본 온보딩 화면 구성
온보딩 화면을 구현하기 위해 page_view 위젯을 사용합니다. 이 위젯을 사용하면 페이지를 슬라이드 형태로 전환할 수 있습니다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: OnboardingScreen(),
);
}
}
class OnboardingScreen extends StatefulWidget {
@override
_OnboardingScreenState createState() => _OnboardingScreenState();
}
class _OnboardingScreenState extends State<OnboardingScreen> {
final PageController _pageController = PageController();
int _currentPage = 0;
List<Widget> _buildPageContent() {
return [
_buildPage(
title: "Welcome",
description: "This is an introduction to our app.",
imageUrl: "https://via.placeholder.com/300",
),
_buildPage(
title: "Feature 1",
description: "Here we describe the first feature of the app.",
imageUrl: "https://via.placeholder.com/300",
),
_buildPage(
title: "Feature 2",
description: "Here we describe the second feature of the app.",
imageUrl: "https://via.placeholder.com/300",
),
];
}
Widget _buildPage({required String title, required String description, required String imageUrl}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.network(imageUrl, height: 300),
SizedBox(height: 20),
Text(title, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
SizedBox(height: 10),
Text(description, textAlign: TextAlign.center, style: TextStyle(fontSize: 16)),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
children: _buildPageContent(),
),
Positioned(
bottom: 30,
left: 16,
right: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_currentPage == 0
? SizedBox(width: 60) // Invisible widget for alignment
: TextButton(
onPressed: () {
_pageController.previousPage(duration: Duration(milliseconds: 300), curve: Curves.ease);
},
child: Text("Back"),
),
Row(
children: List.generate(3, (index) => _buildIndicator(index == _currentPage)),
),
_currentPage == 2
? TextButton(
onPressed: () {
// Implement navigation to the main screen
},
child: Text("Done"),
)
: TextButton(
onPressed: () {
_pageController.nextPage(duration: Duration(milliseconds: 300), curve: Curves.ease);
},
child: Text("Next"),
),
],
),
),
],
),
);
}
Widget _buildIndicator(bool isActive) {
return AnimatedContainer(
duration: Duration(milliseconds: 150),
margin: EdgeInsets.symmetric(horizontal: 8),
height: 8,
width: isActive ? 24 : 16,
decoration: BoxDecoration(
color: isActive ? Colors.blue : Colors.grey,
borderRadius: BorderRadius.circular(12),
),
);
}
}
위 코드는 기본적인 온보딩 화면을 구현한 예제입니다. PageView 위젯을 사용하여 페이지를 슬라이드 형태로 전환하며, 각 페이지는 이미지, 제목, 설명으로 구성됩니다. 하단에는 페이지 인디케이터와 탐색 버튼이 있습니다.
2. 커스텀 온보딩 화면 구성
온보딩 화면을 더욱 풍부하게 만들기 위해 커스텀 애니메이션과 디자인 요소를 추가할 수 있습니다. 다음은 페이지 전환 시 애니메이션 효과를 추가한 예제입니다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CustomOnboardingScreen(),
);
}
}
class CustomOnboardingScreen extends StatefulWidget {
@override
_CustomOnboardingScreenState createState() => _CustomOnboardingScreenState();
}
class _CustomOnboardingScreenState extends State<CustomOnboardingScreen> {
final PageController _pageController = PageController();
int _currentPage = 0;
List<Widget> _buildPageContent() {
return [
_buildPage(
title: "Welcome",
description: "This is an introduction to our app.",
imageUrl: "https://via.placeholder.com/300",
),
_buildPage(
title: "Feature 1",
description: "Here we describe the first feature of the app.",
imageUrl: "https://via.placeholder.com/300",
),
_buildPage(
title: "Feature 2",
description: "Here we describe the second feature of the app.",
imageUrl: "https://via.placeholder.com/300",
),
];
}
Widget _buildPage({required String title, required String description, required String imageUrl}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.network(imageUrl, height: 300),
SizedBox(height: 20),
Text(title, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
SizedBox(height: 10),
Text(description, textAlign: TextAlign.center, style: TextStyle(fontSize: 16)),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
PageView.builder(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
itemCount: _buildPageContent().length,
itemBuilder: (context, index) {
return AnimatedBuilder(
animation: _pageController,
builder: (context, child) {
double value = 1.0;
if (_pageController.position.haveDimensions) {
value = _pageController.page! - index;
value = (1 - (value.abs() * 0.3)).clamp(0.0, 1.0);
}
return Transform(
transform: Matrix4.identity()..scale(value, value),
child: _buildPageContent()[index],
);
},
);
},
),
Positioned(
bottom: 30,
left: 16,
right: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_currentPage == 0
? SizedBox(width: 60)
: TextButton(
onPressed: () {
_pageController.previousPage(duration: Duration(milliseconds: 300), curve: Curves.ease);
},
child: Text("Back"),
),
Row(
children: List.generate(3, (index) => _buildIndicator(index == _currentPage)),
),
_currentPage == 2
? TextButton(
onPressed: () {
// Implement navigation to the main screen
},
child: Text("Done"),
)
: TextButton(
onPressed: () {
_pageController.nextPage(duration: Duration(milliseconds: 300), curve: Curves.ease);
},
child: Text("Next"),
),
],
),
),
],
),
);
}
Widget _buildIndicator(bool isActive) {
return AnimatedContainer(
duration: Duration(milliseconds: 150),
margin: EdgeInsets.symmetric(horizontal: 8),
height: 8,
width: isActive ? 24 : 16,
decoration: BoxDecoration(
color: isActive ? Colors.blue : Colors.grey,
borderRadius: BorderRadius.circular(12),
),
);
}
}
위 코드는 페이지 전환 시 애니메이션 효과를 추가한 온보딩 화면 예제입니다. PageView.builder와 AnimatedBuilder를 사용하여 페이지 전환 시 크기가 변화하는 애니메이션을 구현합니다.
3. 온보딩 완료 처리
온보딩이 완료되면 메인 화면으로 이동하거나, 온보딩 완료 여부를 로컬 저장소에 저장하여 다시 표시되지 않도록 할 수 있습니다. 다음은 온보딩 완료 여부를 SharedPreferences에 저장하는 예제입니다.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
bool seenOnboarding = prefs.getBool('seenOnboarding') ?? false;
runApp(MyApp(seenOnboarding: seenOnboarding));
}
class MyApp extends StatelessWidget {
final bool seenOnboarding;
MyApp({required this.seenOnboarding});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: seenOnboarding ? MainScreen() : OnboardingScreen(),
);
}
}
class OnboardingScreen extends StatefulWidget {
@override
_OnboardingScreenState createState() => _OnboardingScreenState();
}
class _OnboardingScreenState extends State<OnboardingScreen> {
final PageController _pageController = PageController();
int _currentPage = 0;
List<Widget> _buildPageContent() {
return [
_buildPage(
title: "Welcome",
description: "This is an introduction to our app.",
imageUrl: "https://via.placeholder.com/300",
),
_buildPage(
title: "Feature 1",
description: "Here we describe the first feature of the app.",
imageUrl: "https://via.placeholder.com/300",
),
_buildPage(
title: "Feature 2",
description: "Here we describe the second feature of the app.",
imageUrl: "https://via.placeholder.com/300",
),
];
}
Widget _buildPage({required String title, required String description, required String imageUrl}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.network(imageUrl, height: 300),
SizedBox(height: 20),
Text(title, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
SizedBox(height: 10),
Text(description, textAlign: TextAlign.center, style: TextStyle(fontSize: 16)),
],
);
}
void _completeOnboarding() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool('seenOnboarding', true);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => MainScreen()),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
children: _buildPageContent(),
),
Positioned(
bottom: 30,
left: 16,
right: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_currentPage == 0
? SizedBox(width: 60)
: TextButton(
onPressed: () {
_pageController.previousPage(duration: Duration(milliseconds: 300), curve: Curves.ease);
},
child: Text("Back"),
),
Row(
children: List.generate(3, (index) => _buildIndicator(index == _currentPage)),
),
_currentPage == 2
? TextButton(
onPressed: _completeOnboarding,
child: Text("Done"),
)
: TextButton(
onPressed: () {
_pageController.nextPage(duration: Duration(milliseconds: 300), curve: Curves.ease);
},
child: Text("Next"),
),
],
),
),
],
),
);
}
Widget _buildIndicator(bool isActive) {
return AnimatedContainer(
duration: Duration(milliseconds: 150),
margin: EdgeInsets.symmetric(horizontal: 8),
height: 8,
width: isActive ? 24 : 16,
decoration: BoxDecoration(
color: isActive ? Colors.blue : Colors.grey,
borderRadius: BorderRadius.circular(12),
),
);
}
}
class MainScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Main Screen')),
body: Center(child: Text('Welcome to the Main Screen!')),
);
}
}
위 코드는 온보딩 완료 여부를 SharedPreferences에 저장하고, 온보딩이 완료되면 메인 화면으로 이동하는 예제입니다. 앱이 처음 실행될 때 온보딩 화면을 표시하고, 이후에는 메인 화면을 표시하도록 설정합니다.
결론
Flutter에서 온보딩 화면을 구현하면 사용자에게 앱의 주요 기능을 소개하고 초기 사용 방법을 안내할 수 있습니다. PageView 위젯을 사용하여 슬라이드 형태의 온보딩 화면을 만들고, 다양한 커스텀 옵션을 통해 사용자 경험을 향상시킬 수 있습니다. 이번 글에서 소개한 방법들을 활용하여 Flutter 애플리케이션에서 매력적이고 효과적인 온보딩 화면을 구현해보세요. 온보딩 화면을 통해 사용자에게 앱의 가치를 효과적으로 전달할 수 있습니다.
'Flutter' 카테고리의 다른 글
Flutter의 Splash Screen 만들기 (0) | 2024.11.09 |
---|---|
Flutter의 캐러셀(Carousel) 위젯 사용법 (0) | 2024.10.15 |
Flutter의 Rive 애니메이션 사용법 (3) | 2024.10.15 |
Flutter의 Flare 애니메이션 사용법 (1) | 2024.10.14 |
Flutter의 애니메이션 라이브러리(Animations Library) 사용법 (1) | 2024.10.14 |