我正在尝试创建一个像list view这样的列表视图,我得到了一些不错的东西,但它并不像运球那样流畅。
我猜这是因为我多次调用setState并在动画过程中更新树,但我不知道如何使其更平滑,也许只调用一次setState。 我正在使用继承的小部件通过树传递状态,这是编辑状态的定义
void onChangeIndex (int newIndex) {
fadeText(_index, newIndex);
changePosition(newIndex);
}
void fadeText(int oldIndex,int newIndex) async {
setState(() {
_visible[oldIndex] = !_visible[oldIndex];
});
await Future.delayed(Duration(milliseconds: 300));
setState(() {
_visible[newIndex] = !_visible[newIndex];
debugPrint(_visible[oldIndex].toString());
});
}
void changePosition(newIndex) async {
await Future.delayed(Duration(milliseconds: 50));
setState(() {
_index = newIndex;
_scrollController.animateTo((_index)*320.0, duration: Duration(milliseconds: 800), curve: Curves.fastOutSlowIn);
});
}
当我拖动列表视图时会调用此部分,其作用是滚动列表视图并为文本设置动画
这是我的列表视图中包含的卡,如果结构混乱,对不起,我感到很陌生。
GestureDetector(
child: Padding(
padding: EdgeInsets.all(6.0),
child: Card(
elevation: 0.0,
color: Colors.transparent,
child: Container(
width: 295.0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children:<Widget>[
Expanded(
child:
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
elevation: 5.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Image.asset('images/paris.jpg', fit: BoxFit.contain),
),
)
),
]
),
Row(
children: <Widget>[
Expanded(
child: AnimatedOpacity(
opacity: StateManager.of(context).visible[widget.index] ? 1.0 : 0.0,
duration: Duration(milliseconds: 300),
child: Container(
height: 24.0,
child: Padding(
padding: const EdgeInsets.only(
left: 8.0
),
child: Text(
"Paris, France",
style: TextStyle(
color: Color.fromRGBO(0,0,0,0.9),
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
)
)
)
),
],
),
Row(
children: <Widget>[
Expanded(
child: AnimatedOpacity(
opacity: StateManager.of(context).visible[widget.index] ? 1.0 : 0.0,
duration: Duration(milliseconds: 300),
child: Container(
height: 30.0,
child: Padding(
padding: const EdgeInsets.only(
left: 8.0
),
child: Text(
"Visiter ou bien aller au travail à vélo facilement grâce aux nombreux parkings à vélo présent dans cette ville.",
style: TextStyle(
color: Color.fromRGBO(0,0,0,0.7),
fontSize: 12.0,
fontWeight: FontWeight.bold,
),
),
)
)
)
),
],
),
])),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.all(Radius.circular(15.0)),
// ),
),
),
onHorizontalDragEnd: (details) {
var cardIndex = StateManager.of(context).index;
if(details.velocity.pixelsPerSecond.dx > 0) {
if(cardIndex>0) {
StateManager.of(context).onChangeIndex(cardIndex-1);
}
}else if(details.velocity.pixelsPerSecond.dx < 0){
if(cardIndex<2) {
StateManager.of(context).onChangeIndex(cardIndex+1);
}
}
},
);
如果您有任何想法可以改善我设置状态以使动画更流畅的方式,那将对我有很大帮助。
答案 0 :(得分:1)
首先,在setState
调用之后使用await
是不好的。如果由于用户导航到另一个页面而不再存在该小部件,则将引发异常。而是here is how you can create staggered (delayed) animations in Flutter。
这是由PageController
驱动的解决方案。文本淡入淡出动画的控制器可以在以下项目的状态中找到:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Playground',
home: DestinationsPage(),
);
}
}
class DestinationsPage extends StatefulWidget {
@override
_DestinationsPageState createState() => _DestinationsPageState();
}
class _DestinationsPageState extends State<DestinationsPage> {
PageController _pageController;
int _selectedPage = 0;
@override
void initState() {
super.initState();
_pageController = PageController(
initialPage: 0,
viewportFraction: 0.8,
)..addListener(_updateSelectedPage);
}
void _updateSelectedPage() {
final closestPage = _pageController.page.round();
final isClosestPageSelected = (_pageController.page - closestPage).abs() < 0.2;
final selectedPage = isClosestPageSelected ? closestPage : null;
if (_selectedPage != selectedPage) {
setState(() {
_selectedPage = selectedPage;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Destinations')),
body: PageView.builder(
controller: _pageController,
itemBuilder: (context, index) {
return _DestinationItem(
page: index,
selected: (index == _selectedPage),
);
},
),
);
}
}
class _DestinationItem extends StatefulWidget {
// some content, in this case just
final int page;
// indicates that the page is selected and that the text should be visible
final bool selected;
const _DestinationItem({Key key, this.page, this.selected}) : super(key: key);
@override
_DestinationItemState createState() => _DestinationItemState();
}
class _DestinationItemState extends State<_DestinationItem> with SingleTickerProviderStateMixin<_DestinationItem> {
AnimationController _textTransitionController;
@override
void initState() {
super.initState();
_textTransitionController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 400),
value: widget.selected ? 1.0 : 0.0,
);
}
@override
void didUpdateWidget(_DestinationItem oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.selected != oldWidget.selected) {
if (widget.selected) {
_textTransitionController.forward();
} else {
_textTransitionController.reverse();
}
}
}
@override
void dispose() {
// important call, otherwise throws error if the PageView destroys
// the widget while the fade transition is still running
_textTransitionController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
children: <Widget>[
Container(
height: 200.0,
color: Colors.orange,
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: Center(
child: Text('Image ${widget.page}'),
),
),
SizedBox(height: 16.0),
FadeTransition(
opacity: _textTransitionController,
child: Text(
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.'),
)
],
),
);
}
}
答案 1 :(得分:0)
这是与我的其他答案类似的解决方案,但是在这种情况下,文本不是PageView
的一部分。使用AnimatedSwitcher
淡入和淡出文本:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Playground',
home: DestinationsPage(),
);
}
}
class DestinationsPage extends StatefulWidget {
@override
_DestinationsPageState createState() => _DestinationsPageState();
}
class _DestinationsPageState extends State<DestinationsPage> {
PageController _pageController;
int _selectedPage = 0;
@override
void initState() {
super.initState();
_pageController = PageController(
initialPage: 0,
viewportFraction: 0.8,
)..addListener(_updateSelectedPage);
}
void _updateSelectedPage() {
final closestPage = _pageController.page.round();
final isClosestPageSelected = (_pageController.page - closestPage).abs() < 0.2;
final selectedPage = isClosestPageSelected ? closestPage : null;
if (_selectedPage != selectedPage) {
setState(() {
_selectedPage = selectedPage;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Destinations')),
body: Column(
children: <Widget>[
SizedBox(
height: 200.0,
child: PageView.builder(
controller: _pageController,
itemBuilder: (context, index) {
return Container(
color: Colors.orange,
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: Center(
child: Text('Image ${index}'),
),
);
},
),
),
SizedBox(height: 16.0),
Expanded(
child: AnimatedSwitcher(
duration: Duration(milliseconds: 500),
switchInCurve: Interval(0.5, 1.0, curve: Curves.ease),
switchOutCurve: Interval(0.5, 1.0, curve: Curves.ease),
child: _buildSelectedPageText(),
),
)
],
)
);
}
Widget _buildSelectedPageText() {
if(_selectedPage != null) {
return Text(
'Text for page ${_selectedPage}!',
key: ValueKey(_selectedPage), // setting key is important, see switcher docs
);
} else {
return null;
}
}
}