Как получить доступ ко всему состоянию ребенка из родительского виджета во флаттере? - PullRequest
0 голосов
/ 23 апреля 2020

У меня есть родительский виджет с именем createRoutineScreen, и у него есть 7 похожих дочерних виджетов с именем RoutineFormCard. RoutineFormCard - это форма, которая имеет состояние _isPostSuccesful логического типа, чтобы указать, сохранена ли форма в базе данных или нет. Теперь мне нужно перейти на другой экран из createRoutine, только когда все его 7 детей имеют значение _isPostSuccesful true. Как я могу получить доступ ко всему состоянию детей из виджета createRoutineScreen?

Мой код:

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

Как видите, я пробовал callBack от родительского виджета, который увеличивается или уменьшить количество форм, сохраненных для каждого дочернего виджета. Это делает работу, но, когда одна форма сохраняется, родительское состояние изменяется, и все другие дети получают восстановление, что не является необходимым в моем мнении. Какой лучший способ сделать это?

1 Ответ

1 голос
/ 23 апреля 2020

Попробуйте использовать 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

Я делюсь быстрой идеей решения вашей проблемы, я не проверял ее, но я готов улучшить ответ, если это необходимо

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...