इम्प्लिसिट एनिमेशन की आंतरिक कार्यप्रणाली को समझें और अपने खुद के AnimationController को बारीकी से नियंत्रित करें।
इस पाठ को Kodokon में खोलेंAnimatedContainer, इम्प्लिसिट एनिमेशन के परिवार से आता है: आप एक लक्ष्य वैल्यू बदलते हैं और फ़्रेमवर्क ट्रांज़िशन को एनिमेट करता है। परदे के पीछे, हर ImplicitlyAnimatedWidget अपने स्टेट में एक छिपा हुआ AnimationController रखता है। जब कोई नया कॉन्फ़िगरेशन आता है, तो didUpdateWidget, forEachTween के ज़रिए प्रॉपर्टीज़ पर से गुज़रता है और हर tween को पुनः लक्षित करता है: begin सीमा वर्तमान इंटरपोलेटेड वैल्यू बन जाती है, end सीमा नया लक्ष्य, फिर कंट्रोलर शून्य से दोबारा शुरू होता है। एक मूल्यवान परिणाम: एनिमेशन के बीच में लक्ष्य बदलने से कोई दृश्य झटका नहीं लगता - एनिमेशन ठीक वहीं से फिर शुरू होता है जहाँ वह था।
import 'package:flutter/material.dart';
class SelectableCard extends StatefulWidget {
const SelectableCard({super.key});
@override
State<SelectableCard> createState() =>
_SelectableCardState();
}
class _SelectableCardState extends State<SelectableCard> {
bool _selected = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () =>
setState(() => _selected = !_selected),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
width: _selected ? 220 : 160,
height: 96,
decoration: BoxDecoration(
color: _selected
? Colors.indigo
: Colors.indigo.shade200,
borderRadius: BorderRadius.circular(
_selected ? 28 : 12,
),
),
),
);
}
}इम्प्लिसिट, उन प्रॉपर्टीज़ को कवर करते हैं जिनका फ़्रेमवर्क पूर्वानुमान लगाता है, और TweenAnimationBuilder इस प्रक्रिया को किसी भी इंटरपोलेट की जा सकने वाली वैल्यू तक सामान्यीकृत कर देता है। जैसे ही आपको एनिमेशन को नियंत्रित करने की ज़रूरत हो - रोकना, उलटना, दोहराना, स्थिति पर प्रतिक्रिया देना - एक्सप्लिसिट AnimationController पर स्विच करें। vsync पैरामीटर एक TickerProvider की अपेक्षा करता है: मिक्सिन एक Ticker बनाता है, यानी एक कॉलबैक जिसे SchedulerBinding हर फ़्रेम पर लागू करता है। एक कम-ज्ञात सूक्ष्मता: TickerMode किसी छिपे हुए सबट्री के tickers को मौन कर देता है (उदाहरण के लिए किसी ढके हुए रूट के नीचे)। समय बहता रहता है, पर अब कोई कॉलबैक नहीं चलता - अदृश्य एनिमेशन पर शून्य CPU बर्बाद होती है।
import 'package:flutter/material.dart';
class RevealPanel extends StatefulWidget {
const RevealPanel({super.key, required this.child});
final Widget child;
@override
State<RevealPanel> createState() => _RevealPanelState();
}
class _RevealPanelState extends State<RevealPanel>
with SingleTickerProviderStateMixin {
late final AnimationController _controller =
AnimationController(
vsync: this,
duration: const Duration(milliseconds: 400),
reverseDuration: const Duration(milliseconds: 200),
);
late final Animation<double> _opacity = _controller
.drive(CurveTween(curve: Curves.easeOutCubic));
void toggle() {
switch (_controller.status) {
case AnimationStatus.forward:
case AnimationStatus.completed:
_controller.reverse();
case AnimationStatus.reverse:
case AnimationStatus.dismissed:
_controller.forward();
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: toggle,
child: FadeTransition(
opacity: _opacity,
child: widget.child,
),
);
}
}एक कंट्रोलर बस एक Animation<double> है जो 0 से 1 तक चलता है: इसे कच्चा उपभोग करने के बजाय इससे रचना करें। controller.drive(CurveTween(...)) बिना किसी अतिरिक्त स्टेट के एक व्युत्पन्न एनिमेशन उत्पन्न करता है। वैल्यू को ट्री पर लागू करने के लिए, Transition विजेट (FadeTransition, RotationTransition) को प्राथमिकता दें: वे एनिमेशन को सीधे render object स्तर पर सुनते हैं, बिना प्रति फ़्रेम एक भी विजेट दोबारा बनाए। जब रूपांतरण मनमाना हो, तब AnimatedBuilder यह काम करता है - बशर्ते आप स्थिर सबट्री को child के ज़रिए पास करें: इसे केवल एक बार बनाया जाता है फिर हर फ़्रेम पर builder में दोबारा इंजेक्ट किया जाता है। यह एक्सप्लिसिट एनिमेशन के लिए सबसे अधिक लाभ देने वाला अनुकूलन है।
import 'dart:math' as math;
import 'package:flutter/material.dart';
class Orbit extends StatefulWidget {
const Orbit({super.key});
@override
State<Orbit> createState() => _OrbitState();
}
class _OrbitState extends State<Orbit>
with SingleTickerProviderStateMixin {
late final AnimationController _controller =
AnimationController(
vsync: this,
duration: const Duration(seconds: 4),
)..repeat();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
child: const Icon(Icons.satellite_alt, size: 32),
builder: (context, child) {
final angle = _controller.value * 2 * math.pi;
return Transform.translate(
offset: Offset(
math.cos(angle) * 80,
math.sin(angle) * 80,
),
child: child,
);
},
);
}
}