1

I try to use FutureBuilder in Flutter to wait ulti my initState is finished then buil the UI for the app. But when the app is running, the screen keep rebuilding each time I press another button (the button does totally different thing).

Future loadUser() async {
    String jsonString = await storage.read(key: "jwt");
    final jsonResponse = json.decode(jsonString);
    loggedUser = new LoggedUser.fromJson(jsonResponse);
    print(loggedUser.token);
    getProfile();
    getJourneyByUserId()
        .then((receivedList){
      addRanges(receivedList);});
    }

Future<List<Journey>>getJourneyByUserId() async {
    var res = await http.get(
      Uri.parse("$baseUrl/journeys/userid=${loggedUser.user.userId}"),
      headers: {
        'Content_Type': 'application/json; charset=UTF-8',
        'Authorization': 'Bearer ${loggedUser.token}',
      },
    );
    if (res.statusCode == 200) {
      print("Get journeys successfully");
    }
    var data = jsonDecode(res.body);
    List idList = [];
    for (var i in data) {
      idList.add(i["journeyId"]);
    }
    for (var i in idList) {
      var res = await http.get(
        Uri.parse("$baseUrl/journeys/$i"),
      );
      var data = jsonDecode(res.body);
      Journey userJourney = new Journey.fromJson(data);
      setState(() {
        journeyList.add(userJourney);
      });
    }
    print("Journey ${journeyList.length}");
    return journeyList;
  }

addRanges(journeyList){
    setState(() {
      rangeList=[];
    });
      if (journeyList.isNotEmpty) {
        for (var i in journeyList) {
          DateTime startDate =
          DateTime(i.startDate.year, i.startDate.month, i.startDate.day);
          DateTime endDate =
          DateTime(i.endDate.year, i.endDate.month, i.endDate.day);
          setState(() {
            rangeList.add(PickerDateRange(startDate, endDate));
          });
        }
      }
      print("Range ${rangeList.length}");
      return rangeList;
  }

returnRange() {
    List<PickerDateRange> list = [];
    for(int i =0; i<rangeList.length;i++){
      list.add(rangeList[i]);
    }
    return list;
  }

Future functionForBuilder() async {
    return await returnRange();
  }

//initState function
  @override
  void initState() {
    super.initState();
    loadUser();
    functionForBuilder();
  }

//build the UI
Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("$_name's Profile",style: TextStyle(color: kColorPalette4),),
          centerTitle: true,
        ),
        body: Container(
          child: FutureBuilder(
            future: functionForBuilder(),
            builder: (BuildContext context,AsyncSnapshot snapshot){
            //here I set the condition for each case of snapshot
}

I have read some documents say that I should assign the functionForBuilder() to a Future variable when initState then use it in the future child of FutureBuilder. Example:

Future _future;

//initState function
  @override
  void initState() {
    super.initState();
    loadUser();
    _future=functionForBuilder();
  }

// then with the FutureBuilder
future: _future

With this way the screen is not rebuild anymore but my function returnRange() seems like not running as my expextation (I called the returnRange() once in the build() function).

Thanks in advance for your answer!

Phucss
  • 11
  • 3

3 Answers3

0

Whenever you assign to the _future variable again, you must do that inside a setState block, otherwise the widget will not rebuild with the new future.

For example:

void updateData() {
  setState(() {
      _future = functionForBuilder();
  });
}
caiopo
  • 454
  • 2
  • 8
  • Sorry but I don't understand what you mean. I already assign the `_future` with `functionForBuilder` inside the `initState()`. After that, there is no changes with it and I only pass the `_future` as a parameter for `FutureBuilder(future: _future)`. – Phucss Aug 12 '21 at 04:34
0

If you use FutureBuilder, it rebuild items again and again.

Try two ways:

  1. Don't use `future: functionForBuilder(), comment it.
  2. Remove FutureBuilder(), simply use Container().

And let me know any issue?

Rakesh Saini
  • 660
  • 2
  • 7
  • 20
  • I need the `FutureBuilder` to ensure that my function is finished before the `build` so I have a completed data to use. In case I delete the `future: functionForBuilder`, how it will check if my function is finished or not ? Thank you – Phucss Aug 12 '21 at 14:36
0

Code:

call your future in the initstate method not in the build as shown in the example.

class MyPage extends StatefulWidget { @override State<MyPage> createState() => _MyPageState(); } class _MyPageState extends State<MyPage> { // Declare a variable. late final Future<int> _future; @override void initState() { super.initState(); _future = _calculate(); // Assign your Future to it. } // This is your actual Future. Future<int> _calculate() => Future.delayed(Duration(seconds: 3), () => 42); @override Widget build(BuildContext context) { return Scaffold( body: FutureBuilder<int>( future: _future, // Use your variable here (not the actual Future) builder: (_, snapshot) { if (snapshot.hasData) return Text('Value = ${snapshot.data!}'); return Text('Loading...'); }, ), ); } }

Shahryar Rafique
  • 1,211
  • 15
  • 35
  • I think I did the same thing with your answer. But I mean the `returnRange()` returns the thing that I need for my calendar so I call it once inside the `build`. For the `_future`, I only call it in `initState` and then use it in the future of `FutureBuilder`. I think it is the same with what you did in your answer. – Phucss Aug 12 '21 at 14:32