Анимация флаттера становится очень медленной

Я разрабатывал пользовательский интерфейс во флаттере, с анимацией и прочим, но когда я начал добавлять два StreamBuilders со списками, используя Firestore, в пользовательский интерфейс заменили фиктивные данные, анимация превратилась из маслянисто-гладкой в ​​ооочень медленную, я бы предпочел их не иметь там.

Я использую два AnimationControllers два элемента управления двумя типами анимации, я использовал это руководство, чтобы узнать, как это сделать без setStates, и он работал гладко, пока я не добавил 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, каждый из которых имеет ListView, один по горизонтали, а другой по вертикали. Оба выглядят очень похожими.

Виджеты StreamBuilder:

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,
            ));
      });
}

Когда я комментирую один из StreamBuilders, он просто тормозит, однако второй, AUPostsList, намного медленнее, чем ChatsList. Но когда показываются оба, то анимация действительно очень отстает в режиме выпуска, а не в режиме отладки. И оба закомментированы, тогда это оооочень гладко как в режиме отладки, так и в режиме выпуска.

Да, и на iOS, и на Android эффекты одинаковые.


person Mohamed Mohamed    schedule 31.08.2019    source источник
comment
Это тормозит, потому что он запускает собственный код Firestore в потоке пользовательского интерфейса. Боюсь, что проблема навсегда удержит Флаттер. В режиме выпуска должно быть немного лучше, но до тех пор, пока (если) они не выяснят, как переложить работу на потоки, не связанные с пользовательским интерфейсом, я не ожидаю, что мы увидим звездную производительность от плагина Firestore.   -  person DarkNeuron    schedule 16.04.2020


Ответы (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;
        });
      }
    });
  });
}

Конечно, вам придется скорректировать все данные под свои нужды.

person Zhangir Siranov    schedule 31.08.2019
comment
Хорошо, я изменил свой код на ваш метод, и анимация улучшилась, но это не совсем то же самое, как если бы я комментировал эти виджеты. Есть ли способ улучшить его производительность или это лучшее, что он может получить? - person Mohamed Mohamed; 31.08.2019
comment
@MohamedMohamed, попробуйте отладить приложение с помощью F5, это, вероятно, может дать вам представление о том, что требует наибольшей производительности. Инструменты Dart, похоже, тоже обладали этой функцией. - person Zhangir Siranov; 31.08.2019