flutter AnimatedSwitcher with IndexedStack

eagi6jfj  于 2023-08-07  发布在  Flutter
关注(0)|答案(7)|浏览(141)

我必须使用IndexedStack来维护我的BottomNavigationBar的小部件的状态。现在,我想使用AnimatedSwitcher(或其他)在切换选项卡时创建动画。我在更改IndexedStack时触发AnimatedSwitcher时遇到问题。我将IndexedStack作为AnimatedSwitcher的子项,这显然会导致AnimatedSwitcher无法触发,因为IndexedStack小部件不会更改,只是它是子项。

body: AnimatedSwitcher(  
  duration: Duration(milliseconds: 200),  
  child: IndexedStack(  
    children: _tabs.map((t) => t.widget).toList(),  
    index: _currentIndex,  
  ),  
)

字符串
有没有办法解决这个问题?手动触发AnimatedSwitcher,还是使用不同的方法创建动画?我也试着改变键,但这显然导致它每次创建一个新的状态时都会创建一个新的IndexedStack,因此选项卡的状态也丢失了。

ia2d9nvy

ia2d9nvy1#

这是一个更清晰的方式来使用IndexedStack与动画,我创建了一个FadeIndexedStack小部件。
https://gist.github.com/diegoveloper/1cd23e79a31d0c18a67424f0cbdfd7ad

用法

body: FadeIndexedStack(  
    //this is optional
    //duration: Duration(seconds: 1),
    children: _tabs.map((t) => t.widget).toList(),  
    index: _currentIndex,  
  ),

字符串

djp7away

djp7away2#

如果需要使用IndexedStack。
您可以添加自定义动画并在更改选项卡时触发它,如下所示:

的数据

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  final List<Widget> myTabs = [
    Tab(text: 'one'),
    Tab(text: 'two'),
    Tab(text: 'three'),
  ];

  AnimationController _animationController;
  TabController _tabController;
  int _tabIndex = 0;
  Animation animation;

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

  @override
  void initState() {
    _tabController = TabController(length: 3, vsync: this);
    _animationController = AnimationController(
      vsync: this,
      value: 1.0,
      duration: Duration(milliseconds: 500),
    );
    _tabController.addListener(_handleTabSelection);
    animation = Tween(begin: 0.0, end: 1.0).animate(_animationController);
    super.initState();
  }

  _handleTabSelection() {
    if (!_tabController.indexIsChanging) {
      setState(() {
        _tabIndex = _tabController.index;
      });
      _animationController.reset();
      _animationController.forward();
    }
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> _tabs = [
      MyAnimation(
        animation: animation,
        child: Text('first tab'),
      ),
      MyAnimation(
        animation: animation,
        child: Column(
          children: List.generate(20, (index) => Text('line: $index')).toList(),
        ),
      ),
      MyAnimation(
        animation: animation,
        child: Text('third tab'),
      ),
    ];

    return Scaffold(
      appBar: AppBar(),
      bottomNavigationBar: TabBar(
        controller: _tabController,
        labelColor: Colors.redAccent,
        isScrollable: true,
        tabs: myTabs,
      ),
      body: IndexedStack(
        children: _tabs,
        index: _tabIndex,
      ),
    );
  }
}

class MyAnimation extends AnimatedWidget {
  MyAnimation({key, animation, this.child})
      : super(
          key: key,
          listenable: animation,
        );

  final Widget child;

  @override
  Widget build(BuildContext context) {
    Animation<double> animation = listenable;
    return Opacity(
      opacity: animation.value,
      child: child,
    );
  }
}

字符串

wb1gzix0

wb1gzix03#

尝试向IndexedStack添加键,并向AnimatedSwitcher小部件添加transitionBuilder方法,就像这样。

AnimatedSwitcher(
            duration: Duration(milliseconds: 1200),
            transitionBuilder: (child, animation) => SizeTransition(
              sizeFactor: animation,
              child: IndexedStack(
                key: ValueKey<int>(navigationIndex.state),
                index: navigationIndex.state,
                children: _tabs.map((t) => t.widget).toList(),  
              ),
            ),
            child: IndexedStack(
              key: ValueKey<int>(navigationIndex.state), //using StateProvider<int>
              index: navigationIndex.state,
              children: _tabs.map((t) => t.widget).toList(),  
            ),
          ),

字符串
还有其他很酷的过渡,如ScaleTransition,SizeTransition,FadeTransition

bksxznpy

bksxznpy4#

下面是如何做到的示例:

PageTransitionSwitcher(
   duration: Duration(milliseconds: 250),
      transitionBuilder: (widget, anim1, anim2) {
    return FadeScaleTransition(
      animation: anim1,
      child: widget,
    );
  },
  child: IndexedStack(
    index: _currentIndex,
    key: ValueKey<int>(_currentIndex),
    children: [
      Page1(),
      Page2(),
      Page3()
    ],
  ),
);

字符串

q8l4jmvw

q8l4jmvw5#

以下是你可以解决这个问题的方法:
1.首先使用下面的代码创建一个新的小部件。
1.这个小部件使用PageView,默认情况下它会给予你一个很好的滑动动画,但是如果你愿意的话,你可以自定义动画。
1.要保持孩子的状态,只需在每个孩子的小部件中使用AutomaticKeepAliveClientMixin即可。

class IndexedStackSlider extends StatefulWidget {
  /// Constract
  const IndexedStackSlider({
    super.key,
    required this.currentIndex,
    required this.children,
  });

  /// The current index
  final int currentIndex;

  /// The list of children
  final List<Widget> children;

  @override
  State<IndexedStackSlider> createState() => _IndexedStackSliderState();
}

class _IndexedStackSliderState extends State<IndexedStackSlider> {
  late PageController _pageController;

  @override
  void initState() {
    super.initState();
    _pageController = PageController(initialPage: widget.currentIndex);
  }

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

  @override
  void didUpdateWidget(IndexedStackSlider oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.currentIndex != widget.currentIndex) {
      _pageController.animateToPage(
        widget.currentIndex,
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return PageView.builder(
      controller: _pageController,
      physics:
          const NeverScrollableScrollPhysics(), // Disable swipe between pages
      itemCount: widget.children.length,
      itemBuilder: (context, index) {
        return AnimatedBuilder(
          animation: _pageController,
          builder: (context, child) {
            return Align(
              alignment: Alignment.topCenter,
              child: child,
            );
          },
          child: widget.children[index],
        );
      },
    );
  }
}

字符串

qmelpv7a

qmelpv7a6#

我有点失望,这是不支持开箱即用的IndexedStack,所以我尝试了一个增强版本,将支持自定义转换(有更多的控制),并仍然保持状态的孩子。
我在这里用一个小测试应用程序做了一个repo:https://github.com/IMGNRY/animated_indexed_stack
欢迎任何反馈或PR!

i1icjdpr

i1icjdpr7#

尝试将key添加到IndexedStack中,这样你的代码看起来就像这样:

body: AnimatedSwitcher(  
  duration: Duration(milliseconds: 200),  
  child: IndexedStack(
    key: ValueKey<int>(_currentIndex),
    children: _tabs.map((t) => t.widget).toList(),  
    index: _currentIndex,  
  ),  
)

字符串
关键字被更改,因此AnimatedSwitcher将知道需要重建其子项。

相关问题