[英]How to update the state of Parent Widget from its Child Widget while also updating the Child's state in Flutter?
[英]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.