شرح اضافة تاثير Parallax Effect على الصور في Flutter
في هذه المقالة سوف نشرح معكم واحد من اكثر التاثيرات جمالا في تطبيقات flutter ويعطي منظر رائببع لتطبيقك حيث يعمل على تاثير الصور وعمل تحريك لها اثناء عمل scroll كما هو موضح بالصورة الخاصه بالمقالة وكثير من المبرمجين يبحثون عن طريقة لاستخدام هذا التاثير في تطبيقاتهم ليعطي منظر جميل لهم ويمكنهم من تغيير الطريقة التقليديه لعرض التصميم الخاص بالصور في تطبيقاتهم والامر بسيط جدا جدا وسوفف نشرحه لكم في هذه المقالة .
إن حرفة تطوير تطبيقات الأجهزة المحمولة تتطلب عمالة مكثفة وتغير العالم الرقمي يوميًا. بناء البرمجيات للأجهزة المحمولة هو ما يستلزمه. تقنيات المعالجة الدقيقة المتقدمة مطلوبة لإنشاء تطبيقات الهاتف المحمول. وقد رفعت هذه التقنيات من مستوى إنشاء برامج الهاتف المحمول. على سبيل المثال ، يمكنك الآن تشغيل تطبيقات الأجهزة المحمولة على مجموعة متنوعة من الأنظمة الأساسية. بالإضافة إلى ذلك ، يتم إنشاء أفضل تطبيقات سطح المكتب للهواتف الذكية.
تطبيقات الأجهزة المحمولة التي تم إنشاؤها متاحة للتنزيل والتثبيت المسبق على أجهزة Android و iPhone. يمكن لكل من أجهزة Mac وأجهزة الكمبيوتر التي تعمل بنظام Windows الاستجابة لها. يستكشف المطورون الحقائق المتعلقة باستهلاك تطبيقات الأجهزة المحمولة لإنشاء تطبيقات الأجهزة المحمولة. يأخذون في الاعتبار مقدار الأموال التي ينفقها المستخدمون على هواتفهم الذكية وإمكانية تطوير تطبيقات الهاتف المحمول.
How to add Parallax Effect in Image Flutter
في هذه الصفحة سوف نعرض ال List view التي سوف تحتوي على الصور كما هو متعارف عليه وعرض التصميم والذي سوف نقوم بعمله في الجزء التالي والذي يعبر عن كل عنصر في هذه ال listView .
Parallax_Effect.dart
import 'dart:ui';
import 'package:flutter/material.dart';
class AllDestination extends StatefulWidget {
final double padding;
final double spacing;
const AllDestination({Key? key,this.padding = 24, this.spacing = 24}) : super(key: key);
@override
State<AllDestination> createState() => _AllDestinationState();
}
class _AllDestinationState extends State<AllDestination> {
static const imageW = 250.0;
late final ScrollController _scrollController;
late final double _indexFactor;
double imageOffset = 0.0;
@override
void initState() {
final deviceWidth = (window.physicalSize.shortestSide / window.devicePixelRatio);
_indexFactor = (widget.spacing + imageW) / deviceWidth;
imageOffset = -widget.padding / deviceWidth;
_scrollController = ScrollController()
..addListener(() {
setState(() {
imageOffset = ((_scrollController.offset - widget.padding) / deviceWidth) ;
});
});
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 380,
child: ListView.separated(
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: widget.padding),
controller: _scrollController,
scrollDirection: Axis.horizontal,
separatorBuilder: (_, __) => SizedBox(width: widget.spacing),
itemCount: MockAllDestinations.data.length,
itemBuilder: (_, index) => DestinationItem(
index: index,
details: MockAllDestinations.data[index],
imageWidth: imageW,
imageOffset: imageOffset,
indexFactor: _indexFactor,
),
),
);
}
}
Design Item Parallax Effect
هذا الجزء يعبر عن كل عنصر في ال list السابقة والذي سوف نضيف التاثير لكل صورة من الصورة التي سوف نضعها كما هو موضح بالكود والصور الخاصه به في المقالة , الامر بسيط جدا يمكنك نسخ الكود التالي بشكل كامل والتعديل عليه في تطبيقك او استبدال الصور وبعض المحتويات فقط كل شيئ بين يديك .
DestinationItem.dart
import 'package:flutter/material.dart';
import 'dart:ui';
class DestinationItem extends StatelessWidget {
final int index;
final Destination details;
final double imageWidth;
final double imageOffset;
final double indexFactor;
const DestinationItem({
Key? key,
required this.index,
required this.details,
required this.imageWidth,
required this.imageOffset,
required this.indexFactor,
}) : super(key: key);
Widget image() {
return Image.network(
details.city,
fit: BoxFit.cover,
alignment: Alignment(-imageOffset + indexFactor * index, 0),
);
}
Widget rating() {
return Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.all(12),
child: ClipRRect(
borderRadius: BorderRadius.circular(32),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
child: Container(
height: 48,
color: Colors.black.withOpacity(0.3),
child: Padding(
padding: const EdgeInsets.only(left: 12, right: 16),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.star_rate_rounded,
color: Color(0xFFFFD600),
size: 20,
),
const SizedBox(width: 8),
Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
details.rating,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
),
),
],
),
),
),
),
),
),
);
}
Widget favoriteIcon() {
return Align(
alignment: Alignment.topRight,
child: Padding(
padding: const EdgeInsets.all(12),
child: ClipRRect(
borderRadius: BorderRadius.circular(32),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
child: Container(
width: 48,
height: 48,
color: Colors.black.withOpacity(0.3),
child: const Icon(
Icons.star,
color: Colors.yellow,
),
),
),
),
),
);
}
Widget bottomText() {
return Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(12),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
child: Container(
height: 48,
color: Colors.black.withOpacity(0.3),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
const Icon(
Icons.place,
color: Colors.white,
),
const SizedBox(width: 4),
Text(
details.country.length > 10
? '${details.country.substring(0, 10)}...'
: details.country,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
),
),
),
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: imageWidth,
child: ClipRRect(
borderRadius: BorderRadius.circular(32),
child: Stack(
fit: StackFit.expand,
children: [
image(),
rating(),
favoriteIcon(),
bottomText(),
],
),
),
);
}
}
class Destination {
final String city;
final String country;
final String rating;
const Destination({
required this.city,
required this.country,
required this.rating,
});
// String get image => 'assets/images/${city.toLowerCase()}.jpg';
}
class MockAllDestinations {
static const data = [
Destination(city: 'https://images.unsplash.com/photo-1491557345352-5929e343eb89?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80', country: 'Bahamas', rating: '4.6'),
Destination(city: 'https://images.unsplash.com/photo-1664215795139-516f3a4ce81b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1229&q=80', country: 'Greece', rating: '4.8'),
Destination(city: 'https://images.unsplash.com/photo-1664547401590-287057ac6eb5?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1169&q=80', country: 'Italy', rating: '4.4'),
Destination(city: 'https://images.unsplash.com/photo-1664261421791-c25c5760f577?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80', country: 'England', rating: '4.5'),
];
}