我有这个Future函数,需要花费很多时间来加载,并且基本上像视频渲染一样工作(它执行100次某些步骤)。
有什么办法可以在小部件中显示进度?
我尝试在MyApp()中设置一个全局变量,例如与框架相对应的int并通过setState(
)我尝试重建小部件,但是它不起作用,应用程序冻结并且小部件没有得到更新。
这是功能:
int _progress = 0;
Future _cycleGame() async {
await game.cycle((int value) {
print(value);
setState(() {
_progress = value;
});
}).whenComplete(() {
setState(() {});
});
}
game.cycle()
是一个Future函数,可以循环50次。
game.cycle()函数供参考:
Future cycle(Function cBack) async {
for (int i in Range(50)) {
if (!shouldFinish) {
cBack(i);
///Does things.
到目前为止,我仅在屏幕上使用_progress
值的文本。
该vlaue被正确打印(每两次),但是显示_progress
的文本仅在函数末尾更新。我在做什么错了?
我尝试使用谷歌搜索方法,但没有发现任何问题。这可能吗?
编辑1 。 使用RémiRousselet提供的答案,我仍然无法使代码正常工作。仅在流结束时才更新小部件。
这是代码。
import 'dart:io';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Foo(),
);
}
}
class Foo extends StatefulWidget {
@override
_FooState createState() => _FooState();
}
class _FooState extends State<Foo> {
Stream<int> _cycleStream;
@override
void initState() {
super.initState();
_cycleStream = cycle();
}
Stream<int> cycle() async* {
for (int i = 0; i <= 50; i++) {
sleep(Duration(milliseconds: 100));
yield i;
this.setState(() {
});
// does things
}
}
@override
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: _cycleStream,
initialData: 0,
builder: (context, snapshot) {
return Center(
child: Text(snapshot.data.toString())
);
},
);
}
}
Flutter Doctor
[√] Flutter (Channel beta, v1.5.4-hotfix.2, on Microsoft Windows [Versione 10.0.17134.706], locale it-IT)
• Flutter version 1.5.4-hotfix.2 at
• Framework revision 7a4c33425d (8 days ago), 2019-04-29 11:05:24 -0700
• Engine revision 52c7a1e849
• Dart version 2.3.0 (build 2.3.0-dev.0.5 a1668566e5)
答案 0 :(得分:4)
您可能希望使用Future
而不是Stream
。
Future
仅在它们完全完成时才发出值。因此,我们不能使用它们来表示随时间变化的值(此处是进度指示器)。
另一方面,Stream
可以发射无限次。
这是您的cycle
函数的实现,每次更新时都会发出当前进度:
Stream<double> cycle() async* {
for (int i in Range(50)) {
yield i / 50;
// TODO: does things
}
}
然后您可以使用double
将CircularProgressIndicator
发送到StreamBuilder
中。
这是一个完整的例子:
class Foo extends StatefulWidget {
@override
_FooState createState() => _FooState();
}
class _FooState extends State<Foo> {
Stream<double> _cycleStream;
@override
void initState() {
super.initState();
_cycleStream = cycle();
}
Stream<double> cycle() async* {
for (int i = 0; i <= 50; i++) {
yield i / 50;
// does things
}
}
@override
Widget build(BuildContext context) {
return StreamBuilder<double>(
stream: _cycleStream,
initialData: 0,
builder: (context, snapshot) {
return CircularProgressIndicator(
value: snapshot.data,
);
},
);
}
}
答案 1 :(得分:0)
您尝试过Future Builder吗?
Future < Widget > buildAfter() async {
return Future.delayed(Duration(seconds: 3), () => Text('Hello World!'));
}
return Scaffold(
body: FutureBuilder(
future: buildAfter(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => ListTile(
title: snapshot.data,
subtitle: Text('Counter: $index'),
),
);
}
return Center(
child: CircularProgressIndicator(),
);
},
));
在此代码中,当您运行该代码时,它将显示CircularProgressIndiacator 3秒钟,因为我们已告知编译器在Future函数中保留3秒钟。
当3秒结束时,它将返回数据,FutureBuilder会收到一条通知,通知它包含数据,并使用可用的新数据重建小部件。
注意:不一定需要Future函数必须返回必要的数据,它只需要返回错误以外的内容,以便侦听的Builder函数可以了解何时应该重建自身。
希望您还可以在任何小部件中使用Future Builder。
如果要多次发射数据,则可以将FutureBuilder与bool一起使用,并检查其是否正在加载,然后显示此内容以其他方式显示。代替Stream / Streambuilder并使事情变得更简单。
bool isLoading = false;
Future < Widget > buildAfter() async {
return Future.delayed(Duration(seconds: 3), () => Text('Hello World!'));
}
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: greenColor,
title: const Text('Inbox'),
actions: < Widget > [
new IconButton(
icon: new Icon(Icons.edit),
onPressed: () {
setState(() {
isLoading = !isLoading;
});
}),
],
),
body: isLoading ?
Center(
child: CircularProgressIndicator(
backgroundColor: Colors.redAccent,
),
) :
FutureBuilder(
future: buildAfter(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => ListTile(
title: Text('Code'),
subtitle: Text('Counter: $name'),
),
);
}
return Center(
child: CircularProgressIndicator(),
);
},
));
此代码中发生的事情是检查bool isLoading
如果isLoading == true =>显示循环进度指示器
if isLoading == false =>显示将来的构建器
因此,当您必须多次调用它时,可以将bool isLoading的State设置为true。然后,计算结束后,可以将bool isLoading的State设置为false。
但这取决于用例,当您随时有传入事件时,Streams总是更好的,但是如果您知道自己在做什么,那么Streams会让他们很难理解
答案 2 :(得分:0)
据你说你无法让代码工作。
我已经尝试过 Remo 的代码并且运行良好。
我认为问题是你的 cycle() 需要等待来模拟一些延迟。
Stream<double> cycle() async* {
for (int i = 0; i <= 50; i++) {
yield i / 50;
// does things
await Future.delayed(Duration(seconds: 1));
}
}