تصميم لصفحة onBoarding بإستخدام flutter مع Animation
في هذه المقالة نقدم لكم طريقة جديده لعمل صفحة onBoarding وهيا الصفحه التي تكون في بداية التطبيق وتكون في الغالب عباره عن شرح للتطبيق للمستخدمين وقد سبق وقدمنا لكم اشكال وتصاميم مختلفه وافكار ايضا في تنفيذ onBoarding ولكن في هذه المقالة نقدمها لكم ولكن بشكل مختلف حيث اننا سوف نقوم بعمل وقت معين لتنفيذ الانتقال بين كل صفحة والاخرى وايضا شريط بالاسفل يوضح المده وعندما يكتمل ينتقل الى الصفحه التي بعدها كما هو موضح وايضا يمكنك النقر على الايقونه حتى يتم الانتقال فكلا الطرق تقوم بنقلك الى الصفحه الاخرى بدون اي مشاكل .
Flutter عبارة عن حزمة SDK أمامية لأنها تجمع بين عناصر واجهة المستخدم ومنطق الأعمال. على الرغم من أن العديد من الأشخاص يدعون أن Dart يُستخدم للواجهة الخلفية ، إلا أن الحقيقة هي أن Flutter هي واجهة أمامية بالكامل وأن Dart تستخدم فقط لمنطق الواجهة الأمامية. لكن تطوير التطبيقات الأصلية لنظامي التشغيل iOS و Android هو "ببساطة" الواجهة الأمامية.
Dart و Java و C / C ++ ولغات البرمجة الأخرى ليست سوى عدد قليل من تلك التي يمكن استخدامها لإنشاء تطبيقات الواجهة الأمامية والخلفية باستخدام Flutter ، وهو SDK عبر الأنظمة الأساسية.
من ناحية أخرى ، Flutter ليست لغة برمجة. إنها مجموعة تطوير برمجيات (SDK) تتضمن كودًا مكتوبًا مسبقًا ، وعناصر واجهة مستخدم جاهزة للاستخدام يمكن تخصيصها ، بالإضافة إلى مكتبات وأدوات ووثائق يمكن استخدامها جميعًا لإنشاء تطبيقات عبر الأنظمة الأساسية. 26 مارس 2021
How to Create OnBorading with Flutter and Animation
الكود التالي عباره عن ثلاث صفحات كل صفحة تم وضعها بداخل slider وهيا تحتوي على نص وصورة وبالاسفل قمنا بعمل تصميم للايقونه التي بها المده الزمنيه وعند اكتمالها يتم الانتقال الى صفحة اخرى حتى يتم عرض جميع الصفحات بدون مشاكل .
ui.dart
import 'package:flutter/material.dart';
import 'dart:math';
class Onboard1 extends StatefulWidget {
const Onboard1({Key? key}) : super(key: key);
@override
_Onboard1State createState() => _Onboard1State();
}
class _Onboard1State extends State<Onboard1> {
@override
Widget build(BuildContext c) {
return Scaffold(
backgroundColor: Colors.brown.shade200,
body: Center(
child: Onboard1sub(),
),
);
}
}
const blue = Color(0xFF4781ff);
const kTitleStyle = TextStyle(
fontSize: 30, color: Color(0xFF01002f), fontWeight: FontWeight.bold);
const kSubtitleStyle = TextStyle(fontSize: 22, color: Color(0xFF88869f));
class Onboard1sub extends StatefulWidget {
const Onboard1sub({Key? key}) : super(key: key);
@override
_Onboard1subState createState() => _Onboard1subState();
}
class _Onboard1subState extends State<Onboard1sub> {
PageController pageController = PageController(initialPage: 0);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: PageView(
controller: pageController,
children: [
Slide(
hero: Image.network(listModel[0].photo),
title: "Boost your traffic",
subtitle:
"Outreach to many social networks to improve your statistics",
onNext: nextPage),
Slide(
hero: Image.network(listModel[1].photo),
title: "Give the best solution",
subtitle:
"We will give best solution for your business isues",
onNext: nextPage),
Slide(
hero: Image.network(listModel[2].photo),
title: "Reach the target",
subtitle:
"With our help, it will be easier to achieve your goals",
onNext: nextPage),
const Scaffold(
backgroundColor: Colors.white,
body: Center(
child: Text(
'Be kind to yourself',
style: kTitleStyle,
),
),
)
]),
),
);
}
void nextPage() {
pageController.nextPage(
duration: const Duration(milliseconds: 200), curve: Curves.ease);
}
}
class Slide extends StatelessWidget {
final Widget hero;
final String title;
final String subtitle;
final VoidCallback onNext;
const Slide({Key? key, required this.hero, required this.title, required this.subtitle, required this.onNext})
: super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: hero),
Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Text(
title,
style: kTitleStyle,
),
const SizedBox(
height: 20,
),
Text(
subtitle,
style: kSubtitleStyle,
textAlign: TextAlign.center,
),
const SizedBox(
height: 35,
),
ProgressButton(onNext: onNext),
],
),
),
GestureDetector(
onTap: onNext,
child: const Text(
"Skip",
style: kSubtitleStyle,
),
),
const SizedBox(
height: 4,
)
],
);
}
}
class ProgressButton extends StatelessWidget {
final VoidCallback onNext;
const ProgressButton({Key? key, required this.onNext}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: 75,
height: 75,
child: Stack(children: [
AnimatedIndicator(
duration: const Duration(seconds: 10),
size: 75,
callback: onNext,
),
Center(
child: GestureDetector(
child: Container(
height: 60,
width: 60,
child: const Center(
child: Icon(
Icons.navigate_next_outlined,
color: Colors.white,
size: 45,
)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(99), color: blue),
),
onTap: onNext,
),
)
]),
);
}
}
class AnimatedIndicator extends StatefulWidget {
final Duration duration;
final double size;
final VoidCallback callback;
const AnimatedIndicator({Key? key, required this.duration, required this.size, required this.callback})
: super(key: key);
@override
_AnimatedIndicatorState createState() => _AnimatedIndicatorState();
}
class _AnimatedIndicatorState extends State<AnimatedIndicator>
with TickerProviderStateMixin {
late Animation<double> animation;
late AnimationController controller;
@override
void initState() {
controller = AnimationController(duration: widget.duration, vsync: this);
animation = Tween(begin: 0.0, end: 100.0).animate(controller)
..addListener(() {
setState(() {});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reset();
widget.callback();
}
});
controller.forward();
super.initState();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (context, child) {
return CustomPaint(
size: Size(widget.size, widget.size),
painter: ProgressPainter(animation.value));
});
}
}
class ProgressPainter extends CustomPainter {
final double progress;
ProgressPainter(this.progress);
@override
void paint(Canvas canvas, Size size) {
// setup
var linePaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3
..color = blue;
var circlePaint = Paint()
..style = PaintingStyle.fill
..color = blue;
final radians = (progress / 100) * 2 * pi;
_drawArc(canvas, linePaint, circlePaint, -pi / 2, radians, size);
}
void _drawArc(Canvas canvas,
Paint linePaint,
Paint circlePaint,
double startRadian,
double sweepRadian,
Size size,) {
final centerX = size.width / 2,
centerY = size.height / 2;
final centerOffset = Offset(centerX, centerY);
final double radius = min(size.width, size.height) / 2;
canvas.drawArc(Rect.fromCircle(center: centerOffset, radius: radius),
startRadian, sweepRadian, false, linePaint);
final x = radius * (1 + sin(sweepRadian)),
y = radius * (1 - cos(sweepRadian));
final circleOffset = Offset(x, y);
canvas.drawCircle(circleOffset, 5, circlePaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}