Flutter:AutomaticKeepAliveClientMixin无法与BottomNavigationBar一起使用

时间:2018-10-26 15:13:10

标签: dart flutter

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

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

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

因此,我正在实现AutomaticKeepAliveClientMixin,因此即使更改了选项卡,状态也会保持不变。但这似乎不起作用:

Working Gif via GIPHY

这是我的代码:

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;
          });
        },
      ),
    );
  }
}

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;
}

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

4 个答案:

答案 0 :(得分:9)

如果您希望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);
        },
      ),
    );
  }
}

但是如果您只是想保持页面状态,建议您使用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;
          });
        },
      ),
    );
  }
}


答案 1 :(得分:5)

摘自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).

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

  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(...);
  }

答案 2 :(得分:2)

因此我将选项卡页面包装在Stack小部件中,现在它使表单状态保持不变。我不确定这是否是正确的方法,但确实可以。

代码如下:

AddPatientView (父级)

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;
          });
        },
      ),
    );
  }
}

答案 3 :(得分:0)

AutomaticKeepAliveMixin 通常只用于 pageview/tabview/另一个正在重建的视图,它保留了一个状态,显然不会重建 我们需要控制器