【问题标题】:Flutter api reloads every navigationFlutter api 重新加载每个导航
【发布时间】:2021-06-03 00:23:29
【问题描述】:

每当我更改页面并返回时,都会重新加载 api。我尝试了很多建议。如果您能提供帮助,我会很高兴。

以下是我尝试过的方法: 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),
                        ],
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ],
        );
      },
    );
  }
}

【问题讨论】:

  • 每当您返回此页面时重新加载 API 的原因是因为 initState() 被调用。这意味着initState() 中的任何函数都将被调用。这发生在您的 getWord() 函数中。
  • 但是如果不在 initState 中定义,我该怎么做呢?

标签: api flutter


【解决方案1】:

当您导航并返回时,会调用 build 方法。在这里,您将“myFutureBuilder”放置为 Scaffold 的 body 小部件,因此该代码被执行,在其中调用“getWord”方法,它每次都从 api 获取数据。

建议你去掉“myFutureBuider”,直接使用“myWirdListView”作为脚手架的主体。更改 myWordListView(List&lt;Word&gt; listOfWord) 以使用您已经在 initState() 中获取的单词列表。

【讨论】:

  • 如何避免每次都从api获取数据?
  • @OneMore 我添加了更多信息。检查这是否适合您。
  • 我昨天说解决了我的问题,但现在我的列表加载延迟了 1-2 秒。您对此有什么建议吗?
【解决方案2】:

您将 getWord() 作为函数传递给未来的构建器,因此每次重建小部件时,都会调用它

要解决这个问题,请声明一个变量 Future&lt;Word&gt; getWordFuture ; ,在 initState 中分配 getWordFuture = getWord(); 并在 Future 构建器中使用 getWordFuture。


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),
                        ],
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ],
        );
      },
    );
  }
}

【讨论】:

  • 抱歉,这不起作用。我已经一遍又一遍地尝试过。我不知道为什么,但它不起作用。
  • 很遗憾,这并不能解决问题。 Flutter 中的有状态小部件将数据保存在 widget 级别,这意味着它会保存重绘但不会被取出/放入小部件树中。这里的数据需要通过状态管理来存储:flutter.dev/docs/development/data-and-backend/state-mgmt/…
【解决方案3】:

您需要将您的 api 调用与您的 ui 分开。这样你的 api 只会在你想要的时候被调用。我建议使用某种外部状态管理库,例如 Provider、BLoC 或 RxDart。 Flutter 会在任何时候重建一个小部件,只要你触发它。

【讨论】:

  • 我会为 BLoC 进行研究,谢谢,但我需要更快的解决方案
  • 您需要将调用从 initState() 中取出。如果您在首次加载时需要数据,则可以从用于导航到 WordListPage 的同一函数中调用它。只要确保你等待它。
猜你喜欢
  • 2019-09-05
  • 1970-01-01
  • 2018-09-23
  • 1970-01-01
  • 2019-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多