J'essaie de construire un widget compte à rebours. Actuellement, j'ai la structure pour travailler. Je ne lutte qu'avec le compte à rebours lui-même. J'ai essayé cette approche en utilisant le plugin countdown:
class _Countdown extends State<Countdown> {
int val = 3;
void countdown(){
CountDown cd = new CountDown(new Duration(seconds: 4));
cd.stream.listen((Duration d) {
setState((){
val = d.inSeconds;
});
});
}
@override
build(BuildContext context){
countdown();
return new Scaffold(
body: new Container(
child: new Center(
child: new Text(val.toString(), style: new TextStyle(fontSize: 150.0)),
),
),
);
}
}
Cependant, la valeur change très étrangement et pas du tout. Ça commence à trembler. Toute autre approche ou solution?
On dirait que vous essayez d’afficher un widget texte animé qui change avec le temps. J'utiliserais un AnimatedWidget
avec un StepTween
pour m'assurer que le compte à rebours ne montre que des valeurs entières.
import 'package:flutter/material.Dart';
void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}
class Countdown extends AnimatedWidget {
Countdown({ Key key, this.animation }) : super(key: key, listenable: animation);
Animation<int> animation;
@override
build(BuildContext context){
return new Text(
animation.value.toString(),
style: new TextStyle(fontSize: 150.0),
);
}
}
class MyApp extends StatefulWidget {
State createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
AnimationController _controller;
static const int kStartValue = 4;
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
duration: new Duration(seconds: kStartValue),
);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.play_arrow),
onPressed: () => _controller.forward(from: 0.0),
),
body: new Container(
child: new Center(
child: new Countdown(
animation: new StepTween(
begin: kStartValue,
end: 0,
).animate(_controller),
),
),
),
);
}
}
basé sur la réponse @ raju-bitter, alternative à l'utilisation asynchrone/wait sur le flux du compte à rebours
void countdown() async {
cd = new CountDown(new Duration(seconds:4));
await for (var v in cd.stream) {
setState(() => val = v.inSeconds);
}
}
La méthode countdown()
doit être appelée à partir de la méthode initState()
de l'objet State
.
class _CountdownState extends State<CountdownWidget> {
int val = 3;
CountDown cd;
@override
void initState() {
super.initState();
countdown();
}
...
Description de initState()
à partir des documents Flutter :
Le cadre appelle initState. Les sous-classes d’État doivent prévaloir sur initState pour effectuer une initialisation unique qui dépend du BuildContext ou le widget, disponibles en tant que contexte et propriétés du widget, respectivement, lorsque la méthode initState est appelée.
Voici un exemple de travail complet:
import 'Dart:async';
import 'package:flutter/material.Dart';
import 'package:countdown/countdown.Dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Countdown Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new CountdownWidget();
}
}
class _CountdownState extends State<CountdownWidget> {
int val = 3;
CountDown cd;
@override
void initState() {
super.initState();
countdown();
}
void countdown(){
print("countdown() called");
cd = new CountDown(new Duration(seconds: 4));
StreamSubscription sub = cd.stream.listen(null);
sub.onDone(() {
print("Done");
});
sub.onData((Duration d) {
if (val == d.inSeconds) return;
print("onData: d.inSeconds=${d.inSeconds}");
setState((){
val = d.inSeconds;
});
});
}
@override
build(BuildContext context){
return new Scaffold(
body: new Container(
child: new Center(
child: new Text(val.toString(), style: new TextStyle(fontSize: 150.0)),
),
),
);
}
}
class CountdownWidget extends StatefulWidget {
@override
_CountdownState createState() => new _CountdownState();
}
Exemple de compte à rebours utilisant stream, n'utilisant pas setState(...)
, son tout est donc sans état.
cette idée d'emprunt de l'exemple flutter_stream_friends
import 'Dart:async';
import 'package:flutter/material.Dart';
import 'package:countdown/countdown.Dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
static String appTitle = "Count down";
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: appTitle,
theme: new ThemeData(
primarySwatch: Colors.purple,
),
home: new StreamBuilder(
stream: new CounterScreenStream(5),
builder: (context, snapshot) => buildHome(
context,
snapshot.hasData
// If our stream has delivered data, build our Widget properly
? snapshot.data
// If not, we pass through a dummy model to kick things off
: new Duration(seconds: 5),
appTitle)),
);
}
// The latest value of the CounterScreenModel from the CounterScreenStream is
// passed into the this version of the build function!
Widget buildHome(BuildContext context, Duration duration, String title) {
return new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
child: new Text(
'Count down ${ duration.inSeconds }',
),
),
);
}
}
class CounterScreenStream extends Stream<Duration> {
final Stream<Duration> _stream;
CounterScreenStream(int initialValue)
: this._stream = createStream(initialValue);
@override
StreamSubscription<Duration> listen(
void onData(Duration event),
{Function onError,
void onDone(),
bool cancelOnError}) =>
_stream.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
// The method we use to create the stream that will continually deliver data
// to the `buildHome` method.
static Stream<Duration> createStream(int initialValue) {
var cd = new CountDown(new Duration(seconds: initialValue));
return cd.stream;
}
}
La différence avec stateful est que recharger l'application redémarre le comptage. Lorsque vous utilisez stateful, dans certains cas, il peut ne pas redémarrer lors du rechargement.