从 API 获取数据时如何显示进度指示器?
Posted
技术标签:
【中文标题】从 API 获取数据时如何显示进度指示器?【英文标题】:How to show progress indicator when fetching data from API? 【发布时间】:2020-12-13 02:08:14 【问题描述】:问题很简单,但我找不到合适的解决方案。我有一个搜索屏幕,这就是我想要做的。
第一次启动屏幕时,我想在Text
小部件中显示一条消息“您的结果将出现在此处”。
如果用户在搜索框中输入内容并按搜索,我想显示CircularProgressIndicator
。
然后,当收到 API 响应时,我想显示 ListView
或 Text
说“什么也没找到”。
如果用户再次搜索,我想再次显示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
。
谢谢。
【问题讨论】:
【参考方案1】:你可以试试:
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'));
,
)
【讨论】:
感谢您的回答,但它并没有解决我的问题。从未收到ConnectionState.none
。此外,这种方法不会在下次搜索时显示进度条。
谢谢,但它不起作用。 snapshot.connectionState
总是有一个值,它从它的 case 返回。【参考方案2】:
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() 小部件,其值为“结果将出现在此处”, 然后 onClick 按钮,将初始 Text() 小部件替换为 StreamBuilder
【讨论】:
这不起作用。因为它开始在开始时显示CircularProgressIndicator
。我想在开始时显示Text
小部件,说“您的结果将出现在此处”。我只想在用户开始搜索时显示CircularProgressIndicator
。【参考方案3】:
这是我为了满足我的要求而必须做的事情。如果有更好的方法来实现这一点,请发布答案,我会将其标记为正确。
破解
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);
【讨论】:
很多逻辑不在 UI 中,但理想情况下应该在业务逻辑中。看看flutter_bloc
包,它有一个非常好的标准处理方式。
我已经检查过了。在这种情况下,它似乎对我没有帮助。如果你能想出一个代码示例,你可以在这里发布,我会标记为正确的。以上是关于从 API 获取数据时如何显示进度指示器?的主要内容,如果未能解决你的问题,请参考以下文章
获取数据时如何在 React Redux 应用程序中显示加载指示器? [关闭]