GregoryConrad / rearch-dart

Re-imagined approach to application design and architecture
https://pub.dev/packages/rearch
MIT License
69 stars 4 forks source link

Possible bug on `use.automaticKeepAlive` I #198

Closed busslina closed 1 week ago

busslina commented 1 week ago

Stack trace:

══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY
╞═════════════════════════════════════════════════════════
The following assertion was thrown during performLayout():
setState() or markNeedsBuild() called during build.
This PostWidget widget cannot be marked as needing to build
because the framework is already in the
process of building widgets. A widget can be marked as
needing to be built during the build phase
only if one of its ancestors is currently building. This
exception is allowed because the framework
builds parent widgets before children, which means a dirty
descendant will always be built.
Otherwise, the framework might not visit this widget during
this build phase.
The widget on which setState() or markNeedsBuild() was called
was:
  PostWidget-[<'8eb9a794-0e3b-493d-8c1e-34d30ae254bf'>]
The widget which was currently being built when the offending
call was made was:
  SliverList

The relevant error-causing widget was:
  SliverList
  SliverList:file:///C:/Users/vicen/workspace/FLUTTER/LIBS/bu
  sslina-flutter-web-lib/lib/v2/widgets/screens/posts/posts.s
  creen.dart:90:26

When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/err
ors.dart 296:3       throw_
packages/flutter/src/widgets/framework.dart 5047:9
<fn>
packages/flutter/src/widgets/framework.dart 5058:14
markNeedsBuild
packages/flutter_rearch/src/widgets/consumer.dart 151:5
rebuild
packages/rearch/src/side_effects.dart 71:12
setter
packages/flutter_rearch/src/side_effects/keep_alive.dart 34:7
<fn>
packages/flutter_rearch/src/widgets/consumer.dart 114:7
deactivate
packages/flutter/src/widgets/framework.dart 2095:12
_deactivateRecursively
packages/flutter/src/widgets/framework.dart 6755:7
visitChildren
packages/flutter/src/widgets/framework.dart 2097:12
_deactivateRecursively
packages/flutter/src/widgets/framework.dart 5539:7
visitChildren
packages/flutter/src/widgets/framework.dart 2097:12
_deactivateRecursively
packages/flutter/src/widgets/framework.dart 6755:7
visitChildren
packages/flutter/src/widgets/framework.dart 2097:12
_deactivateRecursively
packages/flutter/src/widgets/framework.dart 5539:7
visitChildren
packages/flutter/src/widgets/framework.dart 2097:12
_deactivateRecursively
packages/flutter/src/widgets/framework.dart 6755:7
visitChildren
packages/flutter/src/widgets/framework.dart 2097:12
_deactivateRecursively
packages/flutter/src/widgets/framework.dart 6755:7
visitChildren
packages/flutter/src/widgets/framework.dart 2097:12
_deactivateRecursively
packages/flutter/src/widgets/framework.dart 6755:7
visitChildren
packages/flutter/src/widgets/framework.dart 2097:12
_deactivateRecursively
packages/flutter/src/widgets/framework.dart 5539:7
visitChildren
packages/flutter/src/widgets/framework.dart 2097:12
_deactivateRecursively
packages/flutter/src/widgets/framework.dart 2109:7
add
packages/flutter/src/widgets/framework.dart 4382:5
deactivateChild
packages/flutter/src/widgets/framework.dart 3772:9
updateChild
packages/flutter/src/widgets/sliver.dart 860:37
updateChild
packages/flutter/src/widgets/sliver.dart 887:33
<fn>
packages/flutter/src/widgets/framework.dart 2845:11
buildScope
packages/flutter/src/widgets/sliver.dart 883:5
removeChild
packages/flutter/src/rendering/sliver_multi_box_adaptor.dart
376:7                [_destroyOrCacheChild]
packages/flutter/src/rendering/sliver_multi_box_adaptor.dart
571:9                <fn>
packages/flutter/src/rendering/object.dart 2688:51
<fn>
packages/flutter/src/rendering/object.dart 1097:7
[_enableMutationsToDirtySubtrees]
packages/flutter/src/rendering/object.dart 2688:7
invokeLayoutCallback
packages/flutter/src/rendering/sliver_multi_box_adaptor.dart
569:5                collectGarbage
packages/flutter/src/rendering/sliver_list.dart 299:5
performLayout
packages/flutter/src/rendering/object.dart 2577:7
layout
packages/flutter/src/rendering/viewport.dart 601:12
layoutChildSequence
packages/flutter/src/rendering/viewport.dart 1516:12
[_attemptLayout]
packages/flutter/src/rendering/viewport.dart 1427:20
performLayout
packages/flutter/src/rendering/object.dart 2416:7
[_layoutWithoutResize]
packages/flutter/src/rendering/object.dart 1051:17
flushLayout
packages/flutter/src/rendering/object.dart 1064:14
flushLayout
packages/flutter/src/rendering/binding.dart 577:5
drawFrame
packages/flutter/src/widgets/binding.dart 1138:13
drawFrame
packages/flutter/src/rendering/binding.dart 443:5
[_handlePersistentFrameCallback]
packages/flutter/src/scheduler/binding.dart 1392:7
[_invokeFrameCallback]
packages/flutter/src/scheduler/binding.dart 1313:9
handleDrawFrame
packages/flutter/src/scheduler/binding.dart 1171:5
[_handleDrawFrame]
lib/_engine/engine/platform_dispatcher.dart 1404:5
invoke
lib/_engine/engine/platform_dispatcher.dart 307:5
invokeOnDrawFrame
lib/_engine/engine/initialization.dart 187:36
<fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/ope
rations.dart 426:37  _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/ope
rations.dart 431:39  dcall

Parent widget:

return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        // Create post button
        if (debugInfo.postCreate || auth.isRoot)
          ElevatedButton(
            onPressed: () => context.beamToNamed(Routes.postsCreate),
            child: Label(
              lang.createNewPost.firstUpper,
              color: Colors.black,
            ),
          ),

        // Posts
        Expanded(
          child: CustomScrollView(
            controller: scrollController,
            scrollDirection: Axis.vertical,
            slivers: [
              SliverList.list(
                addAutomaticKeepAlives: false,
                children: posts
                    .map(
                      (post) => PostWidget(
                        post: post,
                        parentHeight: parentConstraints.maxHeight,
                      )
                          .withSizeConstraints(postConstraints)
                          .withMargin(
                            const EdgeInsets.symmetric(vertical: 30),
                          )
                          .centered(key: Key(post.id)),
                    )
                    .toList(),
              ),
            ],
          ).withRawScrollbar(controller: scrollController, active: true),
        ),

        // Posts loading indicator
        if (!postsDataState.loaded)
          const CircularProgressIndicator().marginBottom(20).centered(),

        // Load more button
        if (postsDataState.loaded &&
            !postsController.reachedLastPost() &&
            isScrollAtBottom.value)
          ElevatedButton(
            onPressed: () => postsController.load(),
            child: Label(
              lang.loadMore.firstUpper,
              color: Colors.black,
            ),
          ),
      ],
    ).sized(width: double.infinity);

Child widget (PostWidget):

@override
  Widget build(BuildContext context, WidgetHandle use) {
    use.automaticKeepAlive();

    return LayoutBuilder(
      builder: (context, constraints) => RearchBuilder(
        builder: (context, use) {
          final lang = use(languageCapsule);
          final state = use.data(_PostState.normal);

          return Stack(
            children: [
              // Default layer
              Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  // Title
                  _buildTitle(lang),

                  // Divider
                  const Divider(
                    height: 10,
                    thickness: 2,
                    color: Color.fromARGB(255, 117, 117, 117),
                  ),

                  // Body
                  buildBody(context, use, constraints, lang),
                ],
              ),

              // Actions layer
              if (state.value.normal)
                _ActionsLayer(
                  post: post,
                  postState: state,
                ),

              // Removing state layer
              if (!state.value.normal)
                Positioned.fill(
                  child: const CircularProgressIndicator().centered().bgColor(
                        Colors.grey.withOpacity(0.5),
                        active: false,
                      ),
                ),
            ],
          )
              .withPadding(
                const EdgeInsets.symmetric(
                  horizontal: 20,
                  vertical: 10,
                ),
              )
              .bgColor(
                const Color.fromARGB(255, 189, 171, 171),
              )
              .roundedCrownBorder(
                color: const Color.fromARGB(255, 117, 117, 117),
                radius: 15,
              );
        },
      ),
    );
busslina commented 1 week ago

So... I found this Flutter bug, and also I found a bypass solution to it. Now this ReArch issue seems gone. But I found another related that seems to indicate that something is wrong with use.automaticKeepAlive. I'm opening another issue...

GregoryConrad commented 1 week ago

If this is triggered by a bug in the framework, then I'm going to close this in favor of the bug in the framework (as it's not a ReArch-specific issue). Please re-open this if I misread what you said.