如何从函数中调用数据,而不需要一次又一次地访问api



我是新的扑动我需要知道我是按照文件从API获取数据。问题是我需要知道如果我需要一次获取数据并调用所有页面该怎么办?

这是文档中的代码:

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.https('jsonplaceholder.typicode.com', 'albums/1'));
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 int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
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: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}

我真正需要知道的是,在init状态下,我正在调用一个函数,比如futureAlbum = fetchAlbum();,但我不想在第二页上一次又一次地回忆它。

假设这是第二页。我如何从fetchAlbum()函数调用数据,而无需再次调用api ?我希望我的问题是可以理解的:D,主要目的是我不想一次又一次地击中我的api。我需要在没有api hit的第二页上显示数据。

class SecondPage extends StatefulWidget {

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

class _SecondPageState extends State<SecondPage> {

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

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: ,
)
);
}
}

将结果作为第二页的属性传递(必须添加到safety and State类中):

class _SecondPageState extends State<SecondPage> {
// Attribut album
Album a;
// Add the constructor, this syntax is wrong but you get the idea
_SecondPageState({this.album})
...
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
// Use the object you passed in paramters
title: Text('${album.title}'),
),
body: ,
)
);
}
...
}

编辑:将已获取的专辑列表保存在map(id, album)

HashMap <int,Album> albumMap = new HashMap<int, Album>();
Future<Album> fetchAlbum(int id) async {
// Get the album from the map, not sure about the syntax to get a value from a hashmap using its key
Album album = albumMap[id];
// If the album is in the map, we return it, else we fetch it from the API and save it
if(album != null) {
return album;
} else {
final response =
await http.get(Uri.https('jsonplaceholder.typicode.com', 'albums/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
Album albumFromApi = Album.fromJson(jsonDecode(response.body));
// Save the album in the map, same not sure about the syntax.
albumMap[id] = albumFromApi;
return albumFromApi 
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
}

当你开始遇到从应用程序的不同部分访问数据以及控制何时调用API的问题时,真的是时候投资学习一个状态管理解决方案来使这类事情变得更容易了。也没有必要让你的fetchAlbum是一个顶级的全局函数。

实际上,任何状态管理都可以工作。但是这里有一种使用GetX状态管理的方法。

这将给你将有完全控制如何和何时调用你的API,它不会依赖于initState的有状态小部件。你的fetchAlbum函数可以存在于它自己的类中。

class AlbumController extends GetxController {
Album newAlbum;
Future<Album> fetchAlbum() async {
debugPrint('fetchAlbum called');
final response =
await http.get(Uri.https('jsonplaceholder.typicode.com', 'albums/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
newAlbum = Album.fromJson(jsonDecode(response.body));
debugPrint('id: ${newAlbum.id} title: ${newAlbum.title}');
return newAlbum;
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
}

你所有的小部件现在都可以是无状态的。下面是如何设置MaterialApp和几个页面的示例。我添加了几个在页面之间来回切换的按钮和一个打印语句,这样您就可以看到何时调用API。

void main() {
Get.put(AlbumController()); // initializing the Getx Controller
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Page1(),
routes: {
Page1.id: (context) => Page1(),
Page2.id: (context) => Page2(),
},
);
}
}
class Page1 extends StatelessWidget {
static const id =
'page_1'; // this is so you don't have to use raw strings for routing
@override
Widget build(BuildContext context) {
final controller =
Get.find<AlbumController>(); // finding the initialized controller
return Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FutureBuilder<Album>(
future: controller
.fetchAlbum(), // this now lives it its own AlbumController class
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
FlatButton(
child: Text('Go to page 2'),
color: Colors.blue,
onPressed: () {
Navigator.pushNamed(context, Page2.id);
},
)
],
),
),
);
}
}
class Page2 extends StatelessWidget {
static const id = 'page2';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GetBuilder<AlbumController>( // this will update the data displayed when fetchAlbum() is called
builder: (controller) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Id: ${controller.newAlbum.id}'),
Text('UserId: ${controller.newAlbum.userId}'),
Text('Title: ${controller.newAlbum.title}'),
FlatButton(
child: Text('Go to page 1'),
color: Colors.blue,
onPressed: () {
Navigator.pushNamed(context, Page1.id);
},
)
],
);
},
),
),
);
}
}

现在没有什么是全局的,数据在Page2上显示,而不需要再次调用API。由于FutureBuilder,当您返回Page1时,它将再次调用API,但如果您不想要这种行为,则不必使用FutureBuilder。还有其他方法可以更好地控制何时调用API。

如果我理解正确,你想要能够调用fetchAlbum,但如果它已经调用了API,它将返回以前获取的数据。

我想到的最简单的方法就是保留从请求中获得的数据的实例。

我会这样做:

Album _savedAlbum;
Future<Album> fetchAlbum() async {
// Check if you don't have already fetched your album
if (_savedAlbum != null) return _savedAlbum;
final response =
await http.get(Uri.https('jsonplaceholder.typicode.com', 'albums/1'));
if (response.statusCode == 200) {
_savedAlbum = Album.fromJson(jsonDecode(response.body));
return _savedAlbum;
} else {
throw Exception('Failed to load album');
}
}

通过添加这个私有变量_savedAlbum,你可以确保如果你已经从API中获取数据,你将在你进行任何其他API调用之前返回它。

相关内容

  • 没有找到相关文章

最新更新