带有 IndexedStack 的 AnimatedSwitcher
AnimatedSwitcher with IndexedStack
我必须使用 IndexedStack 来维护 BottomNavigationBar 的小部件状态。
现在我想在切换选项卡时使用 AnimatedSwitcher(或替代方案)创建动画。
我在让 AnimatedSwitcher 触发 IndexedStack 更改时遇到问题。我将 IndexedStack 作为 AnimatedSwitcher 的子级,这显然会导致 AnimatedSwitcher 无法触发,因为 IndexedStack 小部件不会改变,只是它是子级。
body: AnimatedSwitcher(
duration: Duration(milliseconds: 200),
child: IndexedStack(
children: _tabs.map((t) => t.widget).toList(),
index: _currentIndex,
),
)
有什么办法可以解决这个问题吗?通过手动触发 AnimatedSwitcher,还是使用不同的方法来创建动画?
我也尝试更改密钥,但这显然导致它在每次创建新状态时创建一个新的 IndexedStack,因此选项卡的状态也丢失了。
如果需要使用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,
);
}
}
这是使用 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,
),
尝试将键添加到您的 IndexedStack,这样您的代码将如下所示:
body: AnimatedSwitcher(
duration: Duration(milliseconds: 200),
child: IndexedStack(
key: ValueKey<int>(_currentIndex),
children: _tabs.map((t) => t.widget).toList(),
index: _currentIndex,
),
)
密钥已更改,因此 AnimatedSwitcher 将知道它的子项需要重建。
尝试像这样向您的 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
PageTransitionSwitcher(
持续时间:持续时间(毫秒:250),
transitionBuilder: (widget, anim1, anim2) {
return FadeScaleTransition(
animation: anim1,
child: widget,
);
},
child: IndexedStack(
index: _currentIndex,
key: ValueKey<int>(_currentIndex),
children: [
Page1(),
Page2(),
Page3()
],
),
);
我必须使用 IndexedStack 来维护 BottomNavigationBar 的小部件状态。 现在我想在切换选项卡时使用 AnimatedSwitcher(或替代方案)创建动画。 我在让 AnimatedSwitcher 触发 IndexedStack 更改时遇到问题。我将 IndexedStack 作为 AnimatedSwitcher 的子级,这显然会导致 AnimatedSwitcher 无法触发,因为 IndexedStack 小部件不会改变,只是它是子级。
body: AnimatedSwitcher(
duration: Duration(milliseconds: 200),
child: IndexedStack(
children: _tabs.map((t) => t.widget).toList(),
index: _currentIndex,
),
)
有什么办法可以解决这个问题吗?通过手动触发 AnimatedSwitcher,还是使用不同的方法来创建动画? 我也尝试更改密钥,但这显然导致它在每次创建新状态时创建一个新的 IndexedStack,因此选项卡的状态也丢失了。
如果需要使用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,
);
}
}
这是使用 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,
),
尝试将键添加到您的 IndexedStack,这样您的代码将如下所示:
body: AnimatedSwitcher(
duration: Duration(milliseconds: 200),
child: IndexedStack(
key: ValueKey<int>(_currentIndex),
children: _tabs.map((t) => t.widget).toList(),
index: _currentIndex,
),
)
密钥已更改,因此 AnimatedSwitcher 将知道它的子项需要重建。
尝试像这样向您的 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
PageTransitionSwitcher( 持续时间:持续时间(毫秒:250), transitionBuilder: (widget, anim1, anim2) {
return FadeScaleTransition(
animation: anim1,
child: widget,
);
},
child: IndexedStack(
index: _currentIndex,
key: ValueKey<int>(_currentIndex),
children: [
Page1(),
Page2(),
Page3()
],
),
);