Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter - setState not updating the child widget variable passed as parameter

Tags:

flutter

dart

I'm new to flutter and trying to use two Stateful Widgets first one calling the second in build() method and I just want to update the child widget variable that is passed from parent in the constructor.

Here is the code I'm trying with.

Parent Widget

class Parent extends StatefulWidget {
  Parent({Key? key}) : super(key: key);

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

class _ParentState extends State<Parent> {
  List appointments = [];

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

  @override
  Widget build(BuildContext context) {
    return Builder(builder: (BuildContext context) {
      return RefreshIndicator(
          onRefresh: () async {
            _pullRefresh();
          },
          child: ListView(
            children: [
              AppointmentsWidget(appointments: appointments)     // <----- I passed the variable in constructor and this one is updating in setState and I want it to update in the child widget too
            ],
          ),
        ),
      );
    });
  }

  _pullRefresh() {
    fetchAppointments();
  }

  fetchAppointments() {
    setState(() {
      // Stuff to do
      appointments = ......
      ......
      ......
    });
  }
}

Child Widget

class AppointmentsWidget extends StatefulWidget {
  var appointments;
  AppointmentsWidget({this.appointments});

  @override
  _AppointmentsWidgetState createState() =>
      _AppointmentsWidgetState(appointments: appointments);  // <--- Constructor 1
}

class _AppointmentsWidgetState extends State<AppointmentsWidget> {
  var appointments;
  
  _AppointmentsWidgetState({this.appointments});  // <--- Constructor 2

  @override
  Widget build(BuildContext context) {
    
    return ListView.builder(
      physics: const NeverScrollableScrollPhysics(),
      shrinkWrap: true,
      itemCount: appointments.length,
      itemBuilder: (BuildContext context, int index) {
        return Text(appointments[index].toString());     // <--- This is where I use it
      },
    );
  }
}

I know the constructor calls once but I couldn't find a way to either recall the constructor OR somehow pass the updated value to the constructor.

Your help is really appreciated.

like image 232
Muaaz Khalid Avatar asked Sep 12 '25 23:09

Muaaz Khalid


1 Answers

You should make your child widget stateless, as its state (the appointments) are handled by the parent. What happens currently is that your child widget is constructed, where the empty list is used as its widget.appointments value. Then when the appointments have been fetched, the widget.appointments rebuilds, but since the state of the child is maintained, this value is not passed on (initState of the child is not re-run).

class AppointmentsWidget extends StatelessWidget {
  final List appointments;

  const AppointmentsWidget({Key? key, required this.appointments}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      physics: const NeverScrollableScrollPhysics(),
      shrinkWrap: true,
      itemCount: appointments.length,
      itemBuilder: (BuildContext context, int index) {
        return Text(appointments[index].toString());     // <--- This is where I use it
      },
    );
  }
}

Also, take a look at the flutter docs on handling state: https://docs.flutter.dev/development/data-and-backend/state-mgmt/intro

These also state that it's good to keep your state high up (in the parent in this case), and make child widgets use the state of the parents to render themselves appropriately.

Rectification

As you mention in the comments, you need the child widget to be stateful (for maintaining state on some other data). In that case, you can simply get rid of the appointments state variable and use widget.appointments instead, which will update when the parent rebuilds with a new value.

class AppointmentsWidget extends StatefulWidget {
  var appointments;
  AppointmentsWidget({this.appointments});

  @override
  _AppointmentsWidgetState createState() =>
      _AppointmentsWidgetState();  // <--- Constructor 1
}

class _AppointmentsWidgetState extends State<AppointmentsWidget> {

  _AppointmentsWidgetState();  // <--- Constructor 2

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      physics: const NeverScrollableScrollPhysics(),
      shrinkWrap: true,
      itemCount: widget.appointments.length,
      itemBuilder: (BuildContext context, int index) {
        return Text(widget.appointments[index].toString());     // <--- This is where I use it
      },
    );
  }
}
like image 79
fravolt Avatar answered Sep 14 '25 13:09

fravolt