Reputation: 4295
I was designing the UI in flutter and with the animations and stuff but when I started to add two StreamBuilders
with listviews, using Firestore, to the UI replace the dummy data the animations turned from buttery smooth to sooo laggy I'd rather not have them there.
I'm using two AnimationControllers
two control two types of animations, I used this tutorial to learn how to do it without setStates
and it worked smoothly until I added the StreamBuilders
.
One AnimationController
animates a fade transition, and the other a vertical translation to hide the lists widget off the screen.
Code:
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);
}),
),
)
],
)
],
),
);
}
The HomePage()
widget basically has a list of the two StreamBuilders
each having a ListView
, one horizontal and the other vertical. Both look very similar.
The StreamBuilder widgets:
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);
});
}
AND
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,
));
});
}
When I comment on one of the StreamBuilders
out then it's just laggy, however, the second, AUPostsList, is much laggier than ChatsList. But when both are being shown then the animation is really really really laggy on release mode, nonexistent on debug mode. And both are commented out then it's soooooooo smooth both on debug and release mode.
Yes, both on iOS and Android have the same effects.
Upvotes: 0
Views: 2469
Reputation: 369
Instead of using the stream builders, you can use listeners that will listen for the updates of the Firestore database, and setState when you have the update. It worked better for me for some reason. In your case that would be something like this:
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;
});
}
});
});
}
Of Course, you will have to adjust all of the data for your needs.
Upvotes: 1