AturicLiqr
AturicLiqr

Reputation: 69

Memory leak problem / App crushes - StreamBuilder > StreamBuilder > GridView.builder in Flutter

I have faced very annoying issue. I found out that consumtion of memory is still growing and the app crashes saying "lost connection to device".

Please help, I probably don't do some dispose() method or sth similar but actually I have no idea where and how I could use it to clear / manage the memory properly. Maybe my code concept is wrong. Please have a look...

My code works like this:

  1. I am using StreamBuilder to get the collection of children from Firebase then I am sending child's details to the method EmotionsHelper where it gets counted and stored properly in Firebase.
  2. then I am using another StreamBuilder to fetch the counters of specific child's emotions
  3. Then I return GridView.builder of children's avatars with pictures and their emotions (fetched from previously created counters).

This code is working but at the same time consuming more and more memory and then it crashes saying "Lost connection to the device".

I checked it in Android Studio's profiler and noticed that it crashes when the memory reaches level approx 1,4GB on Android Device and more in iPad Air... for an iPad it takes longer to crash (probably because of better parameters comparing to my Android device). Now I have only 17 kids on the list in the app at the moment and it already crashes. So there is surely sth wrong I am doing.

Below you will find my code and screen shot from performance profiler view and at the very bottom, error details from Xcode after an iPad crush.

class ChildrenEmotionsScreen extends StatelessWidget {
  static const routeName = '/children-emotions-screen';

  @override
  Widget build(BuildContext context) {

    final isLandscape =
        MediaQuery.of(context).orientation == Orientation.landscape;
    final _firestore = FirebaseFirestore.instance;
    final user = FirebaseAuth.instance.currentUser;
    final _actualFacility =
        Provider.of<RegistrationHelper>(context).actualFacility;
    final _actualGroup = Provider.of<RegistrationHelper>(context).actualGroup;
    final _actualAccountId =
        Provider.of<RegistrationHelper>(context).actualAccountId;
    final _facilityId = Provider.of<RegistrationHelper>(context).facilityId;

    final streamChildren = _firestore
        .collection('root')
        .doc('accounts')
        .collection('accountData')
        .doc(_actualAccountId)
        .collection('facilities')
        .doc(_facilityId)
        .collection('children')
        .where('childGroupName', isEqualTo: _actualGroup)
        .snapshots();

    return Scaffold(
      appBar: AppBar(
        title: Text(
          'Emotions',
          style: TextStyle(
            color: Colors.white,
          ),
        ),
      ),
      body: Padding(
        padding: const EdgeInsets.only(top: 15.0),
        child: _actualAccountId.isEmpty || _facilityId.isEmpty
            ? Center(
                child: CircularProgressIndicator(),
              )
            : StreamBuilder(
                stream: streamChildren,
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.data == null) return CircularProgressIndicator();

                  if (snapshot.hasError) {
                    return new Text(
                        'Error in receiving snapshot: ${snapshot.error}');
                  }
                  if (!snapshot.hasData) {
                    return Center(
                      child: CircularProgressIndicator(
                        backgroundColor: Theme.of(context).primaryColor,
                      ),
                    );
                  }
                  final children = snapshot.data!.docs;
                  final totalChildren = children.length;
                  print('totalChildren: $totalChildren');

                  return GridView.builder(
                    physics: AlwaysScrollableScrollPhysics(),
                    itemCount: totalChildren,
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: isLandscape ? 5 : 3,
                      mainAxisSpacing: 5,
                      crossAxisSpacing: 5,
                    ),
                    itemBuilder: (BuildContext context, int index) {
                      final _childId = children[index]['childId'];

                      Provider.of<EmotionsHelper>(context, listen: false)
                          .getChildEmotionsMeasurementsAndUpdateToFirebase(
                        _childId,
                        children[index]['childAccountId'],
                        children[index]['childFacilityId'],
                        children[index]['childFirstName'],
                        children[index]['childLastName'],
                        children[index]['childImageUrl'],
                      );

                      final streamEmotions = FirebaseFirestore.instance
                          .collection('root')
                          .doc('accounts')
                          .collection('accountData')
                          .doc(_actualAccountId)
                          .collection('facilities')
                          .doc(_facilityId)
                          .collection('children')
                          .doc(_childId)
                          .collection('emotions')
                          .doc('counters_total')
                          .snapshots();

                      return StreamBuilder(
                          stream: streamEmotions,
                          builder: (BuildContext ctx,
                              AsyncSnapshot countersSnapshot) {
                            if (countersSnapshot.data == null)
                              return CircularProgressIndicator();
                            if (snapshot.hasError) {
                              return new Text(
                                  'Error in receiving snapshot: ${snapshot.error}');
                            }
                            if (!snapshot.hasData ||
                                snapshot.connectionState ==
                                    ConnectionState.waiting) {
                              return Center(
                                child: CircularProgressIndicator(
                                  backgroundColor:
                                      Theme.of(context).primaryColor,
                                ),
                              );
                            }

                            RegistrationHelper().updateActualFacilityId(
                                children[index]['childFacilityId']);
                            RegistrationHelper().updateActualAccountId(
                                children[index]['childAccountId']);
                            RegistrationHelper().updateActualChildId(
                                children[index]['childId']);

                            return ChildAvatar(
                              childId: children[index]['childId'],
                              childFacilityId: children[index]
                                  ['childFacilityId'],
                              childAccountId: children[index]['childAccountId'],
                              childFirstName: children[index]['childFirstName'],
                              childLastName: children[index]['childLastName'],
                              childImageUrl: children[index]['childImageUrl'] ??
                                  '$kChildPlaceHolderImageUrl',
                              fear: countersSnapshot.data!['fearLow'] +
                                  countersSnapshot.data!['fearMedium'] +
                                  countersSnapshot.data!['fearHigh'],
                              sadness: countersSnapshot.data!['sadnessLow'] +
                                  countersSnapshot.data!['sadnessMedium'] +
                                  countersSnapshot.data!['sadnessHigh'],
                              joy: countersSnapshot.data!['joyLow'] +
                                  countersSnapshot.data!['joyMedium'] +
                                  countersSnapshot.data!['joyHigh'],
                              calm: countersSnapshot.data!['calmLow'] +
                                  countersSnapshot.data!['calmMedium'] +
                                  countersSnapshot.data!['calmHigh'],
                              anger: countersSnapshot.data!['angerLow'] +
                                  countersSnapshot.data!['angerMedium'] +
                                  countersSnapshot.data!['angerHigh'],
                            );
                          });
                    },
                  );
                },
              ),
      ),
    );
  }
}

Performance profiler view - screen shot

Error details after iPad crush:

Details

The app “Runner” on iPad (Daniel) quit unexpectedly.
Domain: IDEDebugSessionErrorDomain
Code: 4
Failure Reason: Message from debugger: Terminated due to memory issue
User Info: {
    IDERunOperationFailingWorker = DBGLLDBLauncher;
}
--

Analytics Event: com.apple.dt.IDERunOperationWorkerFinished : {
    "device_model" = "iPad13,1";
    "device_osBuild" = "14.7.1 (18G82)";
    "device_platform" = "com.apple.platform.iphoneos";
    "launchSession_schemeCommand" = Run;
    "launchSession_state" = 2;
    "launchSession_targetArch" = arm64;
    "operation_duration_ms" = 74802;
    "operation_errorCode" = 4;
    "operation_errorDomain" = IDEDebugSessionErrorDomain;
    "operation_errorWorker" = DBGLLDBLauncher;
    "operation_name" = IDEiPhoneRunOperationWorkerGroup;
    "param_consoleMode" = 0;
    "param_debugger_attachToExtensions" = 0;
    "param_debugger_attachToXPC" = 1;
    "param_debugger_type" = 5;
    "param_destination_isProxy" = 0;
    "param_destination_platform" = "com.apple.platform.iphoneos";
    "param_diag_MainThreadChecker_stopOnIssue" = 0;
    "param_diag_MallocStackLogging_enableDuringAttach" = 0;
    "param_diag_MallocStackLogging_enableForXPC" = 1;
    "param_diag_allowLocationSimulation" = 1;
    "param_diag_gpu_frameCapture_enable" = 0;
    "param_diag_gpu_shaderValidation_enable" = 0;
    "param_diag_gpu_validation_enable" = 0;
    "param_diag_memoryGraphOnResourceException" = 0;
    "param_diag_queueDebugging_enable" = 1;
    "param_diag_runtimeProfile_generate" = 0;
    "param_diag_sanitizer_asan_enable" = 0;
    "param_diag_sanitizer_tsan_enable" = 0;
    "param_diag_sanitizer_tsan_stopOnIssue" = 0;
    "param_diag_sanitizer_ubsan_stopOnIssue" = 0;
    "param_diag_showNonLocalizedStrings" = 0;
    "param_diag_viewDebugging_enabled" = 1;
    "param_diag_viewDebugging_insertDylibOnLaunch" = 1;
    "param_install_style" = 0;
    "param_launcher_UID" = 2;
    "param_launcher_allowDeviceSensorReplayData" = 0;
    "param_launcher_kind" = 0;
    "param_launcher_style" = 0;
    "param_launcher_substyle" = 0;
    "param_runnable_appExtensionHostRunMode" = 0;
    "param_runnable_productType" = "com.apple.product-type.application";
    "param_runnable_swiftVersion" = "5.5";
    "param_runnable_type" = 2;
    "param_testing_launchedForTesting" = 0;
    "param_testing_suppressSimulatorApp" = 0;
    "param_testing_usingCLI" = 0;
    "sdk_canonicalName" = "iphoneos15.0";
    "sdk_osVersion" = "15.0";
    "sdk_variant" = iphoneos;
}
--


System Information

macOS Version 12.0 (Build 21A5522h)
Xcode 13.0 (19234) (Build 13A233)
Timestamp: 2021-10-22T23:54:42+02:00

Upvotes: 1

Views: 336

Answers (0)

Related Questions