Skip to content

Gym mode: Null check crash when saving log (setState called without mounted check) #1152

@vhhr7

Description

@vhhr7

Bug description

When saving a workout log in gym mode, the app crashes with:

Error: Null check operator used on a null value
#0  State.setState (package:flutter/src/widgets/framework.dart:1219)
#1  _LogFormWidgetState.build.<anonymous closure> (log_page.dart:653)

The log is saved correctly on the server (POST /api/v2/workoutlog/ returns 201), but the crash makes the user think it failed. On retry, duplicate log entries are created.

Steps to reproduce

  1. Open a routine in gym mode
  2. Enter reps/weight and tap Save
  3. App crashes immediately after the log is saved

Root cause

In lib/widgets/routines/forms/log_form.dart, the async save closure calls setState() in try/catch/finally after await addLog(...) without checking if (mounted) first. If the widget unmounts while the async operation is in flight, Flutter throws on setState of a disposed widget.

Additionally, setState(() { _isSaving = false; }) is called redundantly in both the try block and the finally block, so it always runs twice on success.

Current code (v1.10.2, log_form.dart)

try {
  await provider.Provider.of<RoutinesProvider>(context, listen: false).addLog(logToSave!);
  // ...
  if (mounted) {
    ScaffoldMessenger.of(context).showSnackBar(...); // ✅ mounted check here
  }
  widget.controller.nextPage(...);
  setState(() { _isSaving = false; });              // ❌ no mounted check
} on WgerHttpException {
  setState(() { _isSaving = false; });              // ❌ no mounted check, redundant with finally
  rethrow;
} finally {
  setState(() { _isSaving = false; });              // ❌ no mounted check
}

Proposed fix

Remove the redundant setState calls in try and catch, keep only the finally block wrapped with if (mounted):

try {
  await provider.Provider.of<RoutinesProvider>(context, listen: false).addLog(logToSave!);
  // ...
  if (mounted) {
    ScaffoldMessenger.of(context).showSnackBar(...);
  }
  widget.controller.nextPage(...);
  // setState removed here — finally handles it
} on WgerHttpException {
  rethrow; // setState removed — finally handles it
} finally {
  if (mounted) {                                    // ✅ mounted check added
    setState(() { _isSaving = false; });
  }
}

Environment

  • App version: 1.10.1 (build 180) and 1.10.2 (bug present in both)
  • Platform: Android
  • Server: self-hosted wger instance

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions