Skip to content

PageStorageKeys might benefit from a way to identify their primary client #10868

Open

Description

@HansMuller

Internal: b/292548528

Customer Leafy ran into a confusing problem: ExpansionTiles were asserting in initState():

Error: type 'double' is not a subtype of type 'bool' of 'value'
...

#0 _ExpansionTileState._isExpanded= (package:flutter/src/material/expansion_tile.dart:81)
#1 _ExpansionTileState.initState (package:flutter/src/material/expansion_tile.dart:95)
#2 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3502)
...

The problem was that the ExpansionTile's were looking up their expanded/collapsed boolean with PageStorage, but only the enclosing PageView specified a PageStorageKey. PageView uses PageStorage to save its double scroll offset.

In this case it would have been preferable for the expansion tiles to not save their state.

Some possible ways to improve things:

  • ExpansionTiles don't save their state by default. The dartdoc for a default false keepExpansionState could cover the need for a PageStorageKey.

  • Scrollables include an inherited widget that only exists to stop the page storage key tree traversal. If a key wasn't found by the time this widget was reached, the traversal would short-circuit.

The second approach seems like it would result in most apps quietly getting the behavior they'd expect. It would complicate explaining how PageStorageKeys work a little.

Activity

  1. Hixie commented on Jun 21, 2017

    @Hixie
    Contributor

    I guess another option is that we add the type back in to the key somehow, though then you'd have all the panels stomping on each other which is no good.

  2. devantic commented on Apr 26, 2018

    @devantic

    I know this is old but just ran into same problem myself. For a flutter beginner it's difficult to know what the problem is from the reported error. The simple fix is to specify a PageStorageKey as the ExpansionTile key. Hope this save someone some time!

    new ExpansionTile(key: new PageStorageKey("any string"),  ...
  3. added this to the milestone on May 1, 2018
  4. kgain21 commented on Nov 8, 2018

    @kgain21

    Hey I actually just ran into this problem in a slightly different way. I'm using ExpansionTiles in a TabBarView and each ExpansionTile has a number of SwitchListTiles. I added a PageStorageKey to my ExpansionTiles so the expanded state would be saved but started getting this error. The fix for me was to add a PageStorageKey to each SwitchListTile as well since they have their own boolean selected values which were probably confusing the scroll offset lookup. Just throwing this out there in case this happens to anyone else!

  5. psine commented on Nov 21, 2018

    @psine

    I know this is old but just ran into same problem myself. For a flutter beginner it's difficult to know what the problem is from the reported error. The simple fix is to specify a PageStorageKey as the ExpansionTile key. Hope this save someone some time!

    new ExpansionTile(key: new PageStorageKey("any string"),  ...
    

    You are right that the error is rather unhelpful, but there seem to be similar errors whenever widgets expect cached properties but can't find them. If you have more than one ExpansionTile, however, I believe they will each need a unique key to avoid stepping on each other.

  6. volkangurol commented on Jun 8, 2019

    @volkangurol

    I know this is old but just ran into same problem myself. For a flutter beginner it's difficult to know what the problem is from the reported error. The simple fix is to specify a PageStorageKey as the ExpansionTile key. Hope this save someone some time!

    new ExpansionTile(key: new PageStorageKey("any string"),  ...

    I used "Key" It didnt work in my project. Thank you for your answer

  7. bsr203 commented on Jul 28, 2019

    @bsr203

    Hi. I have the opposite behavior. I am using ExpansionTile in a ListView. The doc say to use PageStorageKey

    /// This widget is typically used with [ListView] to create an
    /// "expand / collapse" list entry. When used with scrolling widgets like
    /// [ListView], a unique [PageStorageKey] must be specified to enable the
    /// [ExpansionTile] to save and restore its expanded state when it is scrolled
    /// in and out of view. 
    

    when I add PageStorageKey like,

      Material(
              child: ExpansionTile(
                key: PageStorageKey<String>(event.id),
                title: Text(event.text),
                initiallyExpanded: false,
                children: <Widget>[
                .....
    

    I get error when expand. Even with one item. Is it safe to use without PageStorageKey if there are multiple items and list is scrolled? Thanks.

    Edit: Looks like the error is from a child TextEdit widget. For example,

    class MsgInput extends StatefulWidget {
      @override
      _MsgInputState createState() => _MsgInputState();
    }
    
    class _MsgInputState extends State<MsgInput> {
      final _formKey = GlobalKey<FormState>();
      var _passKey = GlobalKey<FormFieldState>();
    
      @override
      Widget build(BuildContext context) {
        return Form(
          key: _formKey,
          child: TextFormField(
            key: _passKey,
            decoration: InputDecoration(labelText: 'Enter Name', hintText: 'Name'),
            validator: (value) {
              if (value.isEmpty) {
                return 'Please enter a name';
              }
              return null;
            },
            onSaved: (value) {
              setState(() {
                // _name = value;
              });
            },
          ),
        );
      }
    }
    
    flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
    flutter: The following assertion was thrown building
    flutter: EditableText-[LabeledGlobalKey<EditableTextState>#33601](controller:
    flutter: TextEditingController#8c7bb(TextEditingValue(text: ┤├, selection: TextSelection(baseOffset: -1,
    flutter: extentOffset: -1, affinity: TextAffinity.downstream, isDirectional: false), composing:
    flutter: TextRange(start: -1, end: -1))), focusNode: FocusNode#1c794, debugLabel: (englishLike subhead
    flutter: 2014).merge(blackCupertino subhead), inherit: false, color: Color(0xdd000000), family: .SF UI Text,
    flutter: size: 16.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, textAlign: start,
    flutter: keyboardType: TextInputType(name: TextInputType.text, signed: null, decimal: null), dependencies:
    flutter: [_FocusMarker], state: EditableTextState#fd565(tickers: tracking 2 tickers)):
    flutter: type 'bool' is not a subtype of type 'double'
    flutter:
    flutter: Either the assertion indicates an error in the framework itself, or we should provide substantially
    flutter: more information in this error message to help you determine and fix the underlying cause.
    flutter: In either case, please report this assertion by filing a bug on GitHub:
    flutter:   https://github.com/flutter/flutter/issues/new?template=BUG.md
    flutter:
    flutter: When the exception was thrown, this was the stack:
    flutter: #0      ScrollPosition.restoreScrollOffset 
    package:flutter/…/widgets/scroll_position.dart:356
    flutter: #1      new ScrollPosition 
    package:flutter/…/widgets/scroll_position.dart:82
    flutter: #2      new ScrollPositionWithSingleContext 
    package:flutter/…/widgets/scroll_position_with_single_context.dart:57
    flutter: #3      ScrollController.createScrollPosition 
    package:flutter/…/widgets/scroll_controller.dart:233
    flutter: #4      ScrollableState._updatePosition 
    package:flutter/…/widgets/scrollable.dart:328
    flutter: #5      ScrollableState.didChangeDependencies 
    package:flutter/…/widgets/scrollable.dart:337
    flutter: #6      StatefulElement._firstBuild 
    package:flutter/…/widgets/framework.dart:4051
    flutter: #7      ComponentElement.mount 
    package:flutter/…/widgets/framework.dart:3902
    flutter: #8      Element.inflateWidget 
    package:flutter/…/widgets/framework.dart:3084
    flutter: #9      Element.updateChild 
    package:flutter/…/widgets/framework.dart:2887
    flutter: #10     ComponentElement.performRebuild 
    package:flutter/…/widgets/framework.dart:3935
    flutter: #11     Element.rebuild 
    package:flutter/…/widgets/framework.dart:3721
    flutter: #12     ComponentElement._firstBuild 
    package:flutter/…/widgets/framework.dart:3907
    flutter: #13     StatefulElement._firstBuild 
    package:flutter/…/widgets/framework.dart:4053
    flutter: #14     ComponentElement.mount 
    package:flutter/…/widgets/framework.dart:3902
    flutter: #15     Element.inflateWidget 
    package:flutter/…/widgets/framework.dart:3084
    flutter: #16     Element.updateChild 
    package:flutter/…/widgets/framework.dart:2887
    flutter: #17     SingleChildRenderObjectElement.mount 
    package:flutter/…/widgets/framework.dart:5092
    flutter: #18     Element.inflateWidget 
    package:flutter/…/widgets/framework.dart:3084
    flutter: #19     Element.updateChild 
    package:flutter/…/widgets/framework.dart:2887
    flutter: #20     _RenderDecorationElement._mountChild 
    package:flutter/…/material/input_decorator.dart:1481
    flutter: #21     _RenderDecorationElement.mount 
    package:flutter/…/material/input_decorator.dart:1496
    flutter: #22     Element.inflateWidget 
    package:flutter/…/widgets/framework.dart:3084
    flutter: #23     Element.updateChild 
    package:flutter/…/widgets/framework.dart:2887
    flutter: #24     ComponentElement.performRebuild 
    package:flutter/…/widgets/framework.dart:3935
    flutter: #25     Element.rebuild 
    package:flutter/…/widgets/framework.dart:3721
    flutter: #26     ComponentElement._firstBuild 
    package:flutter/…/widgets/framework.dart:3907
    flutter: #27     StatefulElement._firstBuild 
    package:flutter/…/widgets/framework.dart:4053
    flutter: #28     ComponentElement.mount 
    package:flutter/…/widgets/framework.dart:3902
    flutter: #29     Element.inflateWidget 
    package:flutter/…/widgets/framework.dart:3084
    flutter: #30     Element.updateChild 
    package:flutter/…/widgets/framework.dart:2887
    flutter: #31     ComponentElement.performRebuild 
    package:flutter/…/widgets/framework.dart:3935
    flutter: #32     Element.rebuild 
    package:flutter/…/widgets/framework.dart:3721
    flutter: #33     ComponentElement._firstBuild 
    package:flutter/…/widgets/framework.dart:3907
    flutter: #34     StatefulElement._firstBuild 
    package:flutter/…/widgets/framework.dart:4053
    flutter: #35     ComponentElement.mount 
    package:flutter/…/widgets/framework.dart:3902
    flutter: #36     Element.inflateWidget 
    package:flutter/…/widgets/framework.dart:3084
    flutter: #37     Element.updateChild 
    package:flutter/…/widgets/framework.dart:2887
    flutter: #38     SingleChildRenderObjectElement.mount 
    package:flutter/…/widgets/framework.dart:5092
    
  8. bennybat50 commented on Jan 16, 2020

    @bennybat50

    I used PageStorageKey("any string") to handle isuues with expantion tile throwing error when expanded in a listview or a drawer, and it worked perfectly for me. I know the implementation is different but thanks for the idea.

  9. added
    P2Important issues not at the top of the work list
    on May 29, 2020
  10. removed this from the milestone on Jun 2, 2020
  11. removed this from the milestone on Jun 2, 2020
  12. added
    f: scrollingViewports, list views, slivers, etc.
    c: proposalA detailed proposal for a change to Flutter
    c: crashStack traces logged to the console
    on Jul 22, 2020
  13. removed this from the milestone on Aug 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: crashStack traces logged to the consolec: proposalA detailed proposal for a change to Fluttercustomer: leafy (g3)f: scrollingViewports, list views, slivers, etc.frameworkflutter/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