Flutter - 为 API 实现 JSON getter
Posted
技术标签:
【中文标题】Flutter - 为 API 实现 JSON getter【英文标题】:Flutter - Implementing JSON getter for API 【发布时间】:2019-02-07 17:14:51 【问题描述】:我正在尝试将 API found here 实现到我的 home_page.dart
中,使用 this flutter learners project 进行 JSON 解析。你可以在this GitHub Repo找到完整的代码
查看下面的当前图片,我希望最终项目“Trending News”部分从 API 返回文章图片,以及标题而不是“Item 1”、“Item 2”等...
我在使用这个版本时遇到了问题,因为当我尝试运行调试时,我遇到了问题; The getter length was called on null
home_page.dart
;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:one/cryptoData/crypto_data.dart';
import 'package:one/modules/crypto_presenter.dart';
import 'main.dart';
import 'dart:convert';
import 'background.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
Future<List<Photo>> fetchPhotos(http.Client client) async
final response =
await client.get('https://newsapi.org/v2/top-headlines?sources=crypto-coins-news&apiKey=d40a757cfb2e4dd99fc511a0cbf59098');
// Use the compute function to run parsePhotos in a separate isolate
return compute(parsePhotos, response.body);
// A function that will convert a response body into a List<Photo>
List<Photo> parsePhotos(String responseBody)
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
class Photo
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
Photo(this.albumId, this.id, this.title, this.url, this.thumbnailUrl);
factory Photo.fromJson(Map<String, dynamic> json)
return Photo(
albumId: json['albumId'] as int,
id: json['id'] as int,
title: json['title'] as String,
url: json['url'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
class HomePage extends StatefulWidget
@override
_HomePageState createState() => new _HomePageState();
class _HomePageState extends State<HomePage> implements CryptoListViewContract
CryptoListPresenter _presenter;
List<Crypto> _currencies;
bool _isLoading;
final List<MaterialColor> _colors = [Colors.blue, Colors.indigo, Colors.red];
final List<Photo> photos;
_HomePageState()
_presenter = new CryptoListPresenter(this);
List<String> items = [
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5",
"Item 6",
"Item 7",
"Item 8"
];
@override
void initState()
super.initState();
_isLoading = true;
_presenter.loadCurrencies();
@override
Widget build(BuildContext context)
return new Scaffold(
appBar: new AppBar(
title: new Text("Crypto App",
style: new TextStyle(
color: Colors.white,
fontFamily: 'Poppins',
fontSize: 22.5,
),
),
backgroundColor: const Color(0xFF273A48),
elevation: 0.0,
centerTitle: true,
),
body: _isLoading
? new Center(
child: new CircularProgressIndicator(),
)
: _allWidget()
);
Widget _allWidget()
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
//CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED
final headerList = new ListView.builder(
itemBuilder: (context, index)
EdgeInsets padding = index == 0?const EdgeInsets.only(
left: 20.0, right: 10.0, top: 4.0, bottom: 30.0):const EdgeInsets.only(
left: 10.0, right: 10.0, top: 4.0, bottom: 30.0);
return new Padding(
padding: padding,
child: new InkWell(
onTap: ()
print('Card selected');
,
child: new Container(
decoration: new BoxDecoration(
borderRadius: new BorderRadius.circular(10.0),
color: Colors.lightGreen,
boxShadow: [
new BoxShadow(
color: Colors.black.withAlpha(70),
offset: const Offset(3.0, 10.0),
blurRadius: 15.0)
],
image: new DecorationImage(
image: new ExactAssetImage(
'assets/img_$index%items.length.jpg'),
fit: BoxFit.fitHeight,
),
),
// height: 200.0,
width: 200.0,
child: new Stack(
children: <Widget>[
new Align(
alignment: Alignment.bottomCenter,
child: new Container(
decoration: new BoxDecoration(
color: const Color(0xFF273A48),
borderRadius: new BorderRadius.only(
bottomLeft: new Radius.circular(10.0),
bottomRight: new Radius.circular(10.0))),
height: 30.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'$items[index%items.length]',
style: new TextStyle(color: Colors.white),
)
],
)),
)
],
),
),
),
);
,
scrollDirection: Axis.horizontal,
itemCount: photos.length,
);
final body = new Scaffold(
backgroundColor: Colors.transparent,
body: new Container(
child: new Stack(
children: <Widget>[
new Padding(
padding: new EdgeInsets.only(top: 10.0),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Align(
alignment: Alignment.centerLeft,
child: new Padding(
padding: new EdgeInsets.only(left: 10.0,),
child: new Text(
"Trending News",
style: new TextStyle(
letterSpacing: 0.8,
fontFamily: 'Kanit',
fontSize: 17.5,
color: Colors.white,
),
)
),
),
new Container(
height: 300.0, width: _width, child: headerList),
new Expanded(child:
ListView.builder(
itemBuilder: (BuildContext context, int index)
final int i = index;
final Crypto currency = _currencies[i];
final MaterialColor color = _colors[i % _colors.length];
return new ListTile(
title: new Column(
children: <Widget>[
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
height: 72.0,
width: 72.0,
decoration: new BoxDecoration(
color: Colors.white,
boxShadow: [
new BoxShadow(
color:
Colors.black.withAlpha(80),
offset: const Offset(2.0, 2.0),
blurRadius: 15.0)
],
borderRadius: new BorderRadius.all(
new Radius.circular(35.0)),
image: new DecorationImage(
image: new ExactAssetImage(
"cryptoiconsBlack/"+currency.symbol.toLowerCase()+"@2x.png",
),
fit: BoxFit.cover,
)),
),
new SizedBox(
width: 8.0,
),
new Expanded(
child: new Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
new Text(
currency.name,
style: new TextStyle(
fontSize: 14.0,
fontFamily: 'Poppins',
color: Colors.black87,
fontWeight: FontWeight.bold),
),
_getSubtitleText(currency.price_usd, currency.percent_change_1h),
],
)),
],
),
new Divider(),
],
),
);
))
],
),
),
],
),
),
);
return new Container(
decoration: new BoxDecoration(
color: const Color(0xFF273A48),
),
child: new Stack(
children: <Widget>[
new CustomPaint(
size: new Size(_width, _height),
painter: new Background(),
),
body,
],
),
);
// CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED
Widget _getSubtitleText(String priceUSD, String percentageChange)
TextSpan priceTextWidget = new TextSpan(
text: "\$$priceUSD\n", style: new TextStyle(color: Colors.black));
String percentageChangeText = "1 hour: $percentageChange%";
TextSpan percentageChangeTextWidget;
if (double.parse(percentageChange) > 0)
percentageChangeTextWidget = new TextSpan(
text: percentageChangeText,
style: new TextStyle(color: Colors.green));
else
percentageChangeTextWidget = new TextSpan(
text: percentageChangeText, style: new TextStyle(color: Colors.red));
return new RichText(
text: new TextSpan(
children: [priceTextWidget, percentageChangeTextWidget]));
//DONT TOUCH, Works with cryptoListViewContract implimentation in _MyHomePageState
@override
void onLoadCryptoComplete(List<Crypto> items)
// TODO: implement onLoadCryptoComplete
setState(()
_currencies = items;
_isLoading = false;
);
@override
void onLoadCryptoError()
// TODO: implement onLoadCryptoError
应用程序的最后工作版本;
谢谢,杰克
【问题讨论】:
你尝试过用断点调试你的代码吗? 不,我不知道它们是如何工作的,所以我从不使用它们 【参考方案1】:列表photos
在您的情况下为空。它没有在任何地方初始化。我想你忘了调用返回List<Photo>
的fetchPhotos()
方法。所以才会出现空指针异常。
【讨论】:
嘿,Vinoth,您能否使用提供的赏金说明重新修改您的答案。谢谢 你能发布你的完整项目吗?因为我无法编译这个类,因为缺少 3 个左右的类。 好的,感谢您的关注,我将能够在 5 小时左右发布 github 存储库。抱歉让您久等了 没问题@Jake。那就等你的帖子吧。 好的,非常感谢。我现在不在,但我会尽快解决【参考方案2】:尝试改变你拥有的任何地方
.length
到
(... ?.length ?? 0)
喜欢
itemCount: photos?.length ?? 0,
final MaterialColor color = _colors[i % (_colors?.length ?? 0)];
'assets/img_$index % (items?.length ?? 0).jpg'),
...
【讨论】:
无论如何你可以提供我在上面的问题中留下的代码,所有这些都改变了吗?我目前正在研究我项目的另一个方面,并希望保持我的思路集中。谢谢 这对我来说太多代码了。我目前正在完全从事另一个项目...... ;-) 好的,感谢您的帮助,当我有时间我会完成它并让您知道它是如何进行的。 :) 您好 Günter,刚刚查看代码,我注意到第 60 行_HomePageState()
下的 the final variable 'photos' must be initialised
。我需要更改什么来解决这个问题?
您可能希望删除 final
修饰符,然后在 _HomePageState 的 initState() 中利用您的 fetch 和 parse 方法。【参考方案3】:
替换
class Photo
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
Photo(this.albumId, this.id, this.title, this.url, this.thumbnailUrl);
factory Photo.fromJson(Map<String, dynamic> json)
return Photo(
albumId: json['albumId'] as int,
id: json['id'] as int,
title: json['title'] as String,
url: json['url'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
与:
class Photo
final String albumId;
final String id;
final String title;
final String url;
final String thumbnailUrl;
Photo(this.albumId, this.id, this.title, this.url, this.thumbnailUrl);
factory Photo.fromJson(Map<String, dynamic> json)
return Photo(
albumId: json['albumId'] as String,
id: json['id'] as String,
title: json['title'] as String,
url: json['url'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
【讨论】:
以上是关于Flutter - 为 API 实现 JSON getter的主要内容,如果未能解决你的问题,请参考以下文章