【问题标题】:Stateful widget in listview and reloading leads to error列表视图中的有状态小部件和重新加载导致错误
【发布时间】:2019-11-18 13:51:31
【问题描述】:

当我使用一组无状态小部件创建 ListView 时,我没有收到任何错误,一切正常。但是,当我将有状态的小部件放入数组并通过滚动它来重新呈现小部件时,它会在视图之外结束,然后我会收到错误。

我在 ListView 和 ListView.builder 上试过了,好像没有什么区别。我认为问题出在文本上,所以我尝试删除所有 Text 构造函数,但没有区别。小部件会初始化和释放,但不会重新初始化。

class CustomSettingsScreen extends StatefulWidget {
    static const String routeName = "/settings";

    @override
    CustomSettingsScreenState createState() =>CustomSettingsScreenState();
}

class CustomSettingsScreenState extends State<CustomSettingsScreen> {
    @override
    void initState() {
        super.initState();
    }

final listElements = {
    Divider(height: 500),
    VibrateStartRecordingEnablePreference(
        preferenceKey: PreferenceKeys.keyVibrateStartRecordingEnable,
    ),
    Divider(height: 500),
    Divider(height: 500),
    Divider(height: 500),
    Divider(height: 500),
    };

};

@override
Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
            title: new Text("Settings"),
        ),
        body: ListView.builder(
        itemCount: listElements.length,
        itemBuilder: (BuildContext context, int index) {
            return listElements.elementAt(index);
        },
    ),);
    }
}

class VibrateStartRecordingEnablePreference extends StatefulWidget {
    VibrateStartRecordingEnablePreference({
    Key key,
    @required this.preferenceKey,
});

final preferenceKey;

final _VibrateStartRecordingEnablePreferenceState
  _vibrateStartRecordingEnablePreferenceState =
  new _VibrateStartRecordingEnablePreferenceState();

@override
_VibrateStartRecordingEnablePreferenceState createState() =>
    _vibrateStartRecordingEnablePreferenceState;
}

class _VibrateStartRecordingEnablePreferenceState extends State<VibrateStartRecordingEnablePreference> {
    String _title = "Vibrate at the start of recording";
    String _subtitleOn = "Vibrates once upon start of recording";
    String _subtitleOff = "Will not vibrate at start";
    bool _value = true;

String _getSubtitle() {
    if (_value)
        return _subtitleOn;
    else
      return _subtitleOff;
}

void _updateValue(bool value) {
    Settings().save(widget.preferenceKey, value);
    setState(() {
      _value = value;
    });
}

@override
void initState() {
    //Update the values and text
    Settings().getBool(widget.preferenceKey, true).then((value) {
        setState(() {
        _value = value;
      });
    });

    super.initState();
}

@override
Widget build(BuildContext context) {
    return new SwitchListTile(
        secondary: const Icon(Icons.vibration),
        title: Text("$_title"),
        subtitle: Text(_getSubtitle()),
        value: _value,
        onChanged: (value) {
            _updateValue(value);
        },
    );
    }
}

调试器中的错误

The following assertion was thrown building         NotificationListener<KeepAliveNotification>:     'package:flutter/src/widgets/framework.dart': Failed assertion: line 4006 pos         
12: '_state._widget == null': is not true.

这是正在发生的事情的视频。 https://webm.red/6ZTH

【问题讨论】:

    标签: android listview flutter statefulwidget


    【解决方案1】:

    _vibrateStartRecordingEnablePreferenceState 存储在 VibrateStartRecordingEnablePreference 中,这是错误的。 框架尝试将状态实例附加到小部件,但状态已经附加到一个。所以会导致异常。

    每次在 StatefulWidget 中调用 createState() 时,都必须返回一个新的 state 实例

    【讨论】:

    • 如果我无法通过有状态小部件访问状态的成员函数,我将如何调用它们?
    • 好吧,你可以为final key = GlobalKey&lt;_VibrateStartRecordingEnablePreferenceState&gt;(); 这样的状态创建一个全局键,将它存储在小部件树的更高位置,然后将它传递给小部件构造函数。不要忘记通过调用: super(key: key) 来更新VibrateStartRecordingEnablePreference 的构造函数。然后您将能够访问key.currentState.someMethod()。但是我会质疑这样的设计。真正需要在状态之外调用状态方法的情况并不多。
    猜你喜欢
    • 2019-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-09
    • 1970-01-01
    • 2023-03-20
    相关资源
    最近更新 更多