如何在不同持续时间的颤动中生成Timer?

问题描述

我尝试过

var e = [2,5,6,7];
e.forEach((element) {
  print(element);
  Timer.periodic(new Duration(seconds: element),(timer) {
    debugPrint(element.toString());
    sleep(Duration(seconds: element));
  });
});

但它似乎混乱了,计时器无法按预期工作。

输出为2,2,7,7

预期输出为2,7

我的目标是我需要使用不同的计时器生成类似GIF的图像。

我需要提取dart中的HTML代码,在HTML代码中有图像路径和每个图像的显示时间。

目前,我可以通过图像网络生成图像。但是计时器仍然有问题。我尝试过上面的代码,但是输出错误

这里是完整代码

import 'dart:async';
import 'dart:developer';
import 'dart:ffi';
import 'dart:io';
import 'dart:convert';

import 'package:audioplayers/audio_cache.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:Flutter/material.dart';
import 'package:http/http.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:Flutter/src/foundation/constants.dart';
import 'package:html/parser.dart' show parse;
import 'package:html/dom.dart' as Dom;
import 'package:shared_preferences/shared_preferences.dart';
// import 'package:firebase_admob/firebase_admob.dart';

import 'player_widget.dart';

typedef void OnError(Exception exception);

const kUrl1 =
    'http://n0c.radiojar.com/1t04vq7uc6quv?rj-ttl=5&rj-tok=AAABcd71RMcAnUEp0f_GRjz3pw';

const String testDevice = 'MobileId';

void main() {
  runApp(MaterialApp(debugShowCheckedModeBanner: false,home: ExampleApp()));
}

class ExampleApp extends StatefulWidget {
  @override
  _ExampleAppState createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  AudioCache audioCache = AudioCache();
  AudioPlayer advancedplayer = AudioPlayer();
  String localFilePath;
  String a;

  @override
  void initState() {
    super.initState();

    if (kIsWeb) {
      // Calls to Platform.isIOS fails on web
      return;
    }
    if (Platform.isIOS) {
      if (audioCache.fixedplayer != null) {
        audioCache.fixedplayer.startHeadlessService();
      }
      advancedplayer.startHeadlessService();
    }
  }

  Future saveCompanyAds() async {
    final response = await Client().get("http://www.putrajaya.fm/");
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final List<String> listlogo = [];
    final List<String> listBanner = [];
    final List<int> listNumberlogo = [];
    final List<int> listNumberBanner = [];
    final List<int> listTimelogo = [];
    final List<int> listTimeBanner = [];
    final List<Tag> tagsBanner = [];
    final List<Tag> tagslogo = [];

    RegExp expBanner = new RegExp(r'(gambar)\[[0-9]\].+');
    RegExp explogo = new RegExp(r'(images)\[[0-9]\].+');
    RegExp expNumArr = new RegExp(r'\[[0-9]+\]');
    RegExp expURL =
        new RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
    RegExp expTime = new RegExp(r'(\/\/time=)[0-9]+');
    prefs.remove("banner");
    prefs.remove("logo");

    if (response.statusCode == 200) {
      var document = parse(response.body);
      String text = document.getElementById("radio_jar").outerHtml;
      Iterable<RegExpMatch> matchesBanner = expBanner.allMatches(text);
      Iterable<RegExpMatch> matcheslogo = explogo.allMatches(text);

      matchesBanner.forEach((matchB) {
        listBanner.add(text.substring(matchB.start,matchB.end));
      });

      matcheslogo.forEach((matchL) {
        listlogo.add(text.substring(matchL.start,matchL.end));
      });

      listBanner.forEach((fB) {
        var a = expNumArr.stringMatch(fB);
        var b = a.replaceAll(RegExp('[^0-9]+'),'');
        var c = int.parse(b);
        listNumberBanner.add(c);

        var g = expURL.stringMatch(fB);

        var d = expTime.stringMatch(fB);
        var e = d.replaceAll(RegExp('[^0-9]+'),'');
        var f = int.parse(e);
        listTimeBanner.add(f);

        tagsBanner.add(Tag(c,g,f));
      });

      listlogo.forEach((fL) {
        var a = expNumArr.stringMatch(fL);
        var b = a.replaceAll(RegExp('[^0-9]+'),'');
        var c = int.parse(b);
        listNumberlogo.add(c);

        var g = expURL.stringMatch(fL);

        var d = expTime.stringMatch(fL);
        var e = d.replaceAll(RegExp('[^0-9]+'),'');
        var f = int.parse(e);
        listTimelogo.add(f);

        tagslogo.add(Tag(c,f));
      });
      String jsonTagsBanner = jsonEncode(tagsBanner);
      String jsonTagslogo = jsonEncode(tagslogo);

      prefs.setString("banner",jsonTagsBanner);
      prefs.setString("logo",jsonTagslogo);
    }
  }

  Future readCompanyAds() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    List listBannerjsonDecode = [];
    List listlogoJSONDecode = [];
    listBannerjsonDecode = jsonDecode(prefs.getString("banner"));
    listlogoJSONDecode = jsonDecode(prefs.getString("logo"));
    List<String> bannerList = List<String>();
    List<int> timerbannerList = List<int>();
    final List<String> listBannerNetwork = List<String>();
    final List<int> listBannerTimer = List<int>();

    listBannerjsonDecode.forEach((elementBanner) => {
          listBannerNetwork.add(elementBanner['url']),listBannerTimer.add(elementBanner['time'])
        });
    listBannerNetwork.forEach((urlBanner) {
      bannerList.add(urlBanner);
    });
    //Currently I insert the first element to avoid app crash
    a = bannerList[0];
    listBannerTimer.forEach((timeBanner) {
      timerbannerList.add(timeBanner);
    });
    //this is where I want to insert the GIF with different timer by using 
    //timerbannerList and I want to replace it with the variable "a" above.
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: Future.wait([saveCompanyAds(),readCompanyAds()]),builder: (BuildContext context,AsyncSnapshot<dynamic> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Center(
            child: new Column(
              mainAxisSize: MainAxisSize.min,children: [
                new CircularProgressIndicator(
                  backgroundColor: Colors.white,),],);
        } else {
          if (snapshot.hasError)
            return Center(child: Text('Error: ${snapshot.error}'));
          else
            return MultiProvider(
              providers: [
                StreamProvider<Duration>.value(
                    initialData: Duration(),value: advancedplayer.onAudioPositionChanged),child: Scaffold(
                  appBar: AppBar(
                    title: Text('Putrajaya FM'),body: Column(
                    children: <Widget>[
                      Image.asset('images/putrajayafm.jpg',fit: BoxFit.fill,height: 200,width: double.infinity),Container(
                          child: Row(
                              mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[PlayerWidget(url: kUrl1)])),Container(
                          margin: EdgeInsets.only(top: 20),child: Row(
                            mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[
                              //this is where I call the "a" variable,//where it should be repalced with list of 
                              //image with different timer
                              Image.network(a,height:
                                      (MediaQuery.of(context).size.width / 2),width:
                                      (MediaQuery.of(context).size.width / 2)),Image.asset('images/logo2.gif',width:
                                      (MediaQuery.of(context).size.width / 2))
                            ],)),Expanded(
                          child: Container(
                        child: Image.network(a,width: MediaQuery.of(context).size.width),))
                    ],);
        }
      },);
  }
}

class Tag {
  int number;
  String url;
  int time;

  Tag(this.number,this.url,this.time);

  Map<String,dynamic> toJson() => {'number': number,'url': url,'time': time};
}

解决方法

您所显示的代码的预期行为正是您所看到的。

听起来您真正想要的是一个10秒的重复间隔,然后在每个间隔的2、5、6和7秒处发生一次事件。 您可以通过多种不同方式来实现。计时器最少的计时器使用四个重复计时器,以及四个非重复计时器在不同的时间启动它们:

void startTimers(
    Duration repeat,List<Duration> offsets,void Function(Duration) callback) {
  for (var e in offsets) { 
    Timer(e,() {
      callback(e); // First call.
      Timer.peridic(repeat,(_) => callback(e)); // More calls after that.
    });
  }
}

另一种选择是有一个重复计时器,然后为每个偏移量启动非重复计时器:

Timer startTimers(
    Duration repeat,void Function(Duration) callback) {
  void go(_) {
    for (var e in offsets) Timer(e,() => callback(e));
  }
  go(null);
  return Timer.periodic(repeat,go);
}

如果您需要再次停止计时器,那么我会采用后一种方法,并接受即使我尝试取消,本轮比赛也将完成。 (否则,您将需要一些更复杂的代码来保留所有计时器并取消它们)。