我当时在设计UI时比较混乱,并带有动画和其他内容,但是当我开始使用Firestore向列表中添加两个具有列表视图的StreamBuilders
到UI替换虚拟数据时,动画从黄油般平滑过渡到令人la目结舌我宁愿不在那儿。
我正在使用两个AnimationControllers
和两个控制两种类型的动画,我使用了this教程来学习如何在没有setStates
的情况下进行操作,并且在我添加{{ 1}}。
一个StreamBuilders
为淡入淡出过渡设置动画效果,另一个为垂直平移以将列表小部件隐藏在屏幕之外。
代码:
AnimationController
AnimationController _fadeAnimationController;
AnimationController _margAnimationController;
@override
void initState() {
super.initState();
this._fadeAnimationController = AnimationController(
value: 1.0,
duration: Duration(milliseconds: 300),
reverseDuration: Duration(milliseconds: 300),
vsync: this,
);
this._margAnimationController = AnimationController(
value: 0.0,
upperBound: widget.deviceHeight,
duration: Duration(milliseconds: 300),
reverseDuration: Duration(milliseconds: 300),
vsync: this,
);
}
@override
void dispose() {
super.dispose();
this._fadeAnimationController.dispose();
this._margAnimationController.dispose();
}
@override
Widget build(BuildContext context) {
return Material(
color: Colors.black,
child: Stack(
children: <Widget>[
FadeTransition( // FIRST ANIMATED WIDGET
opacity: this._fadeAnimationController,
child: Container(color: Config.pColor),
),
// Other unrelated widgets…
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[
Platform.isIOS
? CupertinoNavigationBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.transparent,
border: Border.all(color: Colors.transparent, width: 0.0, style: BorderStyle.none),
middle: Text('Dost', style: TextStyle(color: Config.bgColor)),
trailing: this._cameraIconButton(),
transitionBetweenRoutes: false,
heroTag: 'CameraAppBar')
: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.transparent,
elevation: 0.0,
title: Text('Dost', style: TextStyle(color: Config.bgColor)),
actions: <Widget>[this._cameraIconButton()],
)
]),
Expanded(
child: AnimatedBuilder( // SECOND ANIMATED WIDGET
animation: this._margAnimationController,
builder: (_, child) => Transform.translate(
offset: Offset(0, this._margAnimationController.value),
child: child,
),
child: HomePage( // STREAMBUILDERS ARE INSIDE THIS WIDGET
cUser: InheritedUser.of(context).user,
showCamera: () {
this._openCloseCamera();
},
showPosts: () {
Funcs.popup(context: context, w: CUPostsPage(cUser: InheritedUser.of(context).user));
},
showSettings: () {
Funcs.navigateTo(context: context, w: SettingsPage(), fullscreenDialog: false);
}),
),
)
],
)
],
),
);
}
小部件基本上具有两个HomePage()
的列表,每个列表都有一个StreamBuilders
,一个为水平,另一个为垂直。两者看起来非常相似。
StreamBuilder小部件:
ListView
AND
class ChatsList extends StatelessWidget {
@override
Widget build(BuildContext context) => StreamBuilder<List<Chat>>(
initialData: InheritedUser.of(context).user.chats,
stream: APIs().chats.chatsStream(cUserID: InheritedUser.of(context).user.userID),
builder: (context, snap) {
User cUser = InheritedUser.of(context).user;
cUser.chats.clear();
cUser.chats = snap.data;
return ListView.builder(
physics: BouncingScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.only(top: 0.0),
itemBuilder: (context, index) => ChatItem(chat: cUser.chats[index]),
itemCount: cUser.chats.length);
});
}
当我在其中一个class AUPostsList extends StatelessWidget {
final ScrollController scrollController;
AUPostsList({this.scrollController});
@override
Widget build(BuildContext context) => StreamBuilder<List<Post>>(
initialData: [],
stream: APIs().posts.auPostsStream(cUserID: InheritedUser.of(context).user.userID),
builder: (context, snap) {
Map<String, List<Post>> _posts = {};
List<String> _postsUserIDs = [];
snap.data.forEach((post) {
if (_posts[post.user.documentID] == null) {
_posts[post.user.documentID] = [post];
} else {
_posts[post.user.documentID].add(post);
}
if (!_postsUserIDs.contains(post.user.documentID)) {
_postsUserIDs.add(post.user.documentID);
_posts[post.user.documentID].sort((a, b) => b.createdAt.compareTo(a.createdAt));
}
});
return Container(
height: ((MediaQuery.of(context).size.width - 80.0) / 3) * 1.5,
child: ListView.separated(
scrollDirection: Axis.horizontal,
controller: this.scrollController,
physics: AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.only(top: 0.0, left: 10.0, right: 10.0),
itemBuilder: (context, index) => AUPostItem(posts: _posts[_postsUserIDs[index]]),
separatorBuilder: (context, index) => Container(),
itemCount: _postsUserIDs.length,
));
});
}
上发表评论时,它只是很慢,但是第二个AUPostsList比ChatsList慢得多。但是当两者都显示时,动画在发布模式下真的很滞后,在调试模式下却不存在。两者都被注释掉了,那么在调试和发布模式下都非常顺畅。
是的,在iOS和Android上都具有相同的效果。
答案 0 :(得分:1)
您可以使用侦听器来侦听Firestore数据库的更新,而不必使用流构建器,而在有更新时侦听setState。由于某种原因,它对我来说效果更好。在您的情况下,将是这样的:
List<DocumentSnapshot> posts;
var query = Firestore.instance
.collection('posts')
.where('userId', isEqualTo: userId);
listener() async {
query.snapshots().listen((querySnapshot) {
querySnapshot.documentChanges.forEach((changes) {
if (posts.every((f) => f != changes.document)) {
setState(() {
posts.add(changes.document);
});
} else {
setState(() {
posts[posts.indexWhere((f) =>
f.data['postId'] == changes.document.data['postId'])] =
changes.document;
});
}
});
});
}
当然,您必须根据需要调整所有数据。