Dyary
Dyary

Reputation: 907

Flutter app crashes very often after populating it with data and no errors are showed, just lost connection to device

I have a flutter app that is complex enough and i have been developing it for a while.

the problem is after finishing up with the design code , i started implementing API's to receive actual data and populate the app with it. the app is now crashing quite a lot ( although a little less in release mode).

I am thinking it might be because the android device runs out of memory because of all the complex widgets I display in the app.

I have build all of my widgets Stateful I don't know if that could be a related factor (I am a beginner with flutter programming).

Please I have spent a lot of time in developing this app and it's my first enterprise level flutter app.

I haven't had experience with flutter before and I knew this kind of problems would arise and have no solution I would have gone with Java and Swift to develop the app.

Any help is appreciated guys. Thanks.

the app only displays about a 30-40 images, I don't know where all the data coming from.

Here is the code :

import 'package:flutter/material.dart';
import 'package:bestiee/componenets/constants.dart';
import 'package:bestiee/componenets/cards/single-item-hot-screen-card.dart';
import 'package:bestiee/componenets/buttons/small-round-more-button.dart';
import 'package:bestiee/componenets/cards/single-place-hot-screen-card.dart';
import 'package:bestiee/componenets/cards/single-person-hot-screen-card.dart';
import 'package:bestiee/componenets/cards/single-place-large-hot-screen-card.dart';
import 'package:bestiee/screens/hotScreenSingleScreens/more-hot-items-screen.dart';
import 'package:bestiee/screens/hotScreenSingleScreens/more-hot-places-screen.dart';
import 'package:bestiee/screens/hotScreenSingleScreens/more-hot-people-screen.dart';
import 'package:bestiee/translations.dart';
import 'package:bestiee/models/models.dart';
import 'package:bestiee/networking.dart';
import 'package:bestiee/constants.dart';
import 'dart:convert';
import 'package:http/http.dart';

class HotScreen extends StatefulWidget {
  static List<Item> items = [];
  static List<Place> places = [];
  static List<Person> people = [];
  static bool isFirstRun = true;

  @override
  _HotScreenState createState() => _HotScreenState();
}

class _HotScreenState extends State<HotScreen> {



  String getTranslation(String text) {
    try {
      String translation = Translations.of(context).text(text);
      return translation;
    } catch (ex) {
      print(ex);
      return '';
    }
  }

  getAllPlaces() async {
    HotScreen.places.removeRange(0, HotScreen.places.length);

    Response response = await getRequest(devBaseURL + plainPlaceAPI);
    var allPlacesMap = jsonDecode(response.body);

    print(allPlacesMap.length);

    for (int i = 0; i < allPlacesMap.length; i++) {
      var json = allPlacesMap[i];
      Place place = Place.fromJson(json);
      setState(() {
        HotScreen.places.add(place);
      });
    }
  }

  getAllItems() async {
    HotScreen.items.removeRange(0, HotScreen.items.length);
    Response response = await getRequest(devBaseURL + plainItemAPI);
    var allItemsMap = jsonDecode(response.body);

    for (int i = 0; i < allItemsMap.length; i++) {
      var json = allItemsMap[i];
      Item item = Item.fromJson(json);
      setState(() {
        HotScreen.items.add(item);
      });
    }
  }

  getAllPeople() async {
    HotScreen.people.removeRange(0, HotScreen.people.length);
    Response response = await getRequest(devBaseURL + plainPersonAPI);
    var allPeopleMap = jsonDecode(response.body);

    for (int i = 0; i < allPeopleMap.length; i++) {
      var json = allPeopleMap[i];
      Person person = Person.fromJson(json);
      setState(() {
        HotScreen.people.add(person);
      });
    }
  }

  @override
  void initState() {
    super.initState();

    if(HotScreen.isFirstRun == true){
      getAllItems();
      getAllPlaces();
      getAllPeople();

      HotScreen.isFirstRun = false;
    }

  }

  @override
  Widget build(BuildContext context) {
    double screenWidth = MediaQuery.of(context).size.width;

    return Scaffold(
      body: Container(
        decoration: kPageMainBackgroundColorBoxDecoration,
        child: SafeArea(
          child: Padding(
            padding: const EdgeInsets.all(8.0),

            //main screen scrollable widgets
            child: ListView(
              shrinkWrap: true,
              children: <Widget>[
                Text(
                  getTranslation('Bestiee'),
                  style: kBazarGalleryTitleStyle,
                ),
                SizedBox(
                  height: 20,
                ),
                Padding(
                  padding: const EdgeInsets.only(bottom: 10),
                  child: Text('Featured Items', style: kFeatureTitleTextStyle),
                ),
                Container(
                  height: 700 / 3.5,
                  child: ListView.builder(
                      scrollDirection: Axis.horizontal,
                      shrinkWrap: true,
                      itemCount: HotScreen.items.length,
                      itemBuilder: (contet, int index) {
                        return SingleItemCard(
                          item: HotScreen.items[index],
                          isPersonItem: false,
                          moduleName: HotScreen.items[index].moduleName,
                        );
                      }),
                ),

                SizedBox(
                  height: 10,
                ),

                //more button
                Align(
                  alignment: Alignment.topLeft,
                  child: Container(
                    width: 80,
                    height: 30,
                    child: SmallRoundMoreButton(onPressed: () {
                      Navigator.pushNamed(context, MoreHotItemsScreen.id);
                    }),
                  ),
                ),

                SizedBox(
                  height: 20,
                ),

                //second hand section
                Padding(
                  padding: const EdgeInsets.only(bottom: 10),
                  child: Text('Second Hand', style: kFeatureTitleTextStyle),
                ),
//                Container(
//                  height: 700 / 3.5,
//                  child: ListView.builder(
//                      scrollDirection: Axis.horizontal,
//                      shrinkWrap: true,
//                      itemCount: 9,
//                      itemBuilder: (contet, int index) {
//                        return SingleItemCard();
//                      }),
//                ),

                SizedBox(
                  height: 10,
                ),
                Align(
                  alignment: Alignment.topLeft,
                  child: Container(
                    width: 80,
                    height: 30,
                    child: SmallRoundMoreButton(
                      onPressed: () {
                        Navigator.pushNamed(context, MoreHotItemsScreen.id);
                      },
                    ),
                  ),
                ),
                SizedBox(
                  height: 30,
                ),

                //places section
                Text(
                  'Featured Plces',
                  style: kFeatureTitleTextStyle,
                ),

                Container(
                  height: 140,
                  child: ListView.builder(
                    scrollDirection: Axis.horizontal,
                    shrinkWrap: true,
                    itemCount: HotScreen.places.length,
                    itemBuilder: (context, int index) {
                      return Container(
                          width: 190,
                          height: 100,
                          child: SinglePlaceCard(
                            place: HotScreen.places[index],
                          ));
                    },
                  ),
                ),
//

                Align(
                  alignment: Alignment.topLeft,
                  child: Container(
                    width: 80,
                    height: 30,
                    child: SmallRoundMoreButton(
                      onPressed: () {
                        Navigator.pushNamed(context, MoreHotPlacesScreen.id);
                      },
                    ),
                  ),
                ),
                SizedBox(
                  height: 30,
                ),

                //people section
                Padding(
                  padding: const EdgeInsets.only(bottom: 10.0),
                  child: Text(
                    'People',
                    style: kFeatureTitleTextStyle,
                  ),
                ),

                Container(
                  height: 120,
                  child: ListView.builder(
                    scrollDirection: Axis.horizontal,
                    shrinkWrap: true,
                    itemCount: HotScreen.people.length,
                    itemBuilder: (context, int index) {
                      return Align(
                        alignment: Alignment.topLeft,
                        child: Padding(
                          padding: const EdgeInsets.only(right: 8.0),
                          child: SinglePersonHotScreenCard(
                            person: HotScreen.people[index],
                          ),
                        ),
                      );
                    },
                  ),
                ),

                Align(
                  alignment: Alignment.topLeft,
                  child: Container(
                    width: 80,
                    height: 30,
                    child: SmallRoundMoreButton(
                      onPressed: () {
                        Navigator.pushNamed(context, MoreHotPeopleScreen.id);
                      },
                    ),
                  ),
                ),

                SizedBox(
                  height: 60,
                ),


                //single shops section
                Container(
                  height: 100.0 * 3,
                  child: Column(
                    children: List.generate(5,  (index) {
                      return Align(
                        alignment: Alignment.topLeft,
                        child: Padding(
                            padding: const EdgeInsets.only(right: 8.0),
                            child: SinglePlaceLargeHotCard(place: HotScreen.places[index],)
                        ),
                      );
                    }),
                  )
                ),


              ],
            ),
          ),
        ),
      ),
    );
  }
}

code for SingleItemCard :


class SingleItemCard extends StatelessWidget {
  SingleItemCard({
  this.cardHeight = 300,
  this.cardWidth = 200,
  this.color = kAppOrangeColor,
  this.isExtraInfoShowen = true,
  this.item,
  this.isPersonItem,
  this.moduleName});

  final double cardHeight;
  final double cardWidth;
  final Color color;
  final bool isExtraInfoShowen;
  final Item item;
  final bool isPersonItem;
  final String moduleName;



  @override
  Widget build(BuildContext context) {

    String imageURL ;

  //remove the null part in production
  if(item.moduleName == 'item' || item.moduleName == null ){
  imageURL = imageBaseURLPlaces + item.imageVariants[0].imageURL;
  }else if (item.moduleName == 'person' ){
  imageURL = imageBaseURLPeople + item.imageVariants[0].imageURL;

  print(imageURL);
  }

  int ratingUserCount = item.ratingUserCount;



  return FittedBox(
      child: Padding(
        padding: const EdgeInsets.only(right: 7),
        child: GestureDetector(
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => SingleItemScreen(
                  item: item,
                ),
              ),
            );
          },
          child: Stack(
            alignment: Alignment.bottomRight,
            children: <Widget>[
              //upper item description
              Column(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  Visibility(
                    visible: isExtraInfoShowen,
                    child: CircleRater(
                      isRatingDisabled: true,
                      rating: item.rating,
                      ratingUserCount: item.ratingUserCount != null ?  ratingUserCount : 0,
                    ),
                  ),
                  SizedBox(
                    width: 150,
                    child: Text(
//                      'Item Name Here no. 1',
                      item.name,
                      overflow: TextOverflow.clip,
                      style: TextStyle(
                          fontSize: 15,
                          color: Colors.black,
                          fontWeight: FontWeight.w500),
                      softWrap: false,
                    ),
                  ),

                  //item card
                  Container(
                    height: cardHeight,
                    width: cardWidth,
                    child: Container(
                      width: 100,
                      decoration: BoxDecoration(
                        color: color,
                        borderRadius: BorderRadius.all(Radius.circular(10)),
                      ),
                      child: ClipRRect(
                        borderRadius: BorderRadius.all(Radius.circular(10)),
//                        child: Image.asset(
//                          'images/item-image1.jpeg',
//                          width: 180,
//                          fit: BoxFit.fill,
//                        ),
                        child: CachedNetworkImage(imageUrl: imageURL, fit: BoxFit.fill,)
                      ),
                    ),
                  ),
                ],
              ),

              //lower price widget
              Visibility(
                visible: isExtraInfoShowen,
                child: Align(
                  alignment: Alignment.bottomLeft,
                  child: Container(
                    width: 100,
                    height: 20,
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.only(
                            bottomRight: Radius.circular(10),
                            topLeft: Radius.circular(5)),
                        color: color),
                    child: Padding(
                      padding: const EdgeInsets.symmetric(
                          vertical: 3, horizontal: 4),
                      child: Align(
                        alignment: Alignment.bottomRight,
                        child: Text(
//                          '1000000 IQD',
                          item.priceIQD.toString() + ' IQD',
                          style: TextStyle(
                            fontSize: 12,
                            color: Colors.white,
                          ),
                          textAlign: TextAlign.start,
                        ),
                      ),
                    ),
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}


SinglePlaceCard : 


class SinglePlaceCard extends StatelessWidget {

  SinglePlaceCard({this.place});

  Place place;

  @override
  Widget build(BuildContext context) {

    double screenWidth = MediaQuery.of(context).size.width;
    double widgetWidth = screenWidth / 2.3;
    int ratingUserCount = place.ratingUserCount;

    String imageURL = place.coverImages != null
        ? imageBaseURLPlaces +
        place.coverImages[0].imageURL
        : '';

    return GestureDetector(
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => SinglePlaceScreen(place: place, isScreenCalledFromOwnerSelfRegistrationScreen: false,),
          ),
        );
      },
      child: FittedBox(
        child: Padding(
          padding: const EdgeInsets.only(right: 10),
          child: Container(
            child: Stack(
              alignment: Alignment.bottomRight,
              children: <Widget>[
                Column(
                  //upper place name and rating
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    CircleRater(
                      isRatingDisabled: true,
                      rating: place.rating,
                      ratingUserCount: place.ratingUserCount != null ?  ratingUserCount : 0,
                    ),
                    SizedBox(
                      width: widgetWidth,
                      child: Text(
                        place.name,
                        overflow: TextOverflow.clip,
                        style: TextStyle(
                            fontSize: 12,
                            color: Colors.black,
                            fontWeight: FontWeight.w500),
                        softWrap: false,
                      ),
                    ),

                    //place card image
                    Container(
                      height: 80,
                      width: 170,
                      child: Container(
                        width: 150,
                        decoration: BoxDecoration(
                          color: kAppMainDarkGreenColor,
                          borderRadius: BorderRadius.all(Radius.circular(10)),
                        ),
                        child: ClipRRect(
                          borderRadius: BorderRadius.all(Radius.circular(10)),
                          child: place.coverImages == null
                              ? Image.asset(
                            'images/blank-placeholder-images/blank-image-placeholder.png',
                            width: screenWidth / 4,
                            fit: BoxFit.fill,
                          )
                              : CachedNetworkImage(
                            imageUrl: imageURL,
                            fit: BoxFit.fill,
                              ),
                        ),
                      ),
                    ),
                  ],
                ),

                //lower tag
                Align(
                  alignment: Alignment.bottomLeft,
                  child: Container(
                    width: 100,
                    height: 20,
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(3),
                            bottomRight: Radius.circular(8)),
                        color: Colors.black54),
                    child: Padding(
                      padding: const EdgeInsets.symmetric(
                          vertical: 3, horizontal: 4),
                      child: Align(
                        alignment: Alignment.bottomRight,
                        child: FittedBox(
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.end,
                            children: <Widget>[
                              EyeIconWithText(
                                size: 13,
                              ),
                              Padding(
                                padding: const EdgeInsets.only(right: 5),
                                child: Container(
                                  width: 10,
                                  height: 10,
                                  decoration: BoxDecoration(
                                      borderRadius: BorderRadius.all(
                                        Radius.circular(8),
                                      ),
                                      color: place.isOnline != null &&
                                          place.isOnline == true
                                          ? Colors.green
                                          : Colors.red),
                                ),
                              ),
                              Text(
                                place.isOpen != null
                                    ? place.isOpen ? 'Open Now' : 'Colosed Now'
                                    : 'Closed Now',
                                style: TextStyle(
                                  fontSize: 12,
                                  color: Colors.white,
                                ),
                                textAlign: TextAlign.start,
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

SinglePersonHotScreenCard :


class SinglePersonHotScreenCard extends StatelessWidget {

  SinglePersonHotScreenCard(
      { this.isExtraInfoShowen = false, this.person});

  final bool isExtraInfoShowen;
  final Person person;

  @override
  Widget build(BuildContext context) {

    double screenWidth = MediaQuery.of(context).size.width;
    final ratingPersonCount = person.ratingUserCount;

    return Padding(
      padding: const EdgeInsets.only(bottom: 10),
      child: GestureDetector(
        onTap: () {
          Navigator.pushNamed(context, SinglePersonScreen.id);
        },
        child: FittedBox(
          child: Column(
            children: <Widget>[
              //upper label and rating
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  CircleRater(
                    isRatingDisabled: true,
                    rating: person.rating,
                    ratingUserCount: person.ratingUserCount != null ?  ratingPersonCount : 0,
                  ),
                  Text(
                    person.name,
                    style: TextStyle(color: Colors.black),
                  ),
                ],
              ),

              Stack(
                alignment: Alignment.center,
                children: <Widget>[
                  Container(
                    decoration: BoxDecoration(
                      color: Colors.black54,
                      borderRadius: BorderRadius.all(Radius.circular(1000)),
                    ),
                    width: screenWidth / 3.6,
                    height: screenWidth / 3.6,
                  ),

                  Visibility(
                    visible: person != null,
                    child: CircleAvatar(
                        radius: 50,
                        backgroundImage: NetworkImage(imageBaseURLPeople + person.profileImageURL,)
                    ),
                  ),

                  Visibility(
                    visible: person == null,
                    child: ClipOval(
                      child: SizedBox(
                        width: screenWidth / 4,
                        child:
                        Image.asset('images/blank-placeholder-images/person-placeholder.jpg'),
                      ),
                    ),
                  ),
                ],
              ),
              Text(
                person.jobTitle,
                style: TextStyle(color: Colors.black),
              ),
              Padding(
                padding: const EdgeInsets.only(top: 2.0),
                child: Container(
                  width: 15,
                  height: 15,
                  decoration: kOnlineOfflineIndicatorBoxDecoration,
                ),
              ),

              Padding(
                padding: const EdgeInsets.all(4.0),
                child: Visibility(
                  visible: this.isExtraInfoShowen,
                  child: ThumbsUpWithLabel(),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

I have ran the devtool, the app uses too much m memory, I am not sure why.

I changed the code there is a lot less crashes. I use this code

getAllPlaces() async {

    List<Place> places = widget.places;
    places.removeRange(0, places.length);

    List<Place> _places = [];

    Response response = await getRequest(devBaseURL + plainPlaceAPI);
    var allPlacesMap = jsonDecode(response.body);


    for (int i = 0; i < allPlacesMap.length; i++) {
      var json = allPlacesMap[i];
      Place place = Place.fromJson(json);

      _places.add(place);
    }
    setState(() {
      places.addAll(_places );
      print('all added');
    });

  }

Upvotes: 2

Views: 3849

Answers (1)

Omatt
Omatt

Reputation: 10519

The app crash seems to be caused by Out Of Memory issue. Checking on the snippets you've provided, memory issues were caused by nested ListViews rendering Widgets that displays images. While the widgets in ListViews are only rendered when near the viewport, multiple ListViews still adds up to the total memory consumed. I suggest trimming down the images displayed and nest ListViews sparingly to prevent these issues.

Upvotes: 1

Related Questions