Flutter Long list poor performance
Asked Answered
T

2

6

I tried using Listview and Sliverlist, but the performance of my list is very slow. Even if the Items are loaded from the internet, the scrolling is very slow.

Anybody some thoughts about the slow code?

import 'dart:typed_data';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:impex_shop/bloc/warenkorb_bloc.dart';
import 'package:impex_shop/data/articlepodo.dart';
import 'package:impex_shop/routenames.dart';
import 'package:impex_shop/services/repository.dart';
import 'package:impex_shop/styles/impex_icons.dart';
import 'package:impex_shop/styles/impex_styles.dart';
import 'package:impex_shop/utils/utils.dart';
import 'package:impex_shop/widgets/myfuturebuilder.dart';

class SearchResultsWidget extends StatefulWidget {
  final Map<String, String> search;

  SearchResultsWidget(this.search);

  @override
  _SearchResultsWidgetState createState() =>
      _SearchResultsWidgetState(this.search);
}

class _SearchResultsWidgetState extends State<SearchResultsWidget> {
  Map<String, String> search;
  Future<ArticleIdList> _articleIds;

  _SearchResultsWidgetState(this.search);

  @override
  void initState() {
    super.initState();
    _articleIds = Repository().queryArticleSearch(search);
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _articleIds,
      //showProgressIndicator: true,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done &&
            snapshot.hasData) {
          ArticleIdList articleIds = snapshot.data;
          if (articleIds.articleIds.length == 0) {
            return Container(
              padding: EdgeInsets.symmetric(
                  horizontal: ImpexStyle.horizontalPadding),
              child: Text('Kein Artikel gefunden'),
            );
          }
          return CustomScrollView(
            slivers: <Widget>[
              SliverList(
                delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                    if (index == 0) {
                      return Container(
                        height: ImpexStyle.verticalPadding,
                      );
                    }
                    Future<Article> article = Repository()
                        .queryArticleDetails(articleIds.articleIds[index - 1]);
                    return MyFutureBuilder(
                      future: article,
                      builder: (context, article) {
                        return SearchResultLineWidget(article: article);
                      },
                    );
                  },
                  childCount: articleIds.articleIds.length + 1,
                ),
              )
            ],
          );
        } else if (snapshot.hasError) {
          return Row(
            children: <Widget>[
              Icon(
                Icons.error,
                color: ImpexColors.errorColor,
                size: 30,
              ),
              Expanded(
                child: Text(
                  '${snapshot.error}',
                  style: TextStyle(color: ImpexColors.errorColor),
                ),
              )
            ],
          );
        } else {
          return Center(
            child: CircularProgressIndicator(),
          );
        }
      },
    );
  }
}

class SearchResultLineWidget extends StatefulWidget {
  const SearchResultLineWidget({this.article});

  final Article article;

  @override
  State<StatefulWidget> createState() {
    return SearchResultLineState(article);
  }
}

class SearchResultLineState extends State<SearchResultLineWidget> {
  SearchResultLineState(this._article);

  final Article _article;
  Future<String> _articleImageData;

  @override
  void initState() {
    super.initState();
    _articleImageData = Repository().queryArticleImage(
      _article.id,
      width: ImpexStyle.imageSize,
      height: ImpexStyle.imageSize,
    );
  }

  @override
  Widget build(BuildContext context) {
    WarenkorbBloc warenkorbBloc = BlocProvider.of<WarenkorbBloc>(context);
    bool isInWarenkorb = (warenkorbBloc.currentState as WarenkorbLoaded)
            .warenkorbLines[_article.id] !=
        null;

    return Column(
      children: <Widget>[
        FlatButton(
          onPressed: (() {
            Navigator.pushNamed(context, RouteName.ARTICLE_DETAIL,
                arguments: _article.id);
          }),
          child: Row(
            children: <Widget>[
              FutureBuilder<String>(
                future: _articleImageData,
                builder: (context, snapshot) {
                  if (snapshot.hasData &&
                      snapshot.connectionState == ConnectionState.done) {
                    var imageData = snapshot.data;
                    if (imageData.isEmpty || imageData == 'null')
                      return Container(
                        width: ImpexStyle.imageSize.toDouble(),
                        height: ImpexStyle.imageSize.toDouble(),
                        child: EmptyImageIcon(),
                      );
                    return Container(
                      height: ImpexStyle.imageSize.toDouble(),
                      width: ImpexStyle.imageSize.toDouble(),
                      child: Stack(children: <Widget>[
                        Image(
                          image: MemoryImage(
                            Uint8List.fromList(imageData.codeUnits),
                          ),
                        ),
                        isInWarenkorb
                            ? Icon(FontAwesomeIcons.shoppingCart)
                            : Center(),
                      ]),
                    );
                  } else if (snapshot.hasError) {
                    return Container(
                      width: ImpexStyle.imageSize.toDouble(),
                      height: ImpexStyle.imageSize.toDouble(),
                      child: Text(snapshot.error),
                    );
                  }

                  return Container(
                    width: ImpexStyle.imageSize.toDouble(),
                    height: max(96, ImpexStyle.imageSize.toDouble()),
                    child: Center(),
                  );
                },
              ),
              Container(width: ImpexStyle.horizontalPadding),
              Flexible(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Row(
                      children: <Widget>[
                        Expanded(
                          child: Text(
                            _article.no,
                            style: ImpexStyle.fontStyleSmall,
                          ),
                        ),
                        Spacer(),
                        PriceLagerWidget(_article),
                      ],
                    ),
                    Text(_article.name, style: ImpexStyle.fontStyleNormal)
                  ],
                ),
              )
            ],
          ),
        ),
        Divider(color: ImpexColors.dividerColor),
      ],
    );
  }
}

class PriceLagerWidget extends StatelessWidget {
  PriceLagerWidget(this._article)
      : _articlePrice = Repository().queryArticlePrice(_article.id);

  final Article _article;
  final Future<ArticlePrice> _articlePrice;

  @override
  Widget build(BuildContext context) {
    return MyFutureBuilder<ArticlePrice>(
      future: _articlePrice,
      builder: (context, articlePrice) {
        String priceString = getPriceString(articlePrice, context);
        return Row(
          children: <Widget>[
            Row(
              children: <Widget>[
                _article.isRaffleWin
                    ? RaffleWinIcon()
                    : Text('€', style: ImpexStyle.fontStyleSmall),
                Text(' $priceString ', style: ImpexStyle.fontStyleSmall)
              ],
            ),
            AvailabilityIcon(this._article.availabilityColor)
          ],
        );
      },
    );
  }
}
Thomasenathomasin answered 24/7, 2020 at 9:14 Comment(3)
Have you checked it the app in release mode, maybe it should be slow in the debug mode.Gaby
Use dart devtools to figure out whats wrong with your app. Check this out: youtube.com/watch?v=nq43mP7hjAEContention
it is in release mode. it is slow since signingThomasenathomasin
Z
5

I think using ListView.builder might help you as it will render only the visible items.

Here is an article about widget optimization which explains the differences in performance when building some widgets.

Zea answered 24/7, 2020 at 9:37 Comment(0)
I
0
ListView.builder(
        itemCount: rows,
        prototypeItem: ListTile(),
        itemBuilder: (context, index) {
          ListTile()
        }

Make sure to set prototypeItem, otherwise, the performance is still poor.

Indiana answered 19/10, 2023 at 2:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.