0

I am trying to call setState inside a ListView.builder which itself is inside a FutureBuilder

FutureBuilder<List<dynamic>>(
      future: BidRepository().bidsTrucker('new'),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return const Center(child: CircularProgressIndicator());
        }
        final trips = snapshot.data!;
        return ListView.builder(
          itemCount: trips.length,
          itemBuilder: ((context, index) {
            final trip = trips[index];
            return AcceptBidCard(
              fromLocation:
                  '${trip['from_address']['name']}, ${trip['from_address']['country']}',
              toLocation:
                  '${trip['to_address']['name']}, ${trip['to_address']['country']}',
              bidderName: trip['customer_name'],
              date: DateFormat('dd-MM-yyyy')
                  .format(DateTime.parse(trip['trip_date'])),
              tripId: trip['trip_id'],
              onPressed: (pressed) => setState(()=> 'this is the place where it's not working'),
            );
          }),
        );
      },
    )

onPressed is typedef ValueChanged<in T> = void Function(T value). so I was trying to update list on button pressed.

this is the error message: The instance member 'setState' can't be accessed in an initializer. Try replacing the reference to the instance member with a different expression

2 Answers2

0

It looks like you want to call setState inside the onPressed callback of a button in your ListView.builder widget that's inside a FutureBuilder. The problem is that the setState method requires access to the State object of the widget, and you're trying to call it from within the builder function. To achieve this, you need to move the FutureBuilder to a stateful widget and then use setState within that widget.

Here's an example of how you can structure your Flutter code to achieve this

import 'package:flutter/material.dart';

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  Future<List<dynamic>> _fetchData() async {
    // Fetch your data here and return a List<dynamic>
    // Example: return BidRepository().bidsTrucker('new');
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<dynamic>>(
      future: _fetchData(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Center(child: CircularProgressIndicator());
        } else if (snapshot.hasError) {
          return const Center(child: Text('Error loading data'));
        } else if (!snapshot.hasData || snapshot.data.isEmpty) {
          return const Center(child: Text('No data available'));
        }

        final trips = snapshot.data!;

        return ListView.builder(
          itemCount: trips.length,
          itemBuilder: (context, index) {
            final trip = trips[index];
            return AcceptBidCard(
              // Your AcceptBidCard properties here
              onPressed: () {
                // Use setState to update the widget's state
                setState(() {
                  // Perform your update logic here
                  // For example, remove the item from the list:
                  trips.removeAt(index);
                });
              },
            );
          },
        );
      },
    );
  }
}

In this code:

MyWidget is a stateful widget that contains the FutureBuilder and the ListView.builder. _fetchData is a method where you should fetch your data asynchronously. Replace the example code with your actual data fetching logic. Inside the itemBuilder of the ListView.builder, you can call setState to update the widget's state when the button is pressed. In this example, it removes the item from the list, but you can perform your specific update logic here. Remember to replace the data fetching logic and other widget properties as per your application's requirements.

If both the parent (the widget containing the FutureBuilder) and the child (the AcceptBidCard widget) are already stateful widgets, you can propagate the callback from the parent widget to the child widget without needing to use setState directly in the child widget. Here's how you can do that:

  1. In your parent widget, define a callback function that you want to execute when the "Accept" button is pressed.

  2. Pass this callback function as a parameter to your child widget.

  3. In the child widget, call this callback function when the "Accept" button is pressed.

Here's an example of how to do this:

In your parent widget (the one containing the FutureBuilder):

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  // Define a callback function to handle bid acceptance
  void handleAcceptBid(String tripId) {
    // Perform any necessary logic and updates here
    setState(() {
      // Update your list or any state variables here
      // You can access 'tripId' to identify which bid was pressed
    });
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<dynamic>>(
      future: BidRepository().bidsTrucker('new'),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return const Center(child: CircularProgressIndicator());
        }
        final trips = snapshot.data!;
        return ListView.builder(
          itemCount: trips.length,
          itemBuilder: ((context, index) {
            final trip = trips[index];
            return AcceptBidCard(
              fromLocation:
                  '${trip['from_address']['name']}, ${trip['from_address']['country']}',
              toLocation:
                  '${trip['to_address']['name']}, ${trip['to_address']['country']}',
              bidderName: trip['customer_name'],
              date: DateFormat('dd-MM-yyyy')
                  .format(DateTime.parse(trip['trip_date'])),
              tripId: trip['trip_id'],
              onPressed: () {
                // Call the callback function from the parent
                handleAcceptBid(trip['trip_id']);
              },
            );
          }),
        );
      },
    );
  }
}

In the child widget (AcceptBidCard), you can use the onPressed callback to trigger the handleAcceptBid callback passed down from the parent widget when the "Accept" button is pressed. This way, you're handling state updates in the parent widget's stateful context.

0

From the error The instance member 'setState' can't be accessed in an initializer. seems like you need to pass your methods to the initState. You should init all your state properties in initState. if you are using FutureBuilder you can do something like this:

Future<void> _future;

  @override
  void initState() {
    super.initState();
    _future = Future<void>.value();
  }

then call it in the future: _future,

please refer to this answer

Clinton Njiru
  • 41
  • 1
  • 7