问题描述
我正在使用Flutter Http包处理Flutter内的网络请求。
列表视图将为每个列表项显示文本和图像。
这些项目的数据来自HTTP请求。
该请求在我的网络助手类中完成。
在欢迎屏幕中,我有一个从initState()调用的函数,该函数会将本地变量设置为从网络帮助程序返回的json,但是一旦我尝试从该变量访问数据,则该函数为null。但是,如果我只打印数据,那么数据就在那里。
我的问题是我已经访问了数据,但是想不到将数据传递到列表视图的方式。
这是我的网络助手类代码的样子
import 'package:http/http.dart' as http;
import 'dart:convert';
class NetworkHelper{
//Sets up search filter variables
int year;
String make;
String model;
String condition;
String combustible;
String style;
String color;
double minPrice;
double maxPrice;
Future fetchAlbum() async {
http.Response response = await http.get('https://jsonplaceholder.typicode.com/todos');
if (response.statusCode == 200) {
String data = response.body;
return jsonDecode(data);
} else {
throw Exception('Failed to load album');
}
}
}
这是我的欢迎屏幕的样子
import 'package:Flutter/material.dart';
import 'package:components/appBarTitle.dart';
import 'package:components/bottomBarComponent.dart';
import 'package:convex_bottom_bar/convex_bottom_bar.dart';
import 'package:constants.dart';
import 'package:helpers/network_helper.dart';
import 'dart:convert';
class WelcomeScreen extends StatefulWidget {
static String id = '/welcome_screen';
@override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
int _index = 0;
NetworkHelper http = NetworkHelper();
dynamic data;
Future testMethod () async {
this.data = await http.fetchAlbum();
print(this.data);
}
@override
void initState() {
super.initState();
testMethod();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,appBar: AppBar(
elevation: 0.0,title: AppBarTitle(),),body: Padding(
padding: const EdgeInsets.all(12.0),child: ListView(
children: [
ListBody(
children: [
Card(
child: Row(
children: [
Expanded(
child: Column(
children: [
Text('2017 Nissan',style: TextStyle(fontWeight: FontWeight.w300,)),Text('Versa 1.6 SL',style: TextStyle(fontWeight: FontWeight.w100,Padding(
padding: const EdgeInsets.all(8.0),child: Text('\$7,998.00',style: TextStyle(fontWeight: FontWeight.w200,],Expanded(
child: Image.network('https://via.placeholder.com/450/0000FF',scale: 4,Card(
child: Row(
children: [
Expanded(
child: Column(
children: [
Text('2017 Nissan',)
],bottomNavigationBar: ConvexAppBar(
backgroundColor: Color(0xffe74c3c),items: [
TabItem(icon: Icons.home,title: 'Home'),TabItem(icon: Icons.map,title: 'discover'),TabItem(icon: Icons.search,title: 'Busca'),TabItem(icon: Icons.person,title: 'Mi Cuenta'),TabItem(icon: Icons.build,title: 'Settings'),initialActiveIndex: 2,//optional,default as 0
onTap: (int i) => print('click index=$i'),)
);
}
}
解决方法
最佳做法是添加一些抽象层。不要在一处做这一切。
为每个传入数据创建一个数据模型。然后将这些对象的列表传递到列表视图。
此示例建立呼叫并创建对象:
Future<List<HowTo>> fetchHowTos() async {
// get the full web resource path based on environment,etc.
String callPath = webAPI.getHowToPath();
// just make the call,don't parse yet
dynamic response = await makeCall(path: callPath);
try {
// this is the object type we want to convert the data into so it's easier to work with throughout your app
List<HowTo> howTos = [];
if (response != null) {
// decode the data and then send each piece into a fromJson method
dynamic howTosJson = json.decode(response.body);
howTosJson.forEach((howToJson) {
howTos.add(HowTo.fromJson(howToJson));
});
}
return howTos;
} catch (error,stacktrace) {
logger.error(error,stacktrace,context: 'error parsing how to content');
return [];
}
}
此代码实际上是用来拨打电话的:
Future<dynamic> makeCall({String path}) async {
try {
final response = await network.getCall(path: path);
if (response.statusCode == 200) {
// If server returns an OK response,parse the JSON.
return response;
} else if (response.statusCode == 404) {
// if no document exists return null. expected for items without recommendations
return null;
} else {
throw Exception('Failed http call. status: ' +
response.statusCode.toString());
}
} catch (error,context: 'path: $path');
}
}
这是要在您的应用中创建和使用的对象
class HowTo {
final String id;
final String title;
final int order;
HowTo({
this.id,this.title,this.order,});
factory HowTo.from(HowTo howTo) {
return HowTo(
id: howTo.id,title: howTo.title,order: howTo.order,);
}
factory HowTo.fromJson(Map<String,dynamic> json) {
return HowTo(
id: json['id'] ?? '',title: json['Title'] ?? '',order: json['Order'] ?? 0,);
}
}
将响应从此传递到列表视图包装器
List<HowTo> howTos = await fetchHowTos();
,
您可以在下面复制粘贴运行完整代码
您可以使用FutureBuilder
和ListView.builder
您可以在下面查看工作演示
代码段
Future<List<Payload>> _future;
NetworkHelper http = NetworkHelper();
Future<List<Payload>> testMethod() async {
var data = await http.fetchAlbum();
return data;
}
@override
void initState() {
super.initState();
_future = testMethod();
}
...
FutureBuilder(
future: _future,builder: (context,AsyncSnapshot<List<Payload>> snapshot) {
...
} else {
return ListView.builder(
shrinkWrap: true,itemCount: snapshot.data.length,itemBuilder: (context,index) {
return Card(
elevation: 6.0,child: Padding(
padding: const EdgeInsets.only(
top: 6.0,bottom: 6.0,left: 8.0,right: 8.0),child: ListTile(
title: Text(
snapshot.data[index].id.toString()),subtitle: Text(
snapshot.data[index].title,),
工作演示
完整代码
import 'package:convex_bottom_bar/convex_bottom_bar.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
// To parse this JSON data,do
//
// final payload = payloadFromJson(jsonString);
import 'dart:convert';
List<Payload> payloadFromJson(String str) =>
List<Payload>.from(json.decode(str).map((x) => Payload.fromJson(x)));
String payloadToJson(List<Payload> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Payload {
Payload({
this.userId,this.id,this.completed,});
int userId;
int id;
String title;
bool completed;
factory Payload.fromJson(Map<String,dynamic> json) => Payload(
userId: json["userId"],id: json["id"],title: json["title"],completed: json["completed"],);
Map<String,dynamic> toJson() => {
"userId": userId,"id": id,"title": title,"completed": completed,};
}
class NetworkHelper {
//Sets up search filter variables
int year;
String make;
String model;
String condition;
String combustible;
String style;
String color;
double minPrice;
double maxPrice;
Future<List<Payload>> fetchAlbum() async {
http.Response response =
await http.get('https://jsonplaceholder.typicode.com/todos');
if (response.statusCode == 200) {
//String data = response.body;
return payloadFromJson(response.body);
} else {
throw Exception('Failed to load album');
}
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',theme: ThemeData(
primarySwatch: Colors.blue,visualDensity: VisualDensity.adaptivePlatformDensity,home: WelcomeScreen(),);
}
}
class WelcomeScreen extends StatefulWidget {
static String id = '/welcome_screen';
@override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
int _index = 0;
Future<List<Payload>> _future;
NetworkHelper http = NetworkHelper();
Future<List<Payload>> testMethod() async {
var data = await http.fetchAlbum();
return data;
}
@override
void initState() {
super.initState();
_future = testMethod();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,appBar: AppBar(
elevation: 0.0,title: Text("title"),body: Column(
children: [
Expanded(
flex: 1,child: FutureBuilder(
future: _future,AsyncSnapshot<List<Payload>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',style: TextStyle(color: Colors.red),);
} else {
return ListView.builder(
shrinkWrap: true,index) {
return Card(
elevation: 6.0,child: Padding(
padding: const EdgeInsets.only(
top: 6.0,child: ListTile(
title: Text(
snapshot.data[index].id.toString()),subtitle: Text(
snapshot.data[index].title,);
});
}
}
}),Expanded(
flex: 1,child: Padding(
padding: const EdgeInsets.all(12.0),child: ListView(
shrinkWrap: true,children: [
ListBody(
children: [
Card(
child: Row(
children: [
Expanded(
child: Column(
children: [
Text('2017 Nissan',style: TextStyle(
fontWeight: FontWeight.w300,)),Text('Versa 1.6 SL',style: TextStyle(
fontWeight: FontWeight.w100,Padding(
padding: const EdgeInsets.all(8.0),child: Text('\$7,998.00',style: TextStyle(
fontWeight: FontWeight.w200,],Expanded(
child: Image.network(
'https://via.placeholder.com/450/0000FF',scale: 4,Card(
child: Row(
children: [
Expanded(
child: Column(
children: [
Text('2017 Nissan',)
],bottomNavigationBar: ConvexAppBar(
backgroundColor: Color(0xffe74c3c),items: [
TabItem(icon: Icons.home,title: 'Home'),TabItem(icon: Icons.map,title: 'Discover'),TabItem(icon: Icons.search,title: 'Busca'),TabItem(icon: Icons.person,title: 'Mi Cuenta'),TabItem(icon: Icons.build,title: 'Settings'),initialActiveIndex: 2,//optional,default as 0
onTap: (int i) => print('click index=$i'),));
}
}