flutterflutter-layoutflutter-stateflutter-androidflutter-design

Why parent widget setState disables child widget setState in flutter?


I try to create a date picker for my flutter app. At first i can choose the day i want and the color of selected day will change. The problem is when i change the month or year in drop down button, I can't change selected day. Actually all things works and the selected day will be returned, But it's color won't change. I'm new to flutter This is my code.

import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_datepicker/calander_body/day_box.dart';

class CalanderDays extends StatefulWidget {
  final onSelectDays;
  CalanderDays({required this.onSelectDays});
  @override
  State<StatefulWidget> createState() => CalanderDaysState();
}

class CalanderDaysState extends State<CalanderDays> {
  int previousIndex = 0;
  int? monthDrop = 0;
  int? yearDropDowninit = 1940;
  List monthNames = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'
  ];
  String? year, month, day;

  String? fullDate;

  @override
  Widget build(BuildContext context) {
    List<DropdownMenuItem<int>> monthDropDown = List.generate(12, (index) {
      return DropdownMenuItem(
        child: Text(monthNames[index]),
        value: index,
      );
    });
    List<DropdownMenuItem<int>> yearItems =
        List.generate(DateTime.now().year - 1950, (index) {
      return DropdownMenuItem(
        child: Text('${1950 + index}'),
        value: 1950 + index,
      );
    });
    List<DayBox>? days = List.generate(30, (index) {
      return DayBox(
        day: index,
      );
    });

    return Directionality(
        textDirection: TextDirection.rtl,
        child: Flexible(
            flex: 1,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: [
                Expanded(
                    child: Row(
                  textDirection: TextDirection.rtl,
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    DropdownButton<int>(
                        iconEnabledColor: Colors.black,
                        value: monthDrop,
                        items: monthDropDown,
                        onChanged: (itemValue) {
                          month = itemValue.toString();
                          setState(() {
                            monthDrop = itemValue;
                          });
                        }),
                    DropdownButton<int>(
                        value: yearDropDowninit,
                        items: yearItems,
                        onChanged: (value) {
                          year = value.toString();
                          setState(() {
                            yearDropDowninit = value;
                          });
                        })
                  ],
                )),
                Flexible(
                    flex: 5,
                    child: GridView.builder(
                        itemCount: 31,
                        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                            crossAxisCount: 7),
                        itemBuilder: (context, i) {
                          return InkWell(
                            onTap: () {
                              int selectedDay = i + 1;
                              day = selectedDay.toString().length == 1
                                  ? day = '0$selectedDay'
                                  : selectedDay.toString();
                              print(day);
                              if (i == previousIndex) {
                                print('previous');
                              } else if (i != previousIndex) {
                                days[i].activate();
                                days[previousIndex].deactivate();
                                previousIndex = i;
                                if (previousIndex == -1) {
                                  print('-1');
                                  return;
                                }
                              }
                            },
                            child: days[i],
                          );
                        }))
              ],
            )));
  }
}

The dayBox widget:

import 'package:flutter/material.dart';

class DayBox extends StatefulWidget {
  final day;

  VoidCallback activate = () {};
  VoidCallback deactivate = () {};
  DayBox({Key? key, required this.day}) : super(key: key);
  @override
  State<StatefulWidget> createState() => DayBoxState();
}

class DayBoxState extends State<DayBox> {
  Color boxColor = Colors.transparent;
  Color textColor = Colors.black;

  @override
  void initState() {
    super.initState();

    widget.activate = active;
    widget.deactivate = deactivate;
  }

  active() {
    Future.delayed(Duration.zero, () {
      setState(() {
        boxColor = Colors.red;
        textColor = Colors.white;
      });
    });
  }

  deactivate() {
    Future.delayed(Duration.zero, () {
      setState(() {
        boxColor = Colors.transparent;
        textColor = Colors.black;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      width: 30,
      height: 30,
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        color: boxColor,
      ),
      child: Text(
        '${widget.day + 1}',
        style: TextStyle(
            fontSize: 18,
            color: textColor,
            fontWeight: FontWeight.normal,
            decoration: TextDecoration.none),
      ),
    );
  }
}

I really don't know what to do.


Solution

  • Solution ^^ : Make DayBox extend a stateless Widget

    class CalanderDays extends StatefulWidget {
     // final onSelectDays;
       CalanderDays();
      @override
       State<StatefulWidget> createState() => CalanderDaysState();
    }
    
     class CalanderDaysState extends State<CalanderDays> {
     int previousIndex = 0;
     int? monthDrop = 0;
     int yearDropDowninit = 1940;
    
     int _day = 1;
    
      List monthNames = [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December'
      ];
     String? year, month, day;
    
     String? fullDate;
    
    @override
     Widget build(BuildContext context) {
    List<DropdownMenuItem<int>> monthDropDown = List.generate(12, (index) {
      return DropdownMenuItem(
        child: Text(monthNames[index]),
        value: index,
      );
    });
    
    
    List<DropdownMenuItem<int>> yearItems =[];
    
    
    for(int i= yearDropDowninit;i<=DateTime.now().year;i++){
      yearItems.add(DropdownMenuItem(
        child: Text('${i}'),
        value: i,
      ));
    }
    List.generate(DateTime.now().year - yearDropDowninit, (index) {
      return DropdownMenuItem(
        child: Text('${1950 + index}'),
        value: 1950 + index,
      );
    });
    
    
    return Directionality(
        textDirection: TextDirection.rtl,
        child: Flexible(
            flex: 1,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: [
                Expanded(
                    child: Row(
                      textDirection: TextDirection.rtl,
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: [
                        DropdownButton<int>(
                            iconEnabledColor: Colors.black,
                            value: monthDrop,
                            items: monthDropDown,
                            onChanged: (itemValue) {
                              month = itemValue.toString();
                              setState(() {
                                monthDrop = itemValue;
                              });
                            }),
                        DropdownButton<int>(
                            value: yearDropDowninit,
                            items: yearItems,
                            onChanged: (value) {
                              year = value.toString();
                              setState(() {
                                yearDropDowninit = value!;
                              });
                            })
                      ],
                    )),
                Flexible(
                    flex: 5,
                    child: GridView.builder(
                        itemCount: days.length,
                        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                            crossAxisCount: 7),
                        itemBuilder: (context, index) {
                          return InkWell(
                            onTap: () {
                                setState(() {
                                  _day =index;
                                });
                            },
                            child:  BoxDay(
                              day: index, activated: _day == index ,
                            ) ,
                          );
                        }))
              ],
            )));
    }
    }
    
    
    
    
      class BoxDay extends StatelessWidget {
        final day;
        final bool activated;
      const BoxDay({Key? key, required this.activate, this.day}) : super(key: key);
    
     @override
       Widget build(BuildContext context) {
      return Container(
      alignment: Alignment.center,
      width: 30,
      height: 30,
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        color: activated ? Colors.red : Colors.transparent,
      ),
      child: Text(
        '${day + 1}',
        style: TextStyle(
            fontSize: 18,
            color: activated ? Colors.white : Colors.black,
            fontWeight: FontWeight.normal,
            decoration: TextDecoration.none),
      ),
    );
     }
     }
    

    There was an error,

     List<DropdownMenuItem<int>> yearItems =
        List.generate(DateTime.now().year - 1950, (index) {
      return DropdownMenuItem(
        child: Text('${1950 + index}'),
        value: 1950 + index,
      );
      });
    

    to

    List<DropdownMenuItem<int>> yearItems =[];
    
    
    for(int i= yearDropDowninit;i<=DateTime.now().year;i++){
       yearItems.add(DropdownMenuItem(
        child: Text('${i}'),
       value: i,
     ));
     }