繁体   English   中英

如何从 flutter 中的父小部件访问所有孩子的 state?

[英]How to access all of child's state from Parent Widget in flutter?

我有一个名为 createRoutineScreen 的父小部件,它有 7 个类似的名为 RoutineFormCard 的子小部件。 RoutineFormCard 是一个表单,它有一个 state _isPostSuccesful 类型为 boolean 来判断表单是否保存到数据库。 现在,只有当所有 7 个孩子的 _isPostSuccesful 为 true 时,我才必须从 createRoutine 移至另一个屏幕。 如何从 createRoutineScreen 小部件访问所有儿童的 state?

我的代码是:

 class CreateRoutineScreen extends StatefulWidget {


  final String userID;

  CreateRoutineScreen({this.userID});
  //TITLE TEXT
  final Text titleSection = Text(
      'Create a Routine',
      style: TextStyle(
        color: Colors.white,
        fontSize: 25,
      )
  );

  final List<Map> weekDays = [
    {"name":"Sunday", "value":1},
    {"name":"Monday", "value":2},
    {"name":"Tuesday", "value":3},
    {"name":"Wednesday", "value":4},
    {"name":"Thursday", "value":5},
    {"name":"Friday", "value":6},
    {"name":"Saturday", "value":7},
  ];

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



class _CreateRoutineScreenState extends State<CreateRoutineScreen> {


  Routine routine;
  Future<List<dynamic>> _exercises;
  dynamic selectedDay;
  int _noOfRoutineSaved;
  List _keys = [];


   Future<List<dynamic>>_loadExercisesData()async{
    String url = BASE_URL+ "exercises";
    var res = await http.get(url);
    var exercisesList = Exercises.listFromJSON(res.body);
    //var value = await Future.delayed(Duration(seconds: 5));
    return exercisesList;
  }


  @override
  void initState(){
    super.initState();
    _exercises = _loadExercisesData();
    _noOfRoutineSaved = 0;
    for (int i = 0; i< 7; i++){
      _keys.add(UniqueKey());
    }
  }

  void _changeNoOfRoutineSaved(int a){
    setState(() {
      _noOfRoutineSaved= _noOfRoutineSaved + a;
    });
  }



  @override
  Widget build(BuildContext context) {
    print(_noOfRoutineSaved);
    return Scaffold(
        appBar: AppBar(
          title:Text("Create a Routine"),
          centerTitle: true,
          actions: <Widget>[
            FlatButton(
              child: Text("Done"),
              onPressed: (){
              },
            ),
          ],
        ),
        body: Container(
          color: Theme.of(context).primaryColor,
          padding: EdgeInsets.only(top:5.0,left: 10,right: 10,bottom: 10),
          child: FutureBuilder(
            future: _exercises,
            builder: (context, snapshot){
              if(snapshot.hasData){
                return ListView.builder(
                  itemCount: widget.weekDays.length,
                  itemBuilder: (context,index){
                    return RoutineFormCard(
                      weekDay: widget.weekDays[index]["name"],
                      exerciseList: snapshot.data,
                      userID : widget.userID,
                      changeNoOfRoutineSaved:_changeNoOfRoutineSaved,
                      key:_keys[index]
                    );
                  },
                );
              }
            else if(snapshot.hasError){
              return SnackBar(
              content: Text(snapshot.error),
              );
            }
            else{
              return Center(
                child: CircularProgressIndicator(
                  backgroundColor: Colors.grey,
                )
              );
            }
          }, 
        )
      ),
    );
  }
}

我的孩子小部件是:

class RoutineFormCard extends StatefulWidget {

  final Function createRoutineState;
  final String weekDay;
  final List<dynamic> exerciseList;
  final String userID;
  final Function changeNoOfRoutineSaved;

  RoutineFormCard({this.createRoutineState, 
    this.weekDay, this.exerciseList, this.changeNoOfRoutineSaved,
    this.userID, Key key}):super(key:key);

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

class _RoutineFormCardState extends State<RoutineFormCard> {

  bool _checkBoxValue= false;
  List<int> _selectedExercises;
  bool _inAsyncCall;
  bool   _successfulPost;

  @override
  void initState(){
    super.initState();
    _selectedExercises = [];
    _inAsyncCall = false;
    _successfulPost= false;


  }

  void onSaveClick()async{

    setState(() {
     _inAsyncCall = true; 
    });

    String url = BASE_URL + "users/routine";

    List selectedExercises = _selectedExercises.map((item){
      return widget.exerciseList[item].value;
    }).toList();

    String dataToSubmit = jsonEncode({
      "weekDay":widget.weekDay,
      "userID": widget.userID==null?"5e9eb190b355c742c887b88d":widget.userID,
      "exercises": selectedExercises
    });

    try{
      var res =await  http.post(url, body: dataToSubmit,
        headers: {"Content-Type":"application/json"});

        if(res.statusCode==200){
          print("Succesful ${res.body}");
          widget.changeNoOfRoutineSaved(1);
          setState(() {
           _inAsyncCall = false;
           _successfulPost = true; 
          });

        }
        else{
            print("Not succesful ${res.body}");
            setState(() {
             _inAsyncCall = false; 
            });
        }



    }catch(err){
        setState(() {
          _inAsyncCall = false; 
        });
        print(err);

    }

  }


  Widget saveAndEditButton(){
    if(_inAsyncCall){
      return CircularProgressIndicator();
    }
    else if(_successfulPost)
    {
      return IconButton(
        icon: Icon(Icons.edit, color: Colors.black,),
        onPressed: (){
          widget.changeNoOfRoutineSaved(-1);
          setState(() {
           _successfulPost = false; 
          });
        },
      );
    }
    else{
      return FlatButton(child: Text("Save"),
            onPressed: !_checkBoxValue&&_selectedExercises.length==0?null:onSaveClick,);
    }
  }

  //Card Header
  Widget cardHeader(){
    return  AppBar(
      title: Text(widget.weekDay, style: TextStyle(
        fontFamily: "Raleway",
        fontSize: 20,
        color: Colors.black,),
        ),
      actions: <Widget>[
        saveAndEditButton()    
      ],
      backgroundColor: Colors.lime[400],
    );
  }


  Widget cardBody(){
    return Column(
      children: <Widget>[
          Padding(
              padding: const EdgeInsets.all(8.0),
              child: Row(
                children: <Widget>[
                  Text("Rest Day"),
                  Checkbox(
                    value: _checkBoxValue,
                    onChanged: (value){
                      setState(() {
                        _checkBoxValue = value;
                      });
                    },
                  )
                ],
              ),
            ),

            _checkBoxValue?Container():
            SearchableDropdown.multiple(
            hint: "Select Exercise",
            style: TextStyle(color: Colors.black),
            items: widget.exerciseList.map<DropdownMenuItem>((item){
              return DropdownMenuItem(
                child: Text(item.name), value: item
              );
            }).toList(),
            selectedItems: _selectedExercises,
            onChanged: (values){
              setState(() {
              _selectedExercises = values;
              });
            },
            isExpanded: true,
            dialogBox: true,
          ),
      ],
    );
  }


  @override
  Widget build(BuildContext context) {
    print("<><><><><><><><><><><>${widget.weekDay} called");
    return Card(
      elevation: 8.0,
      child: Form(
        key: GlobalKey(),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
          cardHeader(),
          _successfulPost?Container():cardBody()
          ],
        ),
      ),
    );
  }
} 

如您所见,我已经尝试从父小部件回调,它增加或减少从每个子小部件保存的表单数量。 它可以完成工作,但是当保存一个表单时,父 state 会被修改,并且所有其他孩子都得到了重建,这在我看来是不必要的。 最好的方法是什么?

尝试对每个RoutineFormCard使用GlobalKey而不是UniqueKey 它将帮助您访问每个 RoutineFormCard 的RoutineFormCard 你可以这样做:

// 1. In the top of your CreateRoutineScreen file, add this line (make your RoutineFormCardState class public before)
final List<GlobalKey<RoutineFormCardState>> routineFormCardKeys = <GlobalKey<RoutineFormCardState>>[
  GlobalKey<RoutineFormCardState>(),
  GlobalKey<RoutineFormCardState>(),
  GlobalKey<RoutineFormCardState>(),
  GlobalKey<RoutineFormCardState>(),
  GlobalKey<RoutineFormCardState>(),
  GlobalKey<RoutineFormCardState>(),
  GlobalKey<RoutineFormCardState>(),
];

// 2. Then construct your RoutineFormCard using the right key
RoutineFormCard(
  weekDay: widget.weekDays[index]["name"],
  exerciseList: snapshot.data,
  userID : widget.userID,
  changeNoOfRoutineSaved:_changeNoOfRoutineSaved,
  key: routineFormCardKeys[index]
);

// 3. Now you can create a method in CreateRoutineScreen which will check the state of all RoutineFormCard
bool _allRoutineFormCardsCompleted() {
  bool result = true;
  for (int i = 0; i < 7; i++)
    result = result && routineFormCardKeys[i].currentState.isPostSuccessful;

  return result;
}


// 4. Finally use the result of the previous method where you want to move on another page

我正在分享一个解决您问题的快速想法,我尚未对其进行测试,但我已准备好在需要时改进答案

希望这会有所帮助!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM