簡體   English   中英

英雄小部件過渡與其他動畫沖突

[英]Hero widget transition conflict with other animations

我正在努力實現 Hero & shake upright animation 結果附加gif在這里。 這是我到目前為止得到的結果

似乎英雄小部件與我申請的 animation 沖突。 它似乎適用於200300卡。 但是當點擊100時,它的工作方式似乎有所不同。 下面附上上面演示的代碼。

嘗試使用WidgetsBinding.instance.addPostFrameCallbackSchedulerBinding.instance.addPersistentFrameCallback

有什么方法可以得到預期的結果而不是使用我用過的代碼嗎?

虛擬數據.dart

class _DummyData {
  final IconData icons;
  final Color colors;
  final String backText;

  const _DummyData(this.icons, this.colors, this.backText);
}

const List<_DummyData> _datas = [
  _DummyData(Icons.abc, Colors.blue, '100'),
  _DummyData(Icons.alarm, Colors.red, '200'),
  _DummyData(Icons.shop, Colors.green, '300'),
];

playground_list.dart


class PlayGroundList extends StatelessWidget {
  const PlayGroundList({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: CustomScrollView(
        slivers: [
          const SliverToBoxAdapter(child: SizedBox(height: 50)),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              childCount: _datas.length,
              (context, index) => PlayGroundCardWidget(dummy: _datas[index]),
            ),
          ),
        ],
      ),
    );
  }
}

playground_card_widget.dart

class PlayGroundCardWidget extends StatelessWidget {
  final _DummyData dummy;

  const PlayGroundCardWidget({super.key, required this.dummy});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 5.w, vertical: 1.5.h),
      height: 350,
      child: GestureDetector(
        onTap: () => Navigator.push(
          context,
          PageRouteBuilder(
            transitionsBuilder: (context, animation, _, child) =>
                FadeTransition(
              opacity: Tween(
                begin: 0.0,
                end: 1.0,
              ).chain(CurveTween(curve: Curves.ease)).animate(animation),
              child: child,
            ),
            pageBuilder: (context, _, __) => PlayGroundDetail(dummy: dummy),
          ),
        ),
        child: Stack(
          alignment: Alignment.center,
          children: [
            Positioned.fill(
              child: Hero(
                tag: dummy.colors.value,
                child: Material(color: dummy.colors),
              ),
            ),
            Align(
              alignment: Alignment.topCenter,
              child: Hero(
                tag: dummy.backText,
                child: Material(
                  color: Colors.transparent,
                  child: Text(
                    dummy.backText,
                    textAlign: TextAlign.center,
                    style: const TextStyle(
                      fontWeight: FontWeight.bold,
                      color: Colors.black,
                      fontSize: 140.0,
                    ),
                  ),
                ),
              ),
            ),
            Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                SizedBox(height: 3.h),
                Expanded(
                  flex: 12,
                  child: Hero(
                    tag: dummy.icons,
                    child: Icon(dummy.icons, size: 60.0),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

playground_detail.dart

const _shakeDuration = Duration(milliseconds: 900);

class PlayGroundDetail extends StatefulWidget {
  final _DummyData dummy;

  const PlayGroundDetail({super.key, required this.dummy});

  @override
  State<PlayGroundDetail> createState() => _PlayGroundDetailState();
}

class _PlayGroundDetailState extends State<PlayGroundDetail>
    with TickerProviderStateMixin {
  late final PageController _pageController;

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
  }

  @override
  void dispose() {
    super.dispose();
    _pageController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          CustomScrollView(
            slivers: [
              const SliverToBoxAdapter(child: SizedBox(height: 50)),
              SliverToBoxAdapter(
                child: SizedBox(
                  height: 350,
                  child: Stack(
                    children: [
                      Positioned.fill(
                        child: Hero(
                          tag: widget.dummy.colors.value,
                          child: Material(color: widget.dummy.colors),
                        ),
                      ),
                      Align(
                        alignment: Alignment.topCenter,
                        child: Hero(
                          tag: widget.dummy.backText,
                          child: Material(
                            color: Colors.transparent,
                            child: ShakeTransitionWidget(
                              axis: Axis.vertical,
                              duration: _shakeDuration,
                              offset: 30,
                              child: Text(
                                widget.dummy.backText,
                                textAlign: TextAlign.center,
                                style: const TextStyle(
                                  fontWeight: FontWeight.bold,
                                  color: Colors.black,
                                  fontSize: 140.0,
                                ),
                              ),
                            ),
                          ),
                        ),
                      ),
                      Center(
                        child: Hero(
                          tag: widget.dummy.icons,
                          child: ShakeTransitionWidget(
                            axis: Axis.vertical,
                            offset: 5,
                            duration: _shakeDuration,
                            child: Icon(widget.dummy.icons, size: 60.0),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

shake_transition_widget.dart

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class ShakeTransitionWidget extends StatefulWidget {
  final Widget child;
  final Duration duration;
  final double offset;
  final Axis axis;

  const ShakeTransitionWidget({
    super.key,
    required this.child,
    required this.duration,
    required this.offset,
    required this.axis,
  });

  @override
  State<ShakeTransitionWidget> createState() => _ShakeTransitionWidgetState();
}

class _ShakeTransitionWidgetState extends State<ShakeTransitionWidget>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: widget.duration,
    );
    _animation = Tween(
      begin: 1.0,
      end: 0.0,
    ).chain(CurveTween(curve: Curves.elasticOut)).animate(_controller);
    // Tried this.
    // WidgetsBinding.instance.addPostFrameCallback((_) => _controller.forward());
    if (SchedulerBinding.instance.schedulerPhase ==
        SchedulerPhase.persistentCallbacks) {
      _controller.forward();
    }
    SchedulerBinding.instance.addPersistentFrameCallback((timeStamp) {});
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (ctx, _) => Transform.translate(
        offset: widget.axis == Axis.horizontal
            ? Offset(_animation.value * widget.offset, 0.0)
            : Offset(0.0, _animation.value * widget.offset),
        child: widget.child,
      ),
    );
  }
}

ShakeTransitionWidget包裝你的Hero小部件,而不是相反:

// ... Hero is the child, not the parent
ShakeTransitionWidget(
  axis: Axis.vertical,
  duration: _shakeDuration,
  offset: 30,
  child: Hero(
  tag: widget.dummy.backText,
// ...

現在,在 _ShakeTransitionWidgetState 的_ShakeTransitionWidgetState initState()中,只需調用_controller.forward()即可,無需任何SchedulerBindingWidgetsBinding

根據經驗,英雄及其子項通常不應逐頁更改。 而是向父小部件添加修改。

動圖

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM