我编写了一段代码,它将从FoodData API- https://fdc.nal.usda.gov/api-guide.html获取数据并将其显示在列表中。我还创建了一个TextField,以便用户可以搜索特定的菜肴。一切都很顺利,但我有个小问题-
该列表每隔几秒刷新一次,以便过滤器列表显示几秒钟,然后该列表转到其初始外观。
我只想让列表加载一次,并且仍然可以让过滤器功能正常工作。
这是我的代码-
import 'dart:async';
import 'dart:convert';
import 'package:fit_app/fitness_app_theme.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<List<FoodGen>> fetchPhotos(http.Client client) async {
final response =
await client.get('https://api.nal.usda.gov/fdc/v1/foods/list?dataType=Foundation,SR%20Legacy&pageSize=200&api_key=APIKEY');
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parsePhotos, response.body);
}
// A function that converts a response body into a List<Photo>.
List<FoodGen> parsePhotos(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<FoodGen>((json) => FoodGen.fromJson(json)).toList();
}
class FoodGen {
int fdcId;
String description;
String dataType;
String publicationDate;
String ndbNumber;
List<FoodNutrients> foodNutrients;
FoodGen(
{this.fdcId,
this.description,
this.dataType,
this.publicationDate,
this.ndbNumber,
this.foodNutrients});
FoodGen.fromJson(Map<String, dynamic> json) {
fdcId = json['fdcId'];
description = json['description'];
dataType = json['dataType'];
publicationDate = json['publicationDate'];
ndbNumber = json['ndbNumber'];
if (json['foodNutrients'] != null) {
foodNutrients = new List<FoodNutrients>();
json['foodNutrients'].forEach((v) {
foodNutrients.add(new FoodNutrients.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['fdcId'] = this.fdcId;
data['description'] = this.description;
data['dataType'] = this.dataType;
data['publicationDate'] = this.publicationDate;
data['ndbNumber'] = this.ndbNumber;
if (this.foodNutrients != null) {
data['foodNutrients'] =
this.foodNutrients.map((v) => v.toJson()).toList();
}
return data;
}
}
class FoodNutrients {
String number;
String name;
dynamic amount;
String unitName;
String derivationCode;
String derivationDescription;
FoodNutrients(
{this.number,
this.name,
this.amount,
this.unitName,
this.derivationCode,
this.derivationDescription});
FoodNutrients.fromJson(Map<String, dynamic> json) {
number = json['number'];
name = json['name'];
amount = json['amount'];
unitName = json['unitName'];
derivationCode = json['derivationCode'];
derivationDescription = json['derivationDescription'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['number'] = this.number;
data['name'] = this.name;
data['amount'] = this.amount;
data['unitName'] = this.unitName;
data['derivationCode'] = this.derivationCode;
data['derivationDescription'] = this.derivationDescription;
return data;
}
}
class FoodPage extends StatefulWidget {
FoodPage({Key key}) : super(key: key);
@override
_FoodPageState createState() => _FoodPageState();
}
class _FoodPageState extends State<FoodPage> {
TextEditingController editingController = TextEditingController();
List<FoodGen> dishes;
List<FoodGen> dishesFilter;
List<FoodGen> duplicateItems;
List<FoodGen> dummySearchList = List<FoodGen>();
@override
void initState() {
super.initState();
}
void filterSearchResults(String query) {
if(dishes == null)return;
dummySearchList.addAll(dishes);
if(query.isNotEmpty) {
List<FoodGen> dummyListData = List<FoodGen>();
dummySearchList.forEach((item) {
String queryLowercase = query.toLowerCase();
if(item.description.toLowerCase().startsWith('$queryLowercase')){
dummyListData.add(item);
}
}
);
setState(() {
dishes.clear();
dishes.addAll(dummyListData);
});
return;
} else {
setState(() {
dishes.clear();
dishes.addAll(duplicateItems);
});
}
}
@override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: FitnessAppTheme.darkBackground,
body: SafeArea(
top: true,
child:Container(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
style: TextStyle(color: Colors.white),
onChanged: (value) {
filterSearchResults(value);
},
controller: editingController,
decoration: InputDecoration(
hintStyle: TextStyle(color:FitnessAppTheme.white),
labelStyle: TextStyle(color:FitnessAppTheme.white),
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0)))),
),
),
Expanded(
child: FutureBuilder<List<FoodGen>>(
future: fetchPhotos(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasData) {
dishes = snapshot.data;
return snapshot.hasData
? ListView.builder(
itemCount: dishes.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(title: Text(dishes[index].description))
);
},
)
: Center(child: CircularProgressIndicator());
}
},
),
),
],
),
),
),
);
}
}
我非常感谢您在这里的帮助(:
发布于 2021-02-12 22:23:14
只需将fetchPhotos
放在类变量中并在FutureBuilder
中使用它
var photosRequest;
@override
void initState() {
photosRequest = fetchPhotos(client);
}
在FutureBuilder
中
builder: photosRequest.then((photos) => filterSearchResults(photos)),
如果从变量调用Future
,并且变量不能更改,FutureBuilder
就不能再次调用它并使用先前获取的数据。
发布于 2021-02-12 22:49:20
您可以使用提供程序包和Listen: false
,这将调用一次,
下面是这个包的示例,希望它能对你有所帮助。
将此代码添加到软件包的pubspec.yaml文件中:
dependencies:
provider: ^4.3.3
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// This is a reimplementation of the default Flutter application using provider + [ChangeNotifier].
void main() {
runApp(
/// Providers are above [MyApp] instead of inside it, so that tests
/// can use [MyApp] while mocking the providers
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
],
child: const MyApp(),
),
);
}
/// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool
// ignore: prefer_mixin
class Counter with ChangeNotifier, DiagnosticableTreeMixin {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
/// Makes `Counter` readable inside the devtools by listing all of its properties
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('count', count));
}
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('You have pushed the button this many times:'),
/// Extracted as a separate widget for performance optimization.
/// As a separate widget, it will rebuild independently from [MyHomePage].
///
/// This is totally optional (and rarely needed).
/// Similarly, we could also use [Consumer] or [Selector].
Count(),
],
),
),
floatingActionButton: FloatingActionButton(
key: const Key('increment_floatingActionButton'),
/// Calls `context.read` instead of `context.watch` so that it does not rebuild
/// when [Counter] changes.
onPressed: () => context.read<Counter>().increment(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Count extends StatelessWidget {
const Count({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
/// Calls `context.watch` to make [Count] rebuild when [Counter] changes.
'${context.watch<Counter>().count}',
key: const Key('counterState'),
style: Theme.of(context).textTheme.headline4);
}
}
https://stackoverflow.com/questions/66173166
复制相似问题