Flutter: UniqueKey not working when removing/recreating widget

923

Solution 1

You should never hold a value in a view. Your views should ideally get all of their information from models and controllers.

This is because there are more variables in the state than just the slider value I have to preserve.

And yes, if the view is derived from many inputs, all of those inputs should be available to the view at build() time.

Solution 2

You can copy paste run full code below
In this case, you do not need key
You can use Visibility and set maintainState: true
code snippet

Visibility(
            visible: hidden, maintainState: true, child: widget.child)

working demo

enter image description here

full code

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(home: MyHomePage());
}

class MyHomePage extends StatefulWidget {
  MyHomePage();
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) => Scaffold(
      appBar: AppBar(title: Text("Problem: UniqueKey not working")),
      body: ContentHider(
        child: Column(children: [
          // This Slider DOES change when hiding/unhiding
          // This Slider SHOULDN'T change
          CustomWidget(),
        ]),
      ));
}

// When pressing the button the "child" gets rendered / not rendered
class ContentHider extends StatefulWidget {
  final Widget child;
  ContentHider({this.child});
  @override
  _ContentHiderState createState() => _ContentHiderState();
}

class _ContentHiderState extends State<ContentHider> {
  bool hidden = false;
  @override
  Widget build(BuildContext context) => Center(
        child: Column(
          children: [
            RaisedButton(
              onPressed: () {
                if (hidden == false)
                  setState(() => hidden = true);
                else
                  setState(() => hidden = false);
              },
              child: Text("Hide/Unhide"),
            ),
            Visibility(
                visible: hidden, maintainState: true, child: widget.child)
          ],
        ),
      );
}

// StatfulWidget which contains changable content -> Slider
class CustomWidget extends StatefulWidget {
  CustomWidget({Key key}) : super(key: key);
  @override
  _CustomWidgetState createState() => _CustomWidgetState();
}

class _CustomWidgetState extends State<CustomWidget> {
  double _currentSliderValue = 0.0;
  @override
  Widget build(BuildContext context) {
    return Card(
      color: Color(0xFFACFFBC),
      child: Slider(
        value: _currentSliderValue,
        min: 0,
        max: 100,
        label: _currentSliderValue.round().toString(),
        onChanged: (double value) {
          setState(() {
            _currentSliderValue = value;
          });
        },
      ),
    );
  }
}
Share:
923
UnknownScript
Author by

UnknownScript

Updated on December 26, 2022

Comments

  • UnknownScript
    UnknownScript over 1 year

    Situation: In my app I have input cards (with like sliders) which are displayed/hidden by clicking a button.

    Problem: When I edit the value of a slider, then hide and unhide again, the value is lost.

    I tried using a UniqueKey() but it doesn't solve the problem.

    A workaround which doesn't work for me is storing the value elsewhere and the passing it as an initial value to the slider. This is because there are more variables in the state than just the slider value I have to preserve.

    Run on Dartpad

    import 'package:flutter/material.dart';
    
    final _someKey = UniqueKey();
    
    void main() => runApp(MyApp());
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) => MaterialApp(home: MyHomePage());
    }
    class MyHomePage extends StatefulWidget {
      MyHomePage();
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) => Scaffold(
          appBar: AppBar(title: Text("Problem: UniqueKey not working")),
          body: ContentHider(
            child: Column(children: [
              // This Slider DOES change when hiding/unhiding
              // This Slider SHOULDN'T change
              CustomWidget(
                key: _someKey,
              ),
            ]),
          ));
    }
    
    // When pressing the button the "child" gets rendered / not rendered
    class ContentHider extends StatefulWidget {
      final Widget child;
      ContentHider({this.child});
      @override
      _ContentHiderState createState() => _ContentHiderState();
    }
    
    class _ContentHiderState extends State<ContentHider> {
      bool hidden = false;
      @override
      Widget build(BuildContext context) => Center(
            child: Column(
              children: [
                RaisedButton(
                  onPressed: () {
                    if (hidden == false)
                      setState(() => hidden = true);
                    else
                      setState(() => hidden = false);
                  },
                  child: Text("Hide/Unhide"),
                ),
                hidden ? Container() : widget.child
              ],
            ),
          );
    }
    
    // StatfulWidget which contains changable content -> Slider
    class CustomWidget extends StatefulWidget {
      CustomWidget({Key key}) : super(key: key);
      @override
      _CustomWidgetState createState() => _CustomWidgetState();
    }
    
    class _CustomWidgetState extends State<CustomWidget> {
      double _currentSliderValue = 0.0;
      @override
      Widget build(BuildContext context) {
        return Card(
          color: Color(0xFFACFFBC),
          child: Slider(
          value: _currentSliderValue,
          min: 0,
          max: 100,
          label: _currentSliderValue.round().toString(),
          onChanged: (double value) {
            setState(() {
              _currentSliderValue = value;
            });
          },
          ),
        );
      }
    }
    
  • UnknownScript
    UnknownScript over 3 years
    I use controllers for my sliders, but these controllers loose theirs state. [Unhidden/Hidden Widget] -> [Stateful Slider Controller] -> [Stateless Slider Wrapper] -> [Slider]
  • Randal Schwartz
    Randal Schwartz over 3 years
    I was using controller in the generic sense, as part of the Model/View/Controller triad.
  • UnknownScript
    UnknownScript over 3 years
    Okay, I understand