从 API 获取数据时如何显示进度指示器?
How to show progress indicator when fetching data from API?
问题很简单,但我找不到合适的解决方案。我有一个搜索屏幕,这是我想要做的。
- 第一次启动屏幕时,我想在
Text
小部件 'Your results will appear here' 中显示一条消息。
- 如果用户在搜索框中输入内容并按搜索,我想显示
CircularProgressIndicator
。
- 然后,当收到 API 响应时,我想显示
ListView
或 Text
说 'Found nothing'。
- 如果用户再次搜索某些内容,我想再次显示
CircularProgressIndicator
。
如何使用 Stream
实现此行为?到目前为止,以下是我的代码:
小部件
StreamBuilder<List<Model>>(
stream: bloc.searchStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Error occurred'
),
);
}
if (!snapshot.hasData) {
return Center(
child: Text(
'Your results will appear here'
),
);
}
if (snapshot.hasData && snapshot.data.length < 1) {
return Center(
child: Text(
'Found nothing'
),
);
}
// Where should I put my CircularProgressIndicator?
return ListView.separated(
itemCount: snapshot.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data[index];
return ItemWidget(item);
},
);
},
)
BLoC
final _searchStreamController = StreamController<List<Model>>();
Stream<List<Model>> get searchStream => _searchStreamController.stream;
void search(String searchTerm) async {
if (searchTerm.isEmpty) {
_searchStreamController.add(null);
return;
}
final client = HttpClient();
client.method = HttpMethod.POST;
client.endPoint = 'search';
client.addData('query', searchTerm);
try {
final responseStr = await client.execute();
final response = SearchResponse.fromJson(responseStr);
_searchStreamController.add(response.data);
} catch (e) {
_searchStreamController.addError(e);
}
}
我想在每次调用 search(String searchedTerm)
函数时显示 CircularProgressIndicator
。
谢谢。
StreamBuilder<List<Model>>(
stream: bloc.searchStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.length ==0){
return Center(
child: Text(
'Found nothing'
),
);
}
return ListView.separated(
itemCount: snapshot.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data[index];
return ItemWidget(item);
},
);
}else if (snapshot.hasError) {
return Center(
child: Text(
'Error occurred'
),
);
}else {
return Center(
child:CircularProgressIndicator()
);
}
},
)
给出一个值为“结果将出现在这里”的初始 Text() 小部件,
然后单击 Button,用 StreamBuilder
替换初始的 Text() 小部件
你可以试试:
StreamBuilder<List<Model>>(
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Center(child: Text('Your results will appear here'));
case ConnectionState.active:
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.done:
if (snapshot.hasError)
return Center(child: Text('Error occurred'));
if (snapshot.data.isEmpty) {
return Container(child: Center(child: Text('Found nothing')));
} else {
return ListView.separated(
itemCount: snapshot.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data[index];
return ItemWidget(item);
},
);
}
}
return Center(child: Text('Your results will appear here'));
},
)
这是我为达到我的要求而必须做的 hack。如果有更好的实现方法,请post回答,我会标记为正确。
破解
class JugarModel<T> {
bool isProcessing;
T data;
JugarModel({this.isProcessing, this.data});
}
然后用这个isProcessing
属性判断是显示CircularProgressIndicator
还是ListView
。其余代码变为:
小部件
StreamBuilder<JugarModel>(
stream: bloc.searchStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Error occurred'
),
);
}
if (!snapshot.hasData) {
return Center(
child: Text(
'Your results will appear here'
),
);
}
if (snapshot.hasData && snapshot.data.data.isEmpty) {
if (snapshot.data.isProcessing) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Center(
child: Text(
'Found nothing'
),
);
}
}
return ListView.separated(
itemCount: snapshot.data.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data.data[index];
return ItemWidget(item);
},
);
},
)
BLoC
final _searchStreamController = StreamController<JugarModel<List<Data>>>();
Stream<JugarModel<List<Data>>> get searchStream => _searchStreamController.stream;
void search(String searchTerm) async {
if (searchTerm.isEmpty) {
_searchStreamController.add(null);
return;
}
final client = HttpClient();
client.method = HttpMethod.POST;
client.endPoint = 'search';
client.addData('query', searchTerm);
// This MAGIC line will call StreamBuilder callback with isProcessing set to true.
_searchStreamController.add(JugarModel<List<Data>>(isProcessing: true, data: List()));
try {
final responseStr = await client.execute();
final response = SearchResponse.fromJson(responseStr);
// And after we've received response from API and parsed it, we're calling StreamBuilder
// callback again with isProcessing set to false.
_searchStreamController.add(JugarModel<List<Data>>(isProcessing: false, data: response.data));
} catch (e) {
_searchStreamController.addError(e);
}
}
问题很简单,但我找不到合适的解决方案。我有一个搜索屏幕,这是我想要做的。
- 第一次启动屏幕时,我想在
Text
小部件 'Your results will appear here' 中显示一条消息。 - 如果用户在搜索框中输入内容并按搜索,我想显示
CircularProgressIndicator
。 - 然后,当收到 API 响应时,我想显示
ListView
或Text
说 'Found nothing'。 - 如果用户再次搜索某些内容,我想再次显示
CircularProgressIndicator
。
如何使用 Stream
实现此行为?到目前为止,以下是我的代码:
小部件
StreamBuilder<List<Model>>(
stream: bloc.searchStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Error occurred'
),
);
}
if (!snapshot.hasData) {
return Center(
child: Text(
'Your results will appear here'
),
);
}
if (snapshot.hasData && snapshot.data.length < 1) {
return Center(
child: Text(
'Found nothing'
),
);
}
// Where should I put my CircularProgressIndicator?
return ListView.separated(
itemCount: snapshot.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data[index];
return ItemWidget(item);
},
);
},
)
BLoC
final _searchStreamController = StreamController<List<Model>>();
Stream<List<Model>> get searchStream => _searchStreamController.stream;
void search(String searchTerm) async {
if (searchTerm.isEmpty) {
_searchStreamController.add(null);
return;
}
final client = HttpClient();
client.method = HttpMethod.POST;
client.endPoint = 'search';
client.addData('query', searchTerm);
try {
final responseStr = await client.execute();
final response = SearchResponse.fromJson(responseStr);
_searchStreamController.add(response.data);
} catch (e) {
_searchStreamController.addError(e);
}
}
我想在每次调用 search(String searchedTerm)
函数时显示 CircularProgressIndicator
。
谢谢。
StreamBuilder<List<Model>>(
stream: bloc.searchStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.length ==0){
return Center(
child: Text(
'Found nothing'
),
);
}
return ListView.separated(
itemCount: snapshot.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data[index];
return ItemWidget(item);
},
);
}else if (snapshot.hasError) {
return Center(
child: Text(
'Error occurred'
),
);
}else {
return Center(
child:CircularProgressIndicator()
);
}
},
)
给出一个值为“结果将出现在这里”的初始 Text() 小部件, 然后单击 Button,用 StreamBuilder
替换初始的 Text() 小部件你可以试试:
StreamBuilder<List<Model>>(
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Center(child: Text('Your results will appear here'));
case ConnectionState.active:
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.done:
if (snapshot.hasError)
return Center(child: Text('Error occurred'));
if (snapshot.data.isEmpty) {
return Container(child: Center(child: Text('Found nothing')));
} else {
return ListView.separated(
itemCount: snapshot.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data[index];
return ItemWidget(item);
},
);
}
}
return Center(child: Text('Your results will appear here'));
},
)
这是我为达到我的要求而必须做的 hack。如果有更好的实现方法,请post回答,我会标记为正确。
破解
class JugarModel<T> {
bool isProcessing;
T data;
JugarModel({this.isProcessing, this.data});
}
然后用这个isProcessing
属性判断是显示CircularProgressIndicator
还是ListView
。其余代码变为:
小部件
StreamBuilder<JugarModel>(
stream: bloc.searchStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Error occurred'
),
);
}
if (!snapshot.hasData) {
return Center(
child: Text(
'Your results will appear here'
),
);
}
if (snapshot.hasData && snapshot.data.data.isEmpty) {
if (snapshot.data.isProcessing) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Center(
child: Text(
'Found nothing'
),
);
}
}
return ListView.separated(
itemCount: snapshot.data.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data.data[index];
return ItemWidget(item);
},
);
},
)
BLoC
final _searchStreamController = StreamController<JugarModel<List<Data>>>();
Stream<JugarModel<List<Data>>> get searchStream => _searchStreamController.stream;
void search(String searchTerm) async {
if (searchTerm.isEmpty) {
_searchStreamController.add(null);
return;
}
final client = HttpClient();
client.method = HttpMethod.POST;
client.endPoint = 'search';
client.addData('query', searchTerm);
// This MAGIC line will call StreamBuilder callback with isProcessing set to true.
_searchStreamController.add(JugarModel<List<Data>>(isProcessing: true, data: List()));
try {
final responseStr = await client.execute();
final response = SearchResponse.fromJson(responseStr);
// And after we've received response from API and parsed it, we're calling StreamBuilder
// callback again with isProcessing set to false.
_searchStreamController.add(JugarModel<List<Data>>(isProcessing: false, data: response.data));
} catch (e) {
_searchStreamController.addError(e);
}
}