اضافة تاثير على الصور عند تمرير الماوس عليها في تطوير مواقع الويب باستخدام فلاتر
في هذا المقال يعد من المقالات الرائعه في تحسين تطوير المواقع باستخدام تقنية Flutter حيث اننا سوف نساعدكم بشكل كبير في عملية تنفيذ انميشن عن تحريك الماوس على العناصر لديكم في المشروع الذي تعمل عليه بشكل رائع وهذا التاثير لن يحتاج الى اي مكتبات خارجية كل شيئ موجود لدينا في المشروع الذي نعمل عليه وعباره عن اكواد برمجية بسيط تظهر لنا الانميشن كما هو موضح بالصورة الخاصه بالمقال والامر بسيط جدا وسوف نلاحظ الاكواد التاليه التي توضح لنا العمليه وكيفية القيام بها .
Add an effect to display details when hovering the mouse over the element
قمنا بتقسيم المشروع الى مجموعة من الاقسام جزء يظهر العنصر وجزء يظهر الانميشن على الصور عند تحريك الماوس عليها ولدينا AnimationController لعمل الانميشن و stream لتتبع الحالة الخاصه بالعنصر باستمرار يمكنك نسخ الاكواد واستخدامها لديك بدون مشاكل ولن تحتاج الى اي مكتبات خارجية اطلاقا .
effect.dart
class MyMobileBody extends StatefulWidget {
const MyMobileBody({Key? key}) : super(key: key);
@override
State<MyMobileBody> createState() => _MyMobileBodyState();
}
class _MyMobileBodyState extends State<MyMobileBody> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
InteractiveCard(
image:
'https://images.unsplash.com/photo-1672508644320-c39f93a9904f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=627&q=80',
index: 0,
),
InteractiveCard(
image:
'https://images.unsplash.com/photo-1672426142068-c2103a852426?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80',
index: 1,
),
InteractiveCard(
image:
'https://images.unsplash.com/photo-1672411164027-f1c3fb8dbd5f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80',
index: 2,
),
],
),
),
);
}
}
StreamController<int> controller = StreamController<int>.broadcast();
Stream<int> stream = controller.stream;
class InteractiveCard extends StatefulWidget {
final String image;
final int index;
final String? title;
final String? description;
const InteractiveCard(
{Key? key,
required this.image,
required this.index,
this.title,
this.description})
: super(key: key);
@override
State<InteractiveCard> createState() => _InteractiveCardState();
}
class _InteractiveCardState extends State<InteractiveCard>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 375),
);
_animation = Tween(begin: 0.0, end: -math.pi / 6).animate(
CurvedAnimation(parent: _controller, curve: Curves.ease),
);
super.initState();
}
@override
Widget build(BuildContext context) {
return Flexible(
child: StreamBuilder<int>(
stream: stream,
initialData: -1,
builder: (context, AsyncSnapshot<int> snapshot) {
if (!snapshot.hasData) {
controller.add(-1);
return const SizedBox();
}
if (snapshot.data == -1) {
_controller.animateBack(0.0);
} else if (snapshot.data != widget.index) {
if (snapshot.data! > widget.index) {
_animation = Tween(begin: 0.0, end: -math.pi / 6).animate(
CurvedAnimation(parent: _controller, curve: Curves.ease));
} else {
_animation = Tween(begin: 0.0, end: math.pi / 6).animate(
CurvedAnimation(parent: _controller, curve: Curves.ease));
}
_controller.forward();
} else {
_controller.animateBack(0.0);
}
return MouseRegion(
onEnter: (event) {
controller.add(widget.index);
},
onExit: (event) {
controller.add(-1);
},
child: AnimatedBuilder(
animation: _animation,
child: AnimatedContainer(
duration: const Duration(milliseconds: 375),
height: (snapshot.data != -1 && widget.index == snapshot.data)
? 380.0
: 300.0,
width: (snapshot.data != -1 && widget.index == snapshot.data)
? 380.0
: 300.0,
decoration: BoxDecoration(
boxShadow:
(snapshot.data != -1 && widget.index == snapshot.data)
? [
const BoxShadow(
offset: Offset(0.0, 10.0),
blurRadius: 30.0,
color: Colors.black38,
)
]
: []),
child: Stack(
children: [
Image.network(
widget.image,
fit: BoxFit.cover,
isAntiAlias: true,
height: 400.0,
width: 400.0,
),
LayoutBuilder(
builder: (context, child) {
return AnimatedOpacity(
duration: const Duration(milliseconds: 375),
opacity: (snapshot.data != -1 &&
widget.index == snapshot.data)
? 1.0
: 0.0,
child: AnimatedContainer(
duration: const Duration(milliseconds: 375),
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: Colors.blue[200]!.withOpacity(0.7),
),
padding: EdgeInsets.only(
top: (snapshot.data != -1 &&
widget.index == snapshot.data)
? 00.0
: 200.0,
),
child: Center(
child: OverflowBox(
minWidth: 340.0,
maxWidth: 341.0,
minHeight: 300.0,
maxHeight: 301.0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.title ?? 'Title',
style: TextStyle(
fontWeight: FontWeight.w800,
color: Colors.black,
fontSize: 20.0),
),
const SizedBox(
height: 20.0,
),
Flexible(
child: Text(
widget.description ??
'Lorem ipsum dolor sit amet. A cumque molestias quo dolores consequatur et rerum nihil ea vitae eligendi et itaque iste. Ea fugiat eaque quo odit tempora qui unde iure sed repellat voluptatem. Eum perferendis galisum sed saepe itaque qui soluta amet id fugiat sequi aut necessitatibus blanditiis ex ipsum perspiciatis qui voluptatem enim.',
style: TextStyle(
fontWeight: FontWeight.w500,
color: Colors.black87,
fontSize: 15.0),
),
),
],
),
),
),
),
),
);
},
),
],
),
),
builder: (context, child) {
return Transform(
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(_animation.value),
child: child,
);
},
),
);
},
),
);
}
}