Flutter: как получить данные из api только один раз при использовании FutureBuilder?

Как я могу получить данные только один раз при использовании FutureBuilder, чтобы во время выборки отображался индикатор загрузки?

Проблема в том, что каждый раз, когда пользователь открывает экран, он повторно выбирает данные, даже если я установил будущее в initState ().

Я хочу получить данные только при первом открытии экрана пользователем, тогда я буду использовать сохраненные полученные данные.

мне просто использовать виджет с отслеживанием состояния с переменной загрузки и установить его в setState ()?

Я использую пакет провайдера

Future<void> fetchData() async {
try {
  final response =
      await http.get(url, headers: {'Authorization': 'Bearer $_token'});......

и мой виджет экрана:

    class _MyScreenState extends State<MyScreen> {
  Future<void> fetchData;

  @override
  void initState() {

    fetchData =
        Provider.of<Data>(context, listen: false).fetchData();

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: fetchData,
      builder: (ctx, snapshot) =>
          snapshot.connectionState == ConnectionState.done
              ? Consumer<Data>(
                  builder: (context, data, child) => Text(data.fetchedData)): Center(
                               child: CircularProgressIndicator(),
                               ),
                        
    );
  }
}

person AhWagih    schedule 19.08.2020    source источник


Ответы (3)


Если вы хотите получить данные только один раз, даже если виджет перестраивается, вам придется создать для этого модель. Вот как его можно сделать:

class MyModel{
 String value;
 Future<String> fetchData() async {
 if(value==null){
 try {
  final response =
      await http.get(url, headers: {'Authorization': 'Bearer $_token'});......
 value=(YourReturnedString)
  }
 }
  return value;
 }
}

Не забудьте указать MyModel как Provider. В вашем FutureBuilder:

@override
  Widget build(context) {
    final myModel=Provider.of<MyModel>(context)
    return FutureBuilder<String>(
      future: myModel.fetchData(),
      builder: (context, snapshot) {
        // ...
      }
    );
  }
person Uni    schedule 19.08.2020
comment
дело в том, что я пытаюсь выяснить, как лучше всего получить данные из api, иногда я замечаю, что некоторые приложения извлекают данные каждый раз при загрузке экрана, а некоторые другие приложения просто извлекают данные, когда экран сначала загружается, затем, когда вы повторно посещаете тот же экран, вы обнаруживаете, что данные готовы без загрузки, поэтому я хочу добиться этого. - person AhWagih; 19.08.2020
comment
это действительно зависит. Если ваши данные часто меняются, при желании загрузите их много раз. Если не получить его только один раз. На самом деле все сводится к тому, сколько раз вы действительно хотите его получить. - person Uni; 19.08.2020
comment
Я только что прочитал о фабрике, которая используется вместо конструктора ... официальный пример кажется правильным для этого варианта использования, на самом деле касается кеширования объекта. Если вы хотите его прочитать: dart.dev/guides/language/language -тур # фабрики-конструкторы - person funder7; 07.01.2021

Простой подход - ввести StatefulWidget, в который мы помещаем наше будущее в переменную. Теперь каждая перестройка будет ссылаться на один и тот же экземпляр Future:

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

class _MyWidgetState extends State<MyWidget> {
  Future<String> _future;

  @override
  void initState() {
    _future = callAsyncFetch();
    super.initState();
  }

  @override
  Widget build(context) {
    return FutureBuilder<String>(
      future: _future,
      builder: (context, snapshot) {
        // ...
      }
    );
  }
}

Или вы можете просто использовать FutureProvider вместо StatefulWidget выше:

class MyWidget extends StatelessWidget {
  // Future<String> callAsyncFetch() => Future.delayed(Duration(seconds: 2), () => "hi");
  @override
  Widget build(BuildContext context) {
    // print('building widget');
    return FutureProvider<String>(
      create: (_) {
        // print('calling future');
        return callAsyncFetch();
      },
      child: Consumer<String>(
        builder: (_, value, __) => Text(value ?? 'Loading...'),
      ),
    );
  }
}
person chiquitta    schedule 19.08.2020
comment
но это не решает проблему, и каждый раз при повторном открытии страницы данные будут извлекаться снова. - person AhWagih; 19.08.2020
comment
Конечно, но это совершенно нормально ... если вам нужно сохранить данные, вы должны каким-то образом сохранить состояние, которое может быть любым (даже локальной базой данных). Это во многом зависит от типа данных и времени их обновления. - person funder7; 07.01.2021

Вы можете реализовать поставщика и передавать данные его дочернему элементу. Обратитесь к этому примеру, чтобы получить данные один раз и использовать их во всем дочернем элементе.

person Aashutosh Poudel    schedule 18.03.2021