[英]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.