тип «Null» не является подтипом типа «Строка» .

#android #flutter #android-studio #http

Вопрос:

Я пытаюсь получить данные о конвертации валют из API («https://api.exchangerate.host/latest»).

Вышеуказанная ошибка в заголовке отображается на экране в виде текста.

Я пытаюсь получить это сообщение в качестве вывода.

«msg»:»Если вы или ваша компания используете этот проект или вам нравится то, что мы делаем, пожалуйста, подумайте о том, чтобы поддержать нас, чтобы мы могли продолжать поддерживать и развивать этот проект».

Позже индивидуальные курсы валют.

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

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

Future<Album> fetchAlbum() async {
  final response = await http.get(Uri.parse('https://api.exchangerate.host/latest'));

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    return Album.fromJson(jsonDecode(response.body));
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

class Album {
  final String msg;

  Album({
    required this.msg,
  });

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      msg: json['msg'],
    );
  }
}

class CurrencyPage extends StatefulWidget {
  const CurrencyPage({Key? key}) : super(key: key);

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

class _CurrencyPageState extends State<CurrencyPage> {
  late Future<Album> futureAlbum;

  @override
  void initState() {
    super.initState();
    futureAlbum = fetchAlbum();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Fetch Data Example'),
        ),
        body: Center(
          child: FutureBuilder<Album>(
            future: futureAlbum,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(snapshot.data!.msg);
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              }

              // By default, show a loading spinner.
              return const CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}
 

Любая помощь будет признательна!

Комментарии:

1. скорее всего, это платно, и у вас нет доступа

2. @novol Когда вы вводите URL-адрес в браузере, вы получаете данные. разве это не значит, что это бесплатно?

3. моя ошибка, покажите, пожалуйста, что вы получаете от response.body

4. @novol type 'Null' is not a subtype of type 'String' Эта ошибка отображается на экране

5. я задал ответ, а не ошибку, я отвечаю ниже и проверил — это работа

Ответ №1:

вы неправильно используете модель. измените return Album.fromJson(jsonDecode(response.body)); return jsonDecode(response.body); и получите данные ответа, такие как snapshot.data!['motd']

код:

 Future<dynamic> fetchAlbum() async {
  final response = await http.get(Uri.parse('https://api.exchangerate.host/latest'));

  if (response.statusCode == 200) {

    // If the server did return a 200 OK response,
    // then parse the JSON.
    print(jsonDecode(response.body));
    return jsonDecode(response.body);
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

class Album {
  final String msg;

  Album({
    required this.msg,
  });

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      msg: json['msg'],
    );
  }
}

class CurrencyPage extends StatefulWidget {
  const CurrencyPage({Key? key}) : super(key: key);

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

class _CurrencyPageState extends State<CurrencyPage> {
  late dynamic futureAlbum;

  @override
  void initState() {
    super.initState();
    futureAlbum = fetchAlbum();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Fetch Data Example'),
        ),
        body: Center(
          child: FutureBuilder<dynamic>(
            future: futureAlbum,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(snapshot.data!['motd']['msg']);
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              }

              // By default, show a loading spinner.
              return const CircularProgressIndicator();
            },
          ),
        ),
    );
  }
}
 

Комментарии:

1. Спасибо за ответ

Ответ №2:

msg ключ находится внутри motd объекта в ответе. Поэтому fetchAlbum замените это:

 Future<Album> fetchAlbum() async {
  final response = await http.get(Uri.parse('https://api.exchangerate.host/latest'));

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    var body = jsonDecode(response.body);
    
    return Album.fromJson(body["motd"]);
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}