Flutter:AutomaticKeepAliveClientMixin无法与BottomNavigationBar一起使用

cod*_*ess 12 dart flutter

我有一个名为AddPatientView的页面,其中的BottomNavigationBar包含AddPatientInfo和AddPatientImages页面。这三个都是有状态的小部件。

默认情况下,AddPatientInfo打开,其中包含一堆TextField(用于输入患者信息),用户可以在AddPatientImages页面中添加图像。

问题是如果我在AddPatientInfo上填充TextField,然后转到AddPatientImages,然后返回,则所有TextField都是空的。正确的做法是,由于整个小部件树都得到了重建,因此我丢失了所有填充的数据。

因此,我正在实施,AutomaticKeepAliveClientMixin以便即使更改选项卡也可以保持状态。但这似乎不起作用:

通过GIPHY工作Gif

这是我的代码:

AddPatientView(父级)

class AddPatientView extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AddPatientViewState();
  }
}

class _AddPatientViewState extends State<AddPatientView> {
  int _currentIndex = 0;
  List<Widget> _children;
  List<File> _imageFileList = new List<File>();

  @override
  void initState() {
    super.initState();
    _children = [
      AddPatientInfo(savePatient),
      AddPatientImages(_imageFileList)
    ];
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("New Patient Record"),
      ),
      body: _children[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: [
          BottomNavigationBarItem(icon: new Icon(Icons.create), title: new Text('Info')),
          BottomNavigationBarItem(icon: new Icon(Icons.camera_alt), title: new Text('Images')),
        ],
        onTap: (int index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

AddPatientInfo

class AddPatientInfo extends StatefulWidget {
  final Function savePatient;

  AddPatientInfo(this.savePatient){
  }

  @override
  State<StatefulWidget> createState() {
    return _AddPatientInfoState();
  }
}

class _AddPatientInfoState extends State<AddPatientInfo> with AutomaticKeepAliveClientMixin<AddPatientInfo> {

  Function _savePatient;
  String _firstName, _lastName, _gender, _phone, _email, _diabetesMeds, _hypertensionMeds, _others;
  int _age, _diabetesYears, _hypertensionYears, _smokesPerDay, _smokerYears;
  bool _diabetes = false, _hypertension = false, _smoker = false, _chestPain = false,
      _cva = false, _ckd = false, _breathlessness = false,
      _syncope = false, _sweating = false, _sweatingFeet = false;

  List<String> _genderList = new List<String>();
  List<String> _yesNoList = new List<String>();
  List<File> _imageFileList = new List<File>();

  @override
  void initState() {
    super.initState();
    _savePatient = widget.savePatient;
    _genderList.addAll(['Male', 'Female', 'Other']);
    _yesNoList.addAll(['Yes', 'No']);
    _gender = _genderList.elementAt(0);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomPadding: false,
      body: Container(
        margin: EdgeInsets.all(10.0),
        child: Form(
          child: new ListView(
            children: <Widget>[
              TextField(
                decoration: InputDecoration(
                    labelText: 'Patient First Name',
                    labelStyle: TextStyle(
                        color: Colors.black
                    ),
                    hintText: 'Enter patients first name'
                ),
                onChanged: (String value) {
                  setState(() {
                    _firstName = value;
                  });
                },
              ),
              TextField(
                decoration: InputDecoration(
                    labelText: 'Patient Last Name',
                    labelStyle: TextStyle(
                        color: Colors.black
                    ),
                    hintText: 'Enter patients last name'
                ),
                onChanged: (String value) {
                  setState(() {
                    _lastName = value;
                  });
                },
              ),
              //other textfield widgets below
            ],
          ),
        )
      ),
    );
  }

  @override
  bool get wantKeepAlive => true;
}
Run Code Online (Sandbox Code Playgroud)

我在这里想念什么?有没有更优雅的方式来维护表单中的数据?

小智 15

如果希望AutomaticKeepAliveClientMixin继续工作,请使用PageView包裹正文,代码应如下所示

class AddPatientView extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AddPatientViewState();
  }
}

class _AddPatientViewState extends State<AddPatientView> {
  int _currentIndex = 0;
  List<Widget> _children;
  List<File> _imageFileList = new List<File>();

  // add
  final pageController = PageController();
  void onPageChanged(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  void initState() {
    super.initState();
    _children = [AddPatientInfo(savePatient), AddPatientImages(_imageFileList)];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("New Patient Record"),
      ),
      // here!!!
      body: PageView(
        children: _children,
        controller: pageController,
        onPageChanged: onPageChanged,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: [
          BottomNavigationBarItem(
              icon: new Icon(Icons.create), title: new Text('Info')),
          BottomNavigationBarItem(
              icon: new Icon(Icons.camera_alt), title: new Text('Images')),
        ],
        onTap: (int index) {
          // setState(() {
          //   _currentIndex = index;
          // });
          // update
          pageController.jumpToPage(index);
        },
      ),
    );
  }
}

Run Code Online (Sandbox Code Playgroud)

但是如果您只是想保持页面状态,建议您使用IndexedStack,这很简单?不需要AutomaticKeepAliveClientMixin,代码应该像这样

class AddPatientView extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AddPatientViewState();
  }
}

class _AddPatientViewState extends State<AddPatientView> {
  int _currentIndex = 0;
  List<Widget> _children;
  List<File> _imageFileList = new List<File>();

  @override
  void initState() {
    super.initState();
    _children = [
      AddPatientInfo(savePatient),
      AddPatientImages(_imageFileList)
    ];
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("New Patient Record"),
      ),
      body: IndexedStack(
        index:_currentIndex,
        children:_children
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: [
          BottomNavigationBarItem(icon: new Icon(Icons.create), title: new Text('Info')),
          BottomNavigationBarItem(icon: new Icon(Icons.camera_alt), title: new Text('Images')),
        ],
        onTap: (int index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
    );
  }
}


Run Code Online (Sandbox Code Playgroud)

  • 如果有人在更改路由/导航器推送时使用 PageView 和 AutomaticKeepAliveClientMixin 遇到奇怪的行为,您还应该考虑使用 super.build(context) 添加 @Jonah Williams 解决方案。 (2认同)

Jon*_*ams 8

AutomaticKeepAliveClientMixin的文档中:

/// A mixin with convenience methods for clients of [AutomaticKeepAlive]. Used
/// with [State] subclasses.
///
/// Subclasses must implement [wantKeepAlive], and their [build] methods must
/// call `super.build` (the return value will always return null, and should be
/// ignored).
Run Code Online (Sandbox Code Playgroud)

因此,在您的示例中,在返回脚手架之前,只需调用super.build:

  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(...);
  }
Run Code Online (Sandbox Code Playgroud)

  • 添加了super.build(context); 之前脚手架,但它不起作用:( (8认同)
  • 我已经在使用 PageView 和 AutomaticKeepAliveClientMixin。但是我仍然有问题,当我使用 navigator.push 导航到新页面并再次弹出时,我的小部件仍然再次重新加载。添加您的解决方案后,现在它按预期工作。谢谢你。:) (2认同)
  • 我无法让它工作...我在 CustomScrollView 和 SliverList 中有一个 WebView,它不断刷新/重建...我不能再问问题了,有人可以帮忙吗?! (2认同)

cod*_*ess 5

因此,我将选项卡页包装在 Stack 小部件中,现在它保持了表单的状态完整。我不确定这是否是正确的方法,但它确实有效。

这是代码:

添加PatientView(父级)

class AddPatientView extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AddPatientViewState();
  }
}

class _AddPatientViewState extends State<AddPatientView> {
  int _currentIndex = 0;
  List<File> _imageFileList = new List<File>();

  @override
  void initState() {
    super.initState();
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("New Patient Record"),
      ),
      body: new Stack(
        children: <Widget>[
          new Offstage(
            offstage: _currentIndex != 0,
            child: AddPatientInfo(savePatient),
          ),
          new Offstage(
            offstage: _currentIndex != 1,
            child: AddPatientImages(_imageFileList),
          )
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: [
          BottomNavigationBarItem(icon: new Icon(Icons.create), title: new Text('Info')),
          BottomNavigationBarItem(icon: new Icon(Icons.camera_alt), title: new Text('Images')),
        ],
        onTap: (int index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)