Flutter Long list poor performance

Viewed 8817

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

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.

Related