Flutter-Создать виджет обратного отсчета

Я пытаюсь создать виджет обратного отсчета. В настоящее время я получил структуру для работы. Я борюсь только с самим обратным отсчетом. Я попробовал этот подход, используя плагин обратного отсчета:

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)),
        ),
      ),
    );
  }
}

однако значение изменяется очень странно и совсем не гладко. Он начинает дергаться. Любой другой подход или исправления?

4 ответов


похоже, вы пытаетесь показать анимированный текстовый виджет, который меняется с течением времени. Я бы использовал AnimatedWidget С StepTween чтобы убедиться, что обратный отсчет показывает только целые значения.

countdown

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),
          ),
        ),
      ),
    );
  }
}

на countdown() метод должен вызываться из initState() метод


на основе @raju-bitter answer, альтернатива использовать async / await в потоке обратного отсчета

  void countdown() async {
    cd = new CountDown(new Duration(seconds:4));
    await for (var v in cd.stream) {
      setState(() => val = v.inSeconds);
    }
  }

пример обратного отсчета с использованием потока, а не с помощью его все без гражданства.

это заимствовать идею из примера 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;
  }
}

отличие от stateful заключается в том, что перезагрузка приложения перезапустит подсчет. При использовании stateful в некоторых случаях он может не перезапускаться при перезагрузке.