Могу ли я использовать Future<String> для «заполнения» виджета Text() вместо использования FutureBuilder во Flutter?

Я пытаюсь лучше понять Futures во Flutter. В этом примере мое приложение выполняет вызов API, чтобы получить некоторую информацию типа Future<String>. Я хочу отобразить эту информацию в виджете Text(). Однако, поскольку мой String заключен в Future, я не могу поместить эту информацию в свой виджет Text(), и я не знаю, как с этим справиться, не прибегая к FutureBuilder для создания небольшого дерева виджетов.

В следующем примере используется FutureBuilder, и он работает нормально. Обратите внимание, что я закомментировал следующую строку внизу:

Future<String> category = getData();

Можно ли превратить category в String и просто поместить это в мой виджет Text()?

import 'package:flutter/material.dart';
import 'cocktails.dart';

class CocktailScreen extends StatefulWidget {
  const CocktailScreen({super.key});

  @override
  State<CocktailScreen> createState() => _CocktailScreenState();
}

class _CocktailScreenState extends State<CocktailScreen> {
  @override
  Widget build(BuildContext context) {
    Cocktails cocktails = Cocktails();

    Future<String> getData() async {
      var data = await cocktails.getCocktailByName('margarita');
      String category = data['drinks'][0]['strCategory'];
      print('Category: ${data["drinks"][0]["strCategory"]}');
      return category;
    }

    FutureBuilder categoryText = FutureBuilder(
      initialData: '',
      future: getData(),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          if (snapshot.hasData) {
            return Text(snapshot.data);
          } else if (snapshot.hasError) {
            return Text(snapshot.error.toString());
          }
        }
        return const CircularProgressIndicator();
      },
    );

    //Future<String> category = getData();

    return Center(
      child: categoryText,
    );
  }
}

Вот мой Cocktails класс:

import 'networking.dart';

const apiKey = '1';
const apiUrl = 'https://www.thecocktaildb.com/api/json/v1/1/search.php';

class Cocktails {
  Future<dynamic> getCocktailByName(String cocktailName) async {
    NetworkHelper networkHelper =
        NetworkHelper('$apiUrl?s=$cocktailName&apikey=$apiKey');
    dynamic cocktailData = await networkHelper.getData();
    return cocktailData;
  }
}

А вот и мой NetworkHelper класс:

import 'package:http/http.dart' as http;
import 'dart:convert';

class NetworkHelper {
  NetworkHelper(this.url);

  final String url;

  Future<dynamic> getData() async {
    http.Response response = await http.get(Uri.parse(url));
    if (response.statusCode == 200) {
      String data = response.body;
      var decodedData = jsonDecode(data);
      return decodedData;
    } else {
      //print('Error: ${response.statusCode}');
      throw 'Sorry, there\'s a problem with the request';
    }
  }
}

getData() возвращает Future<String>, поэтому я не могу использовать его напрямую в Text(). Другими словами, я ищу способ использовать category в своем текстовом виджете следующим образом: Text(category), не прибегая к FutureBuilder.
MySilmaril 22.11.2022 16:50
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
98
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Лучше всего использовать FutureBuilder:

FutureBuilder categoryText = FutureBuilder<String>(
        future: getData(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.waiting:
              return Text('Loading....');
            default:
              if (snapshot.hasError) {
                return Text('Error: ${snapshot.error}');
              } else {
                var data = snapshot.data ?? '';

                return Text(data);
              }
          }
        },
      ),

но если вы не хотите использовать FutureBuilder, сначала определите строковую переменную, как показано ниже, и измените свой adasd на это:

String category = '';

Future<void> getData() async {
  var data = await cocktails.getCocktailByName('margarita');
  setState(() {
     category = data['drinks'][0]['strCategory'];
  });
}

затем вызовите его в initState :

@override
  void initState() {
    super.initState();
    getData();
  }

и используйте его так:

@override
  Widget build(BuildContext context) {
    return Center(
      child: Text(category),
    );
  }

помните, определяйте category и getData и cocktails вне метода сборки, а не внутри него.

Спасибо, но мой FutureBuilder уже работает. Я искал способ сделать это без использования FutureBuilder. Я не уверен, что то, о чем я прошу, возможно?

MySilmaril 22.11.2022 16:54

Я обновил свой ответ, зацените, @MySilmaril

eamirho3ein 22.11.2022 16:58

Еще раз спасибо. Это именно то, что я искал. Я попробовал это, и это отлично работает, но я думаю, что последую вашему совету и воспользуюсь FutureBuilder.

MySilmaril 22.11.2022 20:15

@MySilmaril рад помочь, да FutureBuilder это правильный путь.

eamirho3ein 22.11.2022 20:32

Я бы использовал FutureProvider RiverPod перед использованием FutureBuilder в любой день. Гораздо более гибкий и гораздо более чистый синтаксис.

Randal Schwartz 22.11.2022 20:46

Да, вы можете получить значение Future и обновить состояние на основе in без использования FutureBuilder, вызвав Future в initState() и используя ключевое слово then, чтобы обновить состояние, когда Future возвращает снимок.

   class StatefuleWidget extends StatefulWidget {
  const StatefuleWidget({super.key});

  @override
  State<StatefuleWidget> createState() => _StatefuleWidgetState();
}

class _StatefuleWidgetState extends State<StatefuleWidget> {
  String? text;

  Future<String> getData() async {
    var data = await cocktails.getCocktailByName('margarita');
    String category = data['drinks'][0]['strCategory'];
    print('Category: ${data["drinks"][0]["strCategory"]}');
    return category;
  }

  @override
  void initState() {
    super.initState();
    getData().then((value) {
      setState(() {
        text = value;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text(text ?? 'Loading');
  }
}

здесь я сделал переменную text обнуляемой, затем в реализации виджета Text() я установил для нее загружаемый текст в качестве значения по умолчанию, которое будет отображаться до тех пор, пока это Future не будет выполнено0

Спасибо, что поделились, как это сделать, используя ключевое слово then. До сих пор я использовал async-await, так что это очень полезный пример для меня.

MySilmaril 22.11.2022 20:17

Ничего страшного, удачи в работе

Gwhyyy 22.11.2022 20:20
super.initState(); принадлежит первым в initState(), а не последним.
Randal Schwartz 22.11.2022 20:44

спасибо, я ошибся. Я изменю ответ

Gwhyyy 22.11.2022 20:46

Другие вопросы по теме