Kodokon kodokon.com

एनिमेशन: परदे के पीछे इम्प्लिसिट और एक्सप्लिसिट

इम्प्लिसिट एनिमेशन की आंतरिक कार्यप्रणाली को समझें और अपने खुद के AnimationController को बारीकी से नियंत्रित करें।

10 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

AnimatedContainer, इम्प्लिसिट एनिमेशन के परिवार से आता है: आप एक लक्ष्य वैल्यू बदलते हैं और फ़्रेमवर्क ट्रांज़िशन को एनिमेट करता है। परदे के पीछे, हर ImplicitlyAnimatedWidget अपने स्टेट में एक छिपा हुआ AnimationController रखता है। जब कोई नया कॉन्फ़िगरेशन आता है, तो didUpdateWidget, forEachTween के ज़रिए प्रॉपर्टीज़ पर से गुज़रता है और हर tween को पुनः लक्षित करता है: begin सीमा वर्तमान इंटरपोलेटेड वैल्यू बन जाती है, end सीमा नया लक्ष्य, फिर कंट्रोलर शून्य से दोबारा शुरू होता है। एक मूल्यवान परिणाम: एनिमेशन के बीच में लक्ष्य बदलने से कोई दृश्य झटका नहीं लगता - एनिमेशन ठीक वहीं से फिर शुरू होता है जहाँ वह था।

DART
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,
          ),
        ),
      ),
    );
  }
}
एक ही setState से आकार, रंग और त्रिज्या एनिमेट किए गए।

इम्प्लिसिट, उन प्रॉपर्टीज़ को कवर करते हैं जिनका फ़्रेमवर्क पूर्वानुमान लगाता है, और TweenAnimationBuilder इस प्रक्रिया को किसी भी इंटरपोलेट की जा सकने वाली वैल्यू तक सामान्यीकृत कर देता है। जैसे ही आपको एनिमेशन को नियंत्रित करने की ज़रूरत हो - रोकना, उलटना, दोहराना, स्थिति पर प्रतिक्रिया देना - एक्सप्लिसिट AnimationController पर स्विच करें। vsync पैरामीटर एक TickerProvider की अपेक्षा करता है: मिक्सिन एक Ticker बनाता है, यानी एक कॉलबैक जिसे SchedulerBinding हर फ़्रेम पर लागू करता है। एक कम-ज्ञात सूक्ष्मता: TickerMode किसी छिपे हुए सबट्री के tickers को मौन कर देता है (उदाहरण के लिए किसी ढके हुए रूट के नीचे)। समय बहता रहता है, पर अब कोई कॉलबैक नहीं चलता - अदृश्य एनिमेशन पर शून्य CPU बर्बाद होती है।

DART
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,
      ),
    );
  }
}
reverseDuration: आने का रास्ता और जाने का रास्ता, दोनों को अपनी-अपनी गति मिलती है।

एक कंट्रोलर बस एक Animation<double> है जो 0 से 1 तक चलता है: इसे कच्चा उपभोग करने के बजाय इससे रचना करें। controller.drive(CurveTween(...)) बिना किसी अतिरिक्त स्टेट के एक व्युत्पन्न एनिमेशन उत्पन्न करता है। वैल्यू को ट्री पर लागू करने के लिए, Transition विजेट (FadeTransition, RotationTransition) को प्राथमिकता दें: वे एनिमेशन को सीधे render object स्तर पर सुनते हैं, बिना प्रति फ़्रेम एक भी विजेट दोबारा बनाए। जब रूपांतरण मनमाना हो, तब AnimatedBuilder यह काम करता है - बशर्ते आप स्थिर सबट्री को child के ज़रिए पास करें: इसे केवल एक बार बनाया जाता है फिर हर फ़्रेम पर builder में दोबारा इंजेक्ट किया जाता है। यह एक्सप्लिसिट एनिमेशन के लिए सबसे अधिक लाभ देने वाला अनुकूलन है।

DART
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,
        );
      },
    );
  }
}
child के रूप में पास किया गया आइकन केवल एक बार बनाया जाता है।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. जब पिछला एनिमेशन आधे रास्ते में हो, तभी किसी AnimatedContainer को एक नया लक्ष्य रंग मिलता है। क्या होता है?
    • tween को पुनः लक्षित किया जाता है: यह वर्तमान वैल्यू से नए लक्ष्य की ओर, बिना किसी दृश्य झटके के, दोबारा शुरू होता है
    • चल रहा एनिमेशन पहले पूरा होता है, फिर नया शुरू होता है
    • विजेट तुरंत नए रंग पर कूद जाता है
  2. किसी AnimationController का vsync पैरामीटर आख़िर किसलिए होता है?
    • यह फ़्रेम के साथ समकालिक एक Ticker प्रदान करता है, जिसे सबट्री के छिपे होने पर TickerMode मौन कर सकता है
    • यह कंट्रोलर को ऐप के अन्य एनिमेशन के साथ समकालिक करता है
    • यह एनिमेशन को 30 फ़्रेम प्रति सेकंड तक सीमित करता है
    • यह GPU हार्डवेयर त्वरण सक्षम करता है
  3. स्थिर सबट्री को AnimatedBuilder के child पैरामीटर के ज़रिए क्यों पास करें?
    • इसे एक बार बनाया जाता है फिर builder में दोबारा इंजेक्ट किया जाता है, बजाय हर फ़्रेम पर दोबारा बनाए जाने के
    • यह अनिवार्य है: builder नए विजेट नहीं लौटा सकता
    • यह अंत में कंट्रोलर को स्वतः मुक्त कर देता है