Skip to content

'manifest.fromHero == newManifest.toHero': is not true. #172054

@PiCake314

Description

@PiCake314

I got this error message:

══╡ EXCEPTION CAUGHT BY SCHEDULER LIBRARY ╞════════════════════════════
The following assertion was thrown during a scheduler callback:
'package:flutter/src/widgets/heroes.dart': Failed assertion: line 732 pos 14: 'manifest.fromHero == newManifest.toHero': is not true.

Either the assertion indicates an error in the framework itself,
or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=2_bug.yml

When the exception was thrown, this was the stack:
#2      _HeroFlight.divert (package:flutter/src/widgets/heroes.dart:732:14)
#3      HeroController._startHeroTransition (package:flutter/src/widgets/heroes.dart:1041:26)
#4      HeroController._maybeStartHeroTransition.<anonymous closure> (package:flutter/src/widgets/heroes.dart:958:9)
#5      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1442:15)
#6      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1369:11)
#7      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1208:5)
#8      _invoke (dart:ui/hooks.dart:316:13)
#9      PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:428:5)
#10     _drawFrame (dart:ui/hooks.dart:288:31)
(elided 2 frames from class _AssertionError)
═══════════════════════════════════════════════════════════════

The program does function correctly, but seeing this error message was asking me to file a bug report & I couldn't find the bug being reported before, I decided to go ahead.
I'll try to include as much information as I can that I think are relevant.

I've got this widget that loads a page based on a future:

import 'package:flutter/material.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:wink/WaitingRooms.dart';


enum FutureState {
  FETCH_ERROR,
  BAD,
  GOOD,
}


class LoadPage extends StatelessWidget {
  final Future future;
  final void Function(FutureState, String) on_error;
  final String name;
  final bool is_host;
  const LoadPage({
    super.key,
    required this.future,
    required this.on_error,
    required this.name,
    required this.is_host,
  });


  @override
  Widget build(BuildContext context) => FutureBuilder(
      future: future,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting)
          return Scaffold(
            body: Center(
              child: LoadingAnimationWidget.twoRotatingArc(
                color: Theme.of(context).colorScheme.primary,
                size: 69,
              ),
            ),
          );

        else if (
          snapshot.hasError ||
          !snapshot.hasData ||
          (snapshot.data! as (FutureState, String)).$1 == FutureState.BAD
        ) {
          final state = snapshot.hasError ||(!snapshot.hasData) ? FutureState.FETCH_ERROR : FutureState.BAD;
          final err_msg = (snapshot.data as (FutureState, String)).$2;
          Future.microtask(() => on_error(state, err_msg));
          return const Scaffold();
        }

        final String code = (snapshot.data! as (FutureState, String)).$2;
        return WaitingRoom(room: code, name: name, is_host: is_host);
      }
    );
}

I have this method joinRoom that ends up pushing the LoadPage widget:

void joinRoom() {
  final name = name_controller.text.trim();
  if (name.isEmpty) return errMsg(context, "Please enter your name!");

  showDialog(
    context: context,
    builder: (_) => AlertDialog(
      title: const Text("Joining..", style: TextStyle(fontSize: 32)),
      content: TextField(
        controller: code_controller,
        keyboardType: TextInputType.number,
        decoration: const InputDecoration(
          hintText: "room code",
        ),
      ),
      actions: [
        TextButton(
          child: const Text("Cancel", style: TextStyle(fontSize: 20, color: Colors.red)),
          onPressed: Navigator.of(context).pop,
        ),

        TextButton(
          child: const Text("Join", style: TextStyle(fontSize: 20)),
          onPressed: () async {
            final input_code = code_controller.text.trim();
            if (input_code.isEmpty) return; // ignore empty input

            final Future<(FutureState, String)> Function() future = () async {
              if (!await roomExists(input_code)) 
                return (FutureState.BAD, "Room not found!..");


              final players = await getPlayerNames(input_code);
              if (players.contains(name))
                return (FutureState.BAD, "Name taken!");

              // else //, we're good to go
              await addPlayer(room: input_code, name: name);
              return (FutureState.GOOD, input_code);
            };

            Navigator.of(context).pop(); // close dialog

            Navigator.of(context).push(
              PageRouteBuilder(
                pageBuilder: (_, _, _) => LoadPage(
                  future: memo.runOnce(future),
                  on_error: (state, err) {
                    if (state == FutureState.FETCH_ERROR) errMsg(context, "An error has occurred!");
                    else errMsg(context, err);

                    Navigator.of(context).pop();
                  },
                  name: name,
                  is_host: false,
                ),
                settings: const RouteSettings(name: "Waiting Room"),
                transitionsBuilder:(_, animation, _, child) => FadeTransition(opacity: animation, child: child),
              )
            );

            memo = AsyncMemoizer(); // VERY IMPORTANT LOL
          }
        ),
      ],
    ),
  );
}

I've also got this error message function that displays a snackbar. You can see it being called inside the on_error lambda when pushing the LoadPage widget.

void errMsg(final BuildContext context, final String label) {
  ScaffoldMessenger.of(context).removeCurrentSnackBar();
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text(label),
      behavior: SnackBarBehavior.floating,
    ),
  );
}

The assertion failure happens when I try to show the SnackBar before the previous SnackBar disappears. For example, if I try to enter a room that doesn't exist, I will get a SnackBar saying "Room not fond!..". Attempting to join the room again before waiting for the SnackBar to disappear will cause the assertion failure. There is no assertion failure if I try to join the room after the SnackBar disappears.
This is the only case in the app where displaying a SanckBar when another is already being displayed causes an issue. Usually, the previous SnackBar gets removed since the errMsg function calls ScaffoldMessenger.of(context).removeCurrentSnackBar() before displaying the new SnackBar.

This is all that I could gather in the meantime. I didn't try to get a minimal working example so feel free to do so.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: error messageError messages from the Flutter frameworkframeworkflutter/packages/flutter repository. See also f: labels.team-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy