2

Whenever I change the page and come back, the api is reloaded. I have tried many suggestions. I would be glad if you help.

Here are the methods I tried : How to avoid reloading data every time navigating to page

How to parse JSON only once in Flutter

Flutter Switching to Tab Reloads Widgets and runs FutureBuilder

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

import 'models/Word.dart';

class WordListPage extends StatefulWidget {
  WordListPage(Key k) : super(key: k);

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

class _WordListPageState extends State<WordListPage> {
  Future<List<Word>> data;
  bool isSearching = false;
  TextEditingController myController = TextEditingController();
  List<Word> _words = List<Word>();
  List<Word> _wordsForDisplay = List<Word>();
  var keyListPage = PageStorageKey('list_page_key');

  Future<List<Word>> getWord() async {
    var response = await http.get("myAPIurl");
    var _words = List<Word>();

    _words = (json.decode(utf8.decode(response.bodyBytes)) as List)
        .map((singleWordMap) => Word.fromJsonMap(singleWordMap))
        .toList();
    return _words;
  }

  @override
  void initState() {
    getWord().then((value) {
      setState(() {
        _words.addAll(value);
        _wordsForDisplay = _words;
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: myFutureBuilder(),
      appBar: AppBar(
        leading: Center(
          child: RichText(
            textAlign: TextAlign.center,
            text: TextSpan(
              style: DefaultTextStyle.of(context).style,
              children: <TextSpan>[
                TextSpan(
                  text: 'Total',
                  style: TextStyle(
                    decoration: TextDecoration.none,
                    fontSize: 10,
                    color: Colors.white,
                  ),
                ),
                TextSpan(
                  text: '\n${_words.length.toString()}',
                  style: TextStyle(
                    decoration: TextDecoration.none,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                    fontSize: 12,
                  ),
                ),
                TextSpan(
                  text: '\nLetter',
                  style: TextStyle(
                    decoration: TextDecoration.none,
                    color: Colors.white,
                    fontSize: 10,
                  ),
                ),
              ],
            ),
          ),
        ),
        centerTitle: true,
        title: !isSearching
            ? Text('My Title')
            : TextField(
                autofocus: true,
                style: TextStyle(color: Colors.white),
                controller: myController,
                onChanged: (value) {
                  value = value.toLowerCase();
                  setState(
                    () {
                      _wordsForDisplay = _words.where(
                        (word) {
                          var wordTitle = word.word.toLowerCase();
                          return wordTitle.contains(value);
                        },
                      ).toList();
                    },
                  );
                  setState(
                    () {
                      _wordsForDisplay = _words.where(
                        (word) {
                          var wordPronounce = word.pronunciation.toLowerCase();
                          return wordPronounce.contains(value);
                        },
                      ).toList();
                    },
                  );
                },
                decoration: InputDecoration(
                  isCollapsed: true,
                  icon: Icon(
                    Icons.menu_book,
                    color: Colors.white,
                  ),
                  hintText: 'Search',
                  hintStyle: TextStyle(color: Colors.white),
                ),
              ),
        actions: [
          isSearching
              ? IconButton(
                  icon: Icon(Icons.cancel_outlined),
                  onPressed: () {
                    setState(
                      () {
                        this.isSearching = false;
                        myController.clear();
                        _wordsForDisplay = _words.where(
                          (word) {
                            var wordTitle = word.word.toLowerCase();
                            return wordTitle.contains(wordTitle);
                          },
                        ).toList();
                      },
                    );
                  },
                )
              : IconButton(
                  icon: Icon(Icons.search_sharp),
                  onPressed: () {
                    setState(
                      () {
                        this.isSearching = true;
                      },
                    );
                  },
                ),
        ],
      ),
    );
  }

  FutureBuilder<List<Word>> myFutureBuilder() {
    return FutureBuilder(
      future: getWord(),
      builder: (context, AsyncSnapshot<List<Word>> snapshot) {
        if (snapshot.hasData) {
          return myWordListView(snapshot);
        } else {
          return Center(
            child: CircularProgressIndicator(),
          );
        }
      },
    );
  }

  ListView myWordListView(AsyncSnapshot<List<Word>> snapshot) {
    return ListView.builder(
      itemCount: _wordsForDisplay.length,
      itemBuilder: (context, index) {
        return ExpansionTile(
          title: Text(
            _wordsForDisplay[index].word,
            style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.0),
          ),
          subtitle: Text(
            snapshot.data[index].pronunciation[0].toUpperCase() +
                snapshot.data[index].pronunciation.substring(1),
          ),
          leading: CircleAvatar(
            child: Text(snapshot.data[index].word[0]),
          ),
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Container(
                  width: MediaQuery.of(context).size.width,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(
                        vertical: 7.0, horizontal: 19.0),
                    child: RichText(
                      text: TextSpan(
                        style: DefaultTextStyle.of(context).style,
                        children: <TextSpan>[
                          TextSpan(
                            text: snapshot.data[index].word + ' : ',
                            style: TextStyle(fontWeight: FontWeight.bold),
                          ),
                          TextSpan(text: snapshot.data[index].meaning),
                        ],
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ],
        );
      },
    );
  }
}

2
  • The reason your API reload whenever you come back to this page is becasue initState() get called. This means whatever function inside initState() will get called. This happens to your getWord() function. Commented Mar 4, 2021 at 11:06
  • But how can I do it without defining it in initState? Commented Mar 4, 2021 at 11:24

3 Answers 3

0

When you navigate and return back, the build method is called. Here you have "myFutureBuilder" placed as the body widget of Scaffold, thus this code get executed, within it "getWord" method is called, an it fetches data from the api everytime.

I suggest you to remove "myFutureBuider" and use "myWirdListView" directly as the body of the scaffold. Change myWordListView(List<Word> listOfWord) to use the word list you have already fetched in the initState() .

Sign up to request clarification or add additional context in comments.

3 Comments

What should I do to avoid getting data from the api every time?
@OneMore I've added more info. Check whether that works for you.
I said yesterday that solved my problem, but now my list is loading 1-2 seconds late. Do you have any advice on this?
0

You need to to separate your api call from your ui. That way your api will only get called when you want it to. I recommend using some kind of external state management library, such as Provider, BLoC, or RxDart. Flutter will rebuild a widget anytime it wants to, beyond when you trigger it.

2 Comments

I will research for BLoC thank you but I need a faster solution
You need to take the call out of initState(). If you need the data on first load, you can call it from the same function you use to navigate to the WordListPage. Just make sure you await it.
0

you passed getWord() as a function to you future builder so each time the widget get rebuilt, it is called.

To solve that, declare a variable Future<Word> getWordFuture ; , Inside initState assign getWordFuture = getWord(); and use getWordFuture in the Future builder.


import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

import 'models/Word.dart';

class WordListPage extends StatefulWidget {
  WordListPage(Key k) : super(key: k);

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

class _WordListPageState extends State<WordListPage> {
  Future<List<Word>> data;
  bool isSearching = false;
  TextEditingController myController = TextEditingController();
  List<Word> _words = List<Word>();
  List<Word> _wordsForDisplay = List<Word>();
  var keyListPage = PageStorageKey('list_page_key');
Future<List<Word>> getWordFuture = Future<List<Word>> ; //1

  Future<List<Word>> getWord() async {
    var response = await http.get("myAPIurl");
    var _words = List<Word>();

    _words = (json.decode(utf8.decode(response.bodyBytes)) as List)
        .map((singleWordMap) => Word.fromJsonMap(singleWordMap))
        .toList();
    return _words;
  }

  @override
  void initState() {
   getWordFuture = getWord(); //2
    getWord().then((value) {
      setState(() {
        _words.addAll(value);
        _wordsForDisplay = _words;
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: myFutureBuilder(),
      appBar: AppBar(
        leading: Center(
          child: RichText(
            textAlign: TextAlign.center,
            text: TextSpan(
              style: DefaultTextStyle.of(context).style,
              children: <TextSpan>[
                TextSpan(
                  text: 'Total',
                  style: TextStyle(
                    decoration: TextDecoration.none,
                    fontSize: 10,
                    color: Colors.white,
                  ),
                ),
                TextSpan(
                  text: '\n${_words.length.toString()}',
                  style: TextStyle(
                    decoration: TextDecoration.none,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                    fontSize: 12,
                  ),
                ),
                TextSpan(
                  text: '\nLetter',
                  style: TextStyle(
                    decoration: TextDecoration.none,
                    color: Colors.white,
                    fontSize: 10,
                  ),
                ),
              ],
            ),
          ),
        ),
        centerTitle: true,
        title: !isSearching
            ? Text('My Title')
            : TextField(
                autofocus: true,
                style: TextStyle(color: Colors.white),
                controller: myController,
                onChanged: (value) {
                  value = value.toLowerCase();
                  setState(
                    () {
                      _wordsForDisplay = _words.where(
                        (word) {
                          var wordTitle = word.word.toLowerCase();
                          return wordTitle.contains(value);
                        },
                      ).toList();
                    },
                  );
                  setState(
                    () {
                      _wordsForDisplay = _words.where(
                        (word) {
                          var wordPronounce = word.pronunciation.toLowerCase();
                          return wordPronounce.contains(value);
                        },
                      ).toList();
                    },
                  );
                },
                decoration: InputDecoration(
                  isCollapsed: true,
                  icon: Icon(
                    Icons.menu_book,
                    color: Colors.white,
                  ),
                  hintText: 'Search',
                  hintStyle: TextStyle(color: Colors.white),
                ),
              ),
        actions: [
          isSearching
              ? IconButton(
                  icon: Icon(Icons.cancel_outlined),
                  onPressed: () {
                    setState(
                      () {
                        this.isSearching = false;
                        myController.clear();
                        _wordsForDisplay = _words.where(
                          (word) {
                            var wordTitle = word.word.toLowerCase();
                            return wordTitle.contains(wordTitle);
                          },
                        ).toList();
                      },
                    );
                  },
                )
              : IconButton(
                  icon: Icon(Icons.search_sharp),
                  onPressed: () {
                    setState(
                      () {
                        this.isSearching = true;
                      },
                    );
                  },
                ),
        ],
      ),
    );
  }

  FutureBuilder<List<Word>> myFutureBuilder() {
    return FutureBuilder(
      //future: getWord(),
      future:getWordFuture,
      builder: (context, AsyncSnapshot<List<Word>> snapshot) {
        if (snapshot.hasData) {
          return myWordListView(snapshot);
        } else {
          return Center(
            child: CircularProgressIndicator(),
          );
        }
      },
    );
  }

  ListView myWordListView(AsyncSnapshot<List<Word>> snapshot) {
    return ListView.builder(
      itemCount: _wordsForDisplay.length,
      itemBuilder: (context, index) {
        return ExpansionTile(
          title: Text(
            _wordsForDisplay[index].word,
            style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.0),
          ),
          subtitle: Text(
            snapshot.data[index].pronunciation[0].toUpperCase() +
                snapshot.data[index].pronunciation.substring(1),
          ),
          leading: CircleAvatar(
            child: Text(snapshot.data[index].word[0]),
          ),
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Container(
                  width: MediaQuery.of(context).size.width,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(
                        vertical: 7.0, horizontal: 19.0),
                    child: RichText(
                      text: TextSpan(
                        style: DefaultTextStyle.of(context).style,
                        children: <TextSpan>[
                          TextSpan(
                            text: snapshot.data[index].word + ' : ',
                            style: TextStyle(fontWeight: FontWeight.bold),
                          ),
                          TextSpan(text: snapshot.data[index].meaning),
                        ],
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ],
        );
      },
    );
  }
}

2 Comments

Sorry but this is not working. I have tried this over and over already. I don't know why but it doesn't work.
This unfortunately doesn't fix the issue. Stateful widgets in flutter persist data at the widget level, meaning it persists repaints but not being taken out of/being put into the widget tree. The data here needs to be stored via state management: flutter.dev/docs/development/data-and-backend/state-mgmt/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.